33 KiB
33 KiB
PRD - Dutra Store
Changelog
v1.3.0+6 — 2026-10-18
- Kasir list + toggle grid/list: default list (foto 56px), toggle ke grid. Tap = tambah qty berkali-kali. Highlight hijau sesaat (0.9s). Tombol keranjang + badge jumlah item.
- Notifikasi in-app: banner SnackBar + getar — hanya untuk admin, hanya untuk transaksi dari orang lain (bukan transaksi admin sendiri). Deteksi via polling (interval 1 menit) + stream
newSaleNotifications. Load pertama dilewati agar tidak spam. - Badge Riwayat:
unseenSalesCountdi tab Riwayat (admin only), reset saat tab dibuka (markSalesSeen()). - Fix tombol "Muat lebih banyak" Riwayat: cek
filteredSales.lengthbukanstate.sales(sebelumnya negatif -17 saat filter aktif). - Produk hilang / loading terus di layar Kasir (fix 3 lapis):
_loadProducts()now non-crashing saat Supabase return 401; empty server result does not overwrite in-memoryproducts(previouslyproducts = []wiped the on-screen list)._init()no longer waits& _initAuth()first. Auth check now via_ensureSessionAndAuth(): if refresh token expired, immediately set_sessionExpired = true, keepstartPolling()alive, and show a "login ulang" message. This avoids_loadProducts()being blocked by a 30s timeout + 401._refresh()and realtime.stream()products stay alive via 1-minute polling (30 default products, "Muat lebih banyak" to extend).
- Auth flow lengkap (2026-10-18):
login_screen.dart+AuthGate(main.dart) sudah ada & ter-wiring. Dilengkapi:AuthGatesekarang route keLoginScreensaatstate.sessionExpiredtrue (bukan cuma!isLoggedIn) — saat session mati user dipaksa login ulang, bukan nyangkut di HomePage dengan sesi mati._ensureSessionAndAuth()membedakan auth benar-benar mati (AuthException/refresh balikin session tak valid →sessionExpired=true) vs network blip (error jaringan → TIDAK tandai sessionExpired, jadi tak salah-logout saat internet putus sesaat).refresh()stop query saat auth mati (hindari 401 sia-sia + cukup notify agar redirect).login()resetsessionExpired+ langsung muat produk/riwayat (tak tunggu poll 1 menit). - Cleanup analyzer (2026-10-18):
flutter analyze lib/turun dari 20 issue → 0 issue. Yang diperbaiki: hapus_initAuth()(sudah diganti_ensureSessionAndAuth), hapus class_ProductCard(tidak dipakai), hapus dead code ternarysubmitting/cancelling ? null :(variabel tidak pernahtrue) di Kasir & Riwayat, rename local var_listenersAdded/_sheetController→ tanpa underscore (produk_screen),print→debugPrint(offline_queue_service), perbaiki guardBuildContextasync (context.mounted/parentContext.mounted/sheetContext.mounted). Build APK debug sukses + terpasang & jalan normal di HP (tanpa crash). - Fix Gitea ROOT_URL (infra): link download APK sebelumnya mengarah ke IP Tailscale lama → fixed ke
https://git.gosambi.uk/.
v1.2.0+5 — 2026-10-17
- Foto produk: bucket Supabase Storage
product-images(publik, limit 5MB, jpg/png/webp) + kolomproducts.image_path.image_picker^1.2.0 (kompres 800px/quality80). Tombol Kamera/Galeri/Hapus di form Tambah+Edit (produk_screen.dart). Thumbnail di daftar produk (_ProductRow) + keranjang kasir (_KasirAvatar). AndroidManifest +queries intent kamera/galeri. - Fix MIME:
image/jpg→image/jpeg(server 415). - Fix storage policy: bucket
storage.objectstanpa policy → upload 403. Fix: 3 policy SQL (INSERT auth, SELECT public, DELETE auth) — fix di server, APK tidak perlu rebuild.
v1.1.0 — 2026-07-24
- Dialog pembayaran kasir: tombol "Selesai & Cetak" menampilkan loading state (spinner + "Memproses...") dengan guard anti-doble-klik.
- Dialog struk pembayaran: tombol "Cetak Struk" memiliki loading state ("Mencetak...") dan disable saat proses cetak.
- Metode Bayar Nanti: dialog struk mengubah judul menjadi "Pesanan Berhasil" dengan icon receipt.
- Dialog pembayaran di Riwayat: di-redesign dengan card summary (Total, Sudah Bayar, Sisa), badge status, helper text dinamis, StatefulBuilder + loading spinner, actions horizontal (Batal | Simpan).
- Dialog batalkan transaksi di Riwayat: icon warning amber, StatefulBuilder + loading spinner, actions horizontal (Batal | Batalkan).
- Bottom sheet detail riwayat: menggunakan StatefulBuilder dengan
currentSalemutable agar data ter-update setelah pembayaran tercatat; dialog pembayaran otomatis tutup setelah simpan. - Perbaiki kembalian minus: field "Kembali" hanya ditampilkan jika
paymentStatus == 'paid'dan di-clamp kemax(0, ...). Kolomchangejuga di-update saatrecordPayment. - Fitur "Tambah Pesanan Manual" di kasir: tombol "Tambah Manual" di bawah search bar untuk input produk yang belum ada di database, masuk keranjang dengan badge "MANUAL", dan tidak mengotori stok produk.
- Role kasir diperbolehkan menerima pembayaran untuk transaksi unpaid/partial dari detail riwayat.
- Sembunyikan "Harga Pokok" dan "Laba Kotor" di detail transaksi untuk role kasir; hanya admin yang melihatnya.
- Bottom nav kasir: tab "Printer" diganti "Pengaturan". Admin tetap punya tab "Pengaturan" untuk pengaturan toko + akun kasir.
- Settings: tambah periodic sync setiap 30 detik agar multi-device tetap sinkron; pull-to-refresh tetap ada dan reset timer.
- Tampilkan versi aplikasi di Pengaturan menggunakan
package_info_plus(format: "Versi X.Y.Z (build)"). - Preparasi multi-tenant: tambah kolom
store_idke tabelproducts,sales,store_settings, dan tabel barustores. Semua query app kini includestore_id. RLS dimatikan untuk sementara. - Deploy release APK ke device: RMX2061 (Android 11 API 30).
v1.0.1 — 2026-07-22
- Perbaikan UI/UX form produk: auto-scroll keyboard pada Tambah Produk dan Edit Produk.
- reorganisasi metode pembayaran: kelompok atas 3 (Tunai, Bank, QRIS), kelompok bawah 2 (Bayar Nanti, Split).
- Validasi harga reseller: harga reseller tidak boleh lebih tinggi dari harga umum.
- Fix sinttas
produk_screen.dartdankasir_screen.dart(flutter analyzeclean dari error). - Deploy release APK ke device: TECNO KM4 (Android 15).
v1.0.0 — 2026-07-20 (Resmikan)
- Fitur lengkap: Auth, Kasir, Produk, Riwayat, Pembayaran, Struk, Printer Bluetooth, Export PDF.
- Release APK pertama untuk distribusi.
1. Ringkasan Produk
Dutra Store adalah aplikasi kasir digital berbasis Flutter untuk toko grosir. Aplikasi ini menghubungkan multi-device melalui sinkronisasi realtime Supabase, sehingga data produk dan transaksi dapat diakses dari berbagai perangkat secara bersamaan.
2. Tujuan Bisnis
- Mempercepat transaksi penjualan di toko grosir.
- Menjamin harga konsisten untuk pelanggan umum dan reseller.
- Menyediakan laporan penjualan yang realtime.
- Mengurangi risiko inkonsistensi data antar perangkat kasir.
- Memisahkan wewenang antara admin (pemilik) dan kasir (karyawan).
3. Target Pengguna
- Admin (pemilik toko): mengelola produk, melihat laporan, mengubah setting toko.
- Kasir (karyawan): melakukan transaksi penjualan, tidak bisa mengubah data master.
- Pelanggan reseller yang mendapatkan harga khusus.
4. Lingkung Aplikasi
- Platform: Android (emulator & perangkat fisik).
- State management: Provider.
- Backend: Supabase (PostgreSQL).
- Autentikasi: Supabase Auth (email + password) + tabel
profilesuntuk role. - Sinkronisasi: Realtime stream untuk
productsdansalespada paid tier; pada free tier menggunakan periodic polling 1 menit saat app aktif sebagai fallback, ditambah pull-to-refresh manual. - Penyimpanan lokal:
shared_preferencesuntuk cache JSON + session auth dikelola oleh Supabase. - Startup flow:
AuthGatemengecek session/login state →LoginScreenatauHomePage.
5. Role & Permission
5.1 Admin
- Login ke aplikasi.
- CRUD produk (tambah, edit, hapus, atur stok & harga).
- Melihat riwayat transaksi & laporan.
- Mengelola akun kasir (tambah / nonaktifkan).
- Mengubah pengaturan toko (nama toko, mata uang, printer default).
5.2 Kasir
- Login ke aplikasi.
- Melakukan transaksi penjualan (tab Kasir).
- Tidak dapat mengedit atau menghapus produk.
- Tidak dapat melihat laporan lengkap / riwayat lama.
- Tidak dapat mengubah pengaturan toko.
5.3 Guest (tanpa login)
- Tidak diizinkan. Semua perangkat harus login terlebih dahulu.
6. Fitur Utama
6.1 Autentikasi
- Screen login (email + password) menggunakan Supabase Auth.
- Logout.
- Role ditentukan dari kolom
roledi tabelprofilesSupabase. - Session disimpan lokal; startup otomatis login jika session masih valid.
6.2 Manajemen Produk (Admin only)
- CRUD produk: nama, barcode/no. produk, harga umum, harga reseller, stok.
- Pencarian produk berdasarkan nama atau barcode.
- Indikator stok habis (label "Habis" + opacity reduced).
- Input harga menggunakan format Rupiah otomatis (
RupiahInputFormatter): pengguna mengetik angka mentah, field otomatis menampilkanRp XX.XXX. - Scan barcode produk: tombol scan di form "Tambah Produk" menggunakan kamera HP. Setelah barcode discan, hasil scan otomatis diisi ke field "Barcode / No. Produk" (bukan nama). Admin tetap mengisi nama produk, harga, dan stok secara manual.
- Di list produk, barcode ditampilkan di bawah nama produk untuk memudahkan identifikasi.
- Seed data otomatis jika tabel produk kosong.
6.3 Transaksi Kasir (Admin & Kasir)
- Toggle tipe pembeli: Umum / Reseller (global per transaksi).
- Fitur harga reseller dapat dinonaktifkan dari Pengaturan Toko → Mode Reseller Aktif. Jika dimatikan: toggle Pembeli (Umum/Reseller) dan badge per-item (Reseller/Umum) disembunyikan, semua harga otomatis Umum.
- Tambah produk ke keranjang dengan 1 tap.
- Override harga per line: setiap item di keranjang dapat di-toggle antara Umum / Reseller.
- Qty per item, dengan batas stok.
- Diskon transaksi (Rp).
- Perhitungan otomatis: subtotal, diskon, total, bayar, kembalian.
6.4 Keranjang (Cart)
- Bottom bar ringkas menampilkan jumlah item & total.
- Draggable bottom sheet untuk detail keranjang.
- Tombol "Kosongkan" keranjang.
- Input diskon dengan format Rupiah otomatis.
- Tombol "Bayar" mengarah ke dialog pembayaran.
6.5 Pembayaran
- Dialog pembayaran scrollable (mengantisipasi keyboard).
- Input "Uang Dibayar" dengan format Rupiah otomatis dan kalkulasi kembalian realtime.
- Pemilihan metode pembayaran: Tunai, Bank, QRIS, Split.
- Chip cepat untuk Tunai: Uang Pas, +5rb, +10rb, +20rb, +50rb, +100rb.
- Bank/QRIS: tampilkan informasi petunjuk + input jumlah + status Lunas/Kurang.
- Split Payment: input pembayaran gabungan (Tunai + Bank + QRIS) dengan total split real-time.
- Validasi minimal pembayaran >= total untuk Tunai.
- Tombol "Selesai & Cetak" menyelesaikan transaksi. Saat diproses, tombol menampilkan indikator loading (spinner + teks "Memproses...") dan terkunci agar tidak bisa diklik dua kali.
- Jika offline, transaksi disimpan ke queue lokal dan disinkronkan otomatis saat koneksi kembali.
6.6 Struk / Nota
- Dialog struk setelah pembayaran berhasil.
- Menampilkan: daftar item, diskon, total, bayar, kembali, tanggal, ID transaksi.
- Tombol "Tutup".
6.7 Riwayat Transaksi
- Urutan: transaksi terbaru tampil di paling atas (baik untuk kasir maupun admin).
- Kasir: menampilkan transaksi hanya hari ini (tanggal berjalan).
- Admin: menampilkan transaksi bulan berjalan secara default, dengan opsi filter rentang tanggal menggunakan date range picker untuk melihat data periode lain.
- Export: Admin dapat export laporan transaksi dalam format PDF. Export CSV/Excel ditunda untuk pengembangan berikutnya.
- Summary cards dalam bentuk horizontal slider/carousel dengan dot indicator:
- Pendapatan Hari Ini vs Kemarin (persentase perubahan).
- Pendapatan Bulan Ini vs Bulan Lalu.
- Laba Kotor Bulan Ini vs Bulan Lalu.
- Transaksi Bulan Ini vs Bulan Lalu.
- Swipe kiri/kanan untuk berpindah antar card.
- Dot indicator di bawah slider menandakan card aktif.
- Card aktif menampilkan detail lengkap, card non-aktif menampilkan ringkas.
- List transaksi menampilkan: total, tanggal & waktu, jumlah item, ID transaksi.
- Tap pada item transaksi membuka dialog detail struk (daftar item, diskon, total, bayar, kembali, tanggal, ID transaksi).
- Setiap item di dialog detail menampilkan badge "Reseller" atau "Umum" sesuai tipe harganya.
- Dialog detail riwayat menampilkan Metode Pembayaran (Tunai/Bank/QRIS/Split Payment).
- Jika Split Payment, menampilkan breakdown per metode beserta jumlahnya.
- Dialog detail riwayat memiliki tombol "Cetak Struk" yang mengirim struk ke thermal printer Bluetooth (ESC/POS) — mirip dengan cetak dari dialog pembayaran kasir.
6.9 Retur / Pembatalan Transaksi (Admin only)
- Admin dapat membatalkan transaksi dari dialog detail riwayat.
- Saat dibatalkan, stok produk secara otomatis dikembalikan ke jumlah semula.
- Transaksi yang dibatalkan ditandai dengan label "DIBATALKAN" dan tidak masuk hitungan pendapatan.
- Tombol "Batalkan Transaksi" hanya muncul untuk admin dan hanya pada transaksi yang belum dibatalkan.
- Konfirmasi dialog sebelum pembatalan: "Stok akan dikembalikan secara otomatis. Tindakan ini tidak dapat dibatalkan."
6.10 Stok Minimum & Notifikasi
- Setiap produk memiliki field Stok Minimum (default: 5) yang dapat diatur saat tambah/edit produk.
- Di list produk, stok yang di bawah minimum akan menampilkan badge peringatan berwarna oranye: "⚠ Stok rendah (X/Y)".
- Stok habis tetap menampilkan badge merah "Stok habis".
- Field stok minimum ditambahkan di form tambah produk dan form edit produk.
6.11 Laba / Rugi
- Setiap produk memiliki field Harga Pokok (modal) yang dapat diatur saat tambah/edit produk.
- Di Riwayat, menampilkan Laba Kotor per transaksi:
Pendapatan - Harga Pokok. - Kasir: melihat Laba Hari Ini.
- Admin: melihat Laba Kotor Total (semua transaksi) dan Laba Kotor per periode filter.
- Laba kotor dihitung otomatis saat checkout berdasarkan harga pokok yang tersimpan di produk.
- Transaksi yang dibatalkan tidak masuk hitungan laba/rugi.
6.12 Pengaturan Toko (Admin only)
- Nama toko.
- Mata uang default (default: IDR).
- Toggle mode reseller (aktif/nonaktif).
7. Screen / Menu Structure
7.1 Login Screen
- Email + password.
- Tombol "Masuk".
- Loading indicator selama inisialisasi AppState.
- Error handling (invalid credentials).
- Layout:
SafeArea+SingleChildScrollViewdengan padding dinamis untuk keyboard.
7.2 AuthGate (main.dart)
StreamBuilder+Selector<AppState>untuk mengecek:state.loading→ tampilkanCircularProgressIndicator.state.isLoggedIn→ tampilkanHomePage.- Selain itu → tampilkan
LoginScreen.
- Listener
onAuthStateChangediAppStateuntuk updateuserIddanuserRoleotomatis.
7.3 HomePage (bottom nav)
- AppBar: judul "Dutra Store" + icon logout.
- Body:
IndexedStackdengan screens sesuai role. - BottomNavigationBar:
- Kasir: 2 tab (
Kasir,Riwayat). - Admin: 5 tab (
Kasir,Produk,Riwayat,Akun,Pengaturan).
- Kasir: 2 tab (
- Navigasi menggunakan
_indexyang direset ke 0 jika role berubah.
7.4 Kasir Screen
- Search bar + segmented button tipe pembeli (Umum/Reseller).
- Grid produk 2 kolom.
- Bottom bar: jumlah item + total + icon expand.
- Draggable bottom sheet keranjang.
- Dialog pembayaran + struk.
7.5 Produk Screen
- Search bar + form tambah/edit produk.
- Grid daftar produk.
- Hanya admin yang melihat tab ini.
7.6 Riwayat Screen
- Admin: date range picker untuk filter transaksi berdasarkan tanggal.
- Summary cards:
- Kasir: Transaksi Hari Ini, Pendapatan Hari Ini.
- Admin: Semua Transaksi, Total Pendapatan, Jumlah Semua Transaksi.
- List transaksi:
- Kasir: hanya transaksi hari ini.
- Admin: semua transaksi atau sesuai filter tanggal.
- Setiap item: total, tanggal & waktu, jumlah item, ID transaksi.
7.7 Kelola Akun Kasir (Admin only)
- Screen untuk mengelola akun kasir.
- Form tambah akun kasir: email, password, nama lengkap.
- List akun kasir yang ada dengan opsi hapus.
- Setiap akun memiliki role
kasirdi tabelprofiles.
7.8 Pengaturan Toko (Admin only)
- Screen untuk mengubah pengaturan toko.
- Field: nama toko, mata uang (default: IDR), alamat printer Bluetooth.
- Toggle mode reseller (aktif/nonaktif).
- Pengaturan disimpan di
shared_preferences.
7.9 Login/Logout Flow
- Login:
LoginScreen→AppState.login()→ updateuserId+userRole→AuthGateotomatis navigate keHomePage. - Logout: icon logout di AppBar →
AppState.logout()→ clear cart + discount →AuthGateotomatis navigate keLoginScreen.
8. Model Data
Product
| Field | Tipe | Keterangan |
|---|---|---|
| id | text (PK) | UUID unik |
| name | text | Nama produk |
| barcode | text | Barcode / No. Produk (opsional) |
| price | number | Harga umum |
| price_reseller | number | Harga reseller |
| stock | integer | Stok tersedia |
| min_stock | integer | Batas stok minimum untuk peringatan (default: 5) |
| harga_pokok | number | Harga modal per unit (untuk hitung laba kotor) |
Sale
| Field | Tipe | Keterangan |
|---|---|---|
| id | text (PK) | UUID unik |
| created_at | timestamp | Waktu transaksi |
| items | jsonb | Array of {name, price, qty, isReseller} |
| subtotal | number | Total sebelum diskon |
| discount | number | Diskon transaksi |
| total | number | subtotal - discount |
| paid | number | Jumlah dibayar |
| change | number | paid - total |
| user_id | text (FK) | ID user yang melakukan transaksi (opsional) |
| is_cancelled | boolean | true jika transaksi dibatalkan (default: false) |
| total_cost | number | Total harga pokok dari item yang terjual (untuk laba kotor) |
| payment_method | text | Metode pembayaran: 'tunai' (default), 'bank', 'qris', 'split' |
| split_payment | jsonb | Object pembayaran gabungan: {"tunai": 50000, "bank": 100000, "qris": 50000} (opsional) |
CartLine (lokal)
| Field | Tipe | Keterangan |
|---|---|---|
| product | Product | Referensi produk |
| qty | int | Jumlah |
| isReseller | bool | true = harga reseller dipakai |
Profile (Supabase)
| Field | Tipe | Keterangan |
|---|---|---|
| id | uuid (FK auth.users) | Referensi user |
| role | text | 'admin' atau 'kasir' |
| full_name | text | Nama lengkap (opsional) |
8.1 Utilitas Format Rupiah
RupiahInputFormatter:TextInputFormatteruntuk field harga/uang.- Saat pengguna mengetik angka, field otomatis menampilkan format
Rp XX.XXX(contoh:50000→Rp 50.000). RupiahInputFormatter.getRawValue(String): mengembalikan nilaiint?dari string yang diformat, untuk kebutuhan perhitungan.- Digunakan pada:
- Form tambah/edit produk:
Harga Umum,Harga Reseller. - Dialog pembayaran:
Uang Dibayar. - Keranjang:
Diskon.
- Form tambah/edit produk:
9. Alur Transaksi
- User login → role ditentukan → navigasi sesuai role.
- Kasir:
- Pilih tipe pembeli (Umum / Reseller).
- Tambah produk ke keranjang.
- Sesuaikan qty & tipe harga per item.
- Isi diskon (opsional).
- Tekan "Bayar" → dialog pembayaran muncul.
- Input uang dibayar / pilih chip cepat.
- Validasi, lalu checkout → simpan ke Supabase, kurangi stok.
- Tampilkan dialog struk.
- Tutup struk → kembali ke tab Kasir dengan keranjang kosong.
- Admin:
- Dapat melakukan transaksi seperti kasir.
- Dapat menambah/edit/hapus produk.
- Dapat melihat riwayat & laporan.
- Dapat mengelola akun kasir & pengaturan toko.
10. UI / UX Requirements
- Tema gelap dengan aksen amber.
- Kartu produk dengan sudut rounded, shadow, avatar inisial nama.
- Bottom sheet keranjang dengan handle drag.
- Semua dialog yang berpotensi overflow harus menggunakan scroll view.
- Label dan mata uang dalam Bahasa Indonesia (Rupiah).
- UI menyesuaikan role: tombol/fitur yang tidak diizinkan disembunyikan (tidak hanya di-disable).
10.1 Design System
- Tema utama: Dark theme. Warna aksen: amber.
- Radius: 18 (kartu), 12 (input/dialog), 8 (chip/tag).
- Spacing: padding screen/list
EdgeInsets.all(12), padding form/dialogEdgeInsets.all(16), gap fieldSizedBox(height: 10), gap sectionSizedBox(height: 14). - Kartu: padding minimal 12, jangan hardcode tinggi. Jika 3+ field, prioritaskan wrap/scroll.
- Input: label jelas, helper text untuk opsional. Jika form panjang, bungkus
SingleChildScrollView+ padding bottomMediaQuery.of(context).viewInsets.bottomagar tidak tertutup keyboard. - Tombol: primary =
FilledButton(aksi utama: Bayar, Tambah, Simpan), destructive = merah (kDanger), secondary =TextButton/OutlinedButton.
10.2 Responsif & Overflow
- Grid Kasir: 2 kolom di HP, 3–4 kolom di tablet (lebar > 600dp). Hindari
childAspectRatiotetap; pakaiAspectRatio(1.1)atau biarkan grid menentukan tinggi. - Bottom Sheet: keranjang harus berupa bottom sheet (bukan panel tetap), bisa di-drag, konten selalu scrollable (
SingleChildScrollViewtanpaExpanded). - Dialog: input panjang harus scrollable. Jangan pakai
Expandeddi dalam dialog yang bisa overflow. - Produk: form + list dalam satu halaman scroll (
SingleChildScrollView+ListView.shrinkWrap). - Teks: jangan hardcode font size. Batasi nama produk max 2 baris (
maxLines: 2,overflow: TextOverflow.ellipsis).
10.3 UX Kasir (Critical Path)
- Target: transaksi selesai dalam 3 tap (pilih → bayar → selesai).
- Tampilkan total, diskon, kembalian real-time.
- Tipe pembeli pakai segmented button (Umum/Reseller), bukan dropdown.
- Setiap item keranjang bisa ubah qty atau tipe harga dalam 1 tap.
- Stok habis: tampil badge merah "Habis" + matikan visual/tap.
- Stok menipis: badge peringatan oranye sebelum habis.
10.4 Aksesibilitas & Feedback
- Minimal tap target: 44x44dp.
- Kontras teks minimal 4.5:1.
- Jangan andal warna saja (tambah ikon/label). Contoh: stok habis = badge merah + teks "Habis".
- Setiap aksi ada
SnackBarfeedback: sukses hijau, error merah, info biru/abu. - Loading state untuk operasi > 500ms (
CircularProgressIndicator(color: kAccent)).
10.5 Role-Based Visibility
- Fitur tidak diizinkan untuk role tertentu harus disembunyikan sepenuhnya, bukan cuma di-disable.
- Admin: lihat semua transaksi + filter tanggal + aksi batalkan.
- Kasir: lihat transaksi hari ini, tidak ada aksi batalkan/filter.
10.6 Reusability & Konsistensi
- Buat widget reuseable untuk komponen berulang:
_Tag,_ReportCard,_Avatar,_Stepper. - Taruh konstanta warna/spacing di
lib/utils/colors.dart. - Konsisten pakai
formatRupiah()untuk semua angka mata uang.
10.7 Checklist Sebelum Submit Perubahan UI/UX
flutter analyze→ No issues found (tanpa error).- Coba di layar kecil (HP 5") dan besar (tablet).
- Coba dengan keyboard muncul (input uang/diskon).
- Pastikan tidak ada hardcoded height yang bikin overflow.
- Semua teks dalam Bahasa Indonesia.
- Konsisten pakai
formatRupiah()untuk angka mata uang.
11. Non-Fitur (Out of Scope untuk versi ini)
Printer thermal Bluetooth (direncanakan Sprint 4).✅ Sudah diimplementasikan (Sprint 4).Laporan bulanan / export PDF/Excel.✅ Sudah diimplementasikan (export PDF & CSV di Sprint 3/4).Barcode scanner.✅ Sudah diimplementasikan (Sprint 2,mobile_scanner).- Multi-cabang / multi-toko.
12. Kebutuhan Non-Fungsional
- Sinkronisasi realtime < 2 detik antar perangkat.
- Offline mode: data terbaru tetap tampil meskipun koneksi terputus (cache lokal).
- Build APK debug untuk emulator Android 13+.
- Session auth harus aman (token disimpan aman, auto-logout setelah inactivity opsional).
13. Risiko & Mitigasi
| Risiko | Mitigasi |
|---|---|
| Stok menjadi negatif | Validasi di addToCart & changeQty dibatasi oleh stok produk. |
| Data inkonsistenan saat并发 | Supabase realtime + operasi atomic di sisi backend. |
| Keyboard menutupi input di dialog | Semua dialog input menggunakan SingleChildScrollView. |
| Akses tidak sah | Role-based UI + validasi di Supabase (RLS policies). |
| Koneksi putus saat checkout | Simpan transaksi ke queue lokal, kirim saat koneksi kembali (opsional). |
14. Sprint Breakdown
Sprint 0 — Foundation (Selesai)
- Setup Flutter + Provider + Supabase.
- Struktur dasar: screens, models, state, utils.
- CRUD Produk + Realtime stream.
- Dual pricing (Umum / Reseller).
- UI redesign (dark theme, amber accent).
Sprint 1 — Transaksi Kasir (Selesai)
- Tab Kasir: grid produk + search + tipe pembeli.
- Cart: bottom bar + bottom sheet + qty control.
- Diskon transaksi.
- Dialog pembayaran + kalkulasi kembalian.
- Dialog struk.
- Fix overflow (Produk tab, Kasir dialog, Cart sheet).
Sprint 2 — Auth & Role-Based Access (Selesai)
- Setup Supabase Auth (email + password).
- Tabel
profilesdengan kolomrole. - Screen login + loading state + error handling.
- Logout icon di AppBar.
- Integrasi role ke AppState (
userId,userRole,isAdmin,isKasir,isLoggedIn). - Auth state listener (
onAuthStateChange) untuk update otomatis saat login/logout. - UI adaptif sesuai role:
- Bottom nav: Kasir (2 tab), Admin (3 tab).
- Riwayat: Kasir lihat hari ini, Admin lihat semua.
- Produk: hanya admin.
- Seed admin default via
_seedAdminIfEmpty(). - Audit trail: tambah
user_idpada modelSaledan simpan saat checkout. - Fix bottom nav assertion error untuk role kasir (nav disembunyikan jika < 2 item).
- Fix login screen stuck/overflow dengan
SafeArea+SingleChildScrollView+ padding dinamis. - Optimasi
AuthGatedenganSelectoragar tidak rebuild berlebihan. - Rupiah input formatter untuk field harga/uang di Produk, Pembayaran, dan Diskon.
- Scan barcode produk di form Produk menggunakan
mobile_scanner+ permission kamera.
Sprint 3 — Admin Features
- Riwayat transaksi dengan filter role-based (kasir: hari ini, admin: semua).
- Summary cards adaptif sesuai role.
- Filter date range untuk admin di riwayat menggunakan date range picker.
- Manajemen akun kasir (tambah/hapus akun kasir dari dalam app).
- Edit akun kasir (ubah nama/email/password via dialog edit, password pakai
auth.admin.updateUserById). - Pengaturan toko (nama toko, mata uang, toggle reseller).
- Export laporan ke PDF dan CSV (Excel) menggunakan
pdf,csv,path_provider, danshare_plus.
Sprint 4 — Polish & Integration
- Export laporan ke PDF dan CSV (Excel) menggunakan
pdf,csv,path_provider, danshare_plus. - Offline mode improvements (queue transaksi menggunakan
OfflineQueueService+connectivity_plus). - Thermal printer Bluetooth (package
print_bluetooth_thermal+esc_pos_utils_plus, ganti dariblue_thermal_printeryang gagal build). - Cetak struk langsung dari dialog pembayaran kasir.
- Retur / Pembatalan transaksi (admin dapat batalkan transaksi dari riwayat, stok otomatis dipulihkan).
- Stok minimum & notifikasi (field min_stock di produk, badge warning di list produk).
- Laba / Rugi (harga pokok per produk, laporan laba kotor di riwayat).
- Build release APK untuk distribusi.
- Testing & bug fixing menyeluruh.
15. Status Implementasi Saat Ini
Fitur yang Sudah Berjalan
- ✅ Login/logout dengan Supabase Auth
- ✅ Role-based access: Admin (3 tab: Kasir, Produk, Riwayat, Pengaturan) dan Kasir (2 tab) — Akun Kasir & Printer digabung ke dalam tab Pengaturan
- ✅ CRUD Produk dengan barcode scanner
- ✅ Transaksi kasir dengan dual pricing (Umum/Reseller)
- ✅ Mode Reseller dapat dinyalakan/matikan dari Pengaturan Toko (saat mati, toggle harga reseller di kasir disembunyikan)
- ✅ Cart bottom sheet + diskon + pembayaran + struk
- ✅ Pembayaran: tombol "Selesai & Cetak" pakai loading guard (spinner + "Memproses...") agar tidak dobel klik
- ✅ Riwayat transaksi: terurut terbaru di atas, filter role-based, date range
- ✅ Detail riwayat: badge per-item "Reseller"/"Umum" + tombol "Cetak Struk"
- ✅ Manajemen akun kasir (tambah/hapus/edit)
- ✅ Pengaturan toko (nama, mata uang, toggle reseller) — nama app "Dutra Store"
- ✅ Export laporan PDF
- ⏸️ Export laporan CSV/Excel (ditunda untuk pengembangan berikutnya)
- ✅ Offline mode queue untuk transaksi
- ✅ Thermal printer Bluetooth (scan, connect, auto-reconnect, cetak struk ESC/POS)
- ✅ Cetak struk dari riwayat (tap transaksi → dialog detail → tombol "Cetak Struk")
- ✅ Rupiah input formatter
- ✅ Overflow fixes pada semua screen
- ✅ Retur/Pembatalan transaksi (admin dapat batalkan dari dialog detail riwayat, stok otomatis dipulihkan)
- ✅ Stok minimum & notifikasi (badge warning stok rendah di list produk)
- ✅ Laba/Rugi (harga pokok per produk, laporan laba kotor di riwayat: Laba Hari Ini untuk kasir, LABA KOTOR untuk admin)
- ✅ Tab Riwayat: summary cards compact (tap untuk expand), list transaksi ditampilkan di bawah summary, detail transaksi menggunakan bottom sheet
- ✅ Tab Riwayat: ringkasan bulanan dengan perbandingan MoM (Pendapatan, Laba Kotor, Transaksi) untuk admin
- ✅ Tab Riwayat: admin melihat transaksi bulan berjalan secara default (bukan semua transaksi)
- ✅ Tab Riwayat: summary cards dalam bentuk horizontal slider/carousel dengan dot indicator, swipe kiri/kanan untuk pindah card, card aktif menampilkan detail lengkap
- ✅ Tab Produk: form tambah produk dipindah ke bottom sheet (FAB + Tambah Produk), list produk tetap terlihat
- ✅ Tab Kasir: animasi pulse di cart bar saat produk ditambahkan
- ✅ Dialog pembayaran: loading overlay + disable input saat proses checkout + minimum 300ms visibility
- ✅ Dialog pembayaran: pilihan metode pembayaran (Tunai, Bank, QRIS, Split) dengan chip selector
- ✅ Dialog pembayaran: chip cepat untuk Tunai (Uang Pas, +5rb, +10rb, +20rb, +50rb, +100rb)
- ✅ Dialog pembayaran: split payment support dengan total split real-time
- ✅ Receipt & detail riwayat: menampilkan metode pembayaran dan breakdown split payment
- ✅ Sale model: field payment_method + split_payment untuk dokumentasi transaksi
- ✅ Release APK (app-release.apk 67.5MB)
- ✅ Multi-tenant preparation: kolom
store_idditambahkan ke tabelproducts,sales,store_settings, dan tabel barustores. Semua query app kini includestore_iduntuk preparasi multi-store.
Fitur yang Didefer / Belum
- ✅ Ganti password akun kasir via Supabase admin API (
auth.admin.updateUserByIdtersedia di gotrue 2.26.0). - ✅ Testing & bug fixing menyeluruh (analyzer
No issues found!, 0 error/warning). - ⏳ Testing & bug fixing menyeluruh (belum dilakukan comprehensive testing).
- ✅ Build release APK untuk distribusi (app-release.apk 66.9MB).
Catatan Teknis
- Nama aplikasi: Dutra Store (launcher label, AppBar, judul login, export).
- Admin tab dikurangi: Akun Kasir & Printer digabung ke dalam tab Pengaturan (admin sekarang 3 tab: Produk, Riwayat, Pengaturan).
- Printer auto-connect saat app start (MAC tersimpan di shared_preferences).
flutter analyzesekarang No issues found! (semuawithOpacitydigantiwithValues(alpha:),use_build_context_synchronouslydi-handle, dead code dihapus).- App sudah di-deploy ke device: TECNO KM4 (Android 15) dan RMX2061 (Android 11 API 30), keduanya berjalan normal.
- Database migration:
- Jalankan SQL di
supabase/migrations/20240101_add_cancel_and_min_stock.sqluntuk kolomis_cancelled(sales) danmin_stock(products). - Jalankan SQL di
supabase/migrations/20240102_add_harga_pokok_and_profit.sqluntuk kolomharga_pokok(products) dantotal_cost(sales). - Jalankan SQL di
supabase/migrations/20240103_add_payment_method.sqluntuk kolompayment_methoddansplit_payment(sales) — sudah dijalankan. - Jalankan SQL tambahan untuk kolom
store_id(multi-tenant preparation): tambah tabelstores, tambah kolomstore_idkeproducts,sales,store_settings, backfill data existing dengan default store ID, dan buat index. RLS dimatikan untuk semua tabel.
- Jalankan SQL di
- UI/UX skill tersimpan di
.kilo/agent/uiux-pos-grosir.mddan bagian PRD 10.1-10.7. - Color scheme: brand amber dominant di Kasir & Produk, hijau untuk harga umum, merah untuk error/habis, abu untuk muted.
Infra & Setup App
Backend Supabase (Self-Host)
- URL:
https://supa.gosambi.uk(gateway envoy port 8001) - Keys:
D:\HomeLabs\backups\supabase-keys-local.txt(ANON + SERVICE) - Backup data: script
D:\HomeLabs\scripts\supabase_backup.py→D:\HomeLabs\backups\supabase-nwuupvnodsgxdhqcakqr\ - Schema backup:
backups\...\schema_selfhost.sql - Config app:
lib\config.dart(gitignored — berisi URL + keys, jangan commit) - Migrasi cloud → self-host selesai 2026-10-17
- Akun: admin@posgrosir.local, kasir1@toko.com, bibi/bubu/baba@toko.com (password sementara
GrosirBaru2026!)
Standar Rilis APK
- Naikkan versi di
pubspec.yaml(misal1.2.0+5→1.3.0+6). - Commit &
git push gitea main(single remote Gitea; jangan commit APK ke repo). flutter build apk --releasediD:\HomeLabs\app-grosir(jika gagal:.gradlew --stop+flutter clean).- Verifikasi APK: file
build\app\outputs\flutter-apk\app-release.apk; pastikan stringsupa.gosambi.ukada dilibapp.so. - Instal ke HP:
adb -s fd683806 install -r app-release.apk. - Buat tag
v1.x.ydi Gitea + upload APK ke Release (git.gosambi.uk/onie/app-grosir/releases), jangan upload APK langsung ke repo Git. - Catat versi baru di PRD.md ini + rule.md (singkat saja).
Device Testing
- HP utama: Realme RMX2061 (Android 11, device ID
fd683806, packagecom.grosir.app_grosir) - HP admin kedua: sudah ada (untuk testing notifikasi multi-device)