chore: initial commit Dutra Store POS app

This commit is contained in:
Onie
2026-07-23 22:12:32 +07:00
commit 22356f630e
175 changed files with 13934 additions and 0 deletions

476
PRD.md Normal file
View File

@@ -0,0 +1,476 @@
# PRD - Dutra Store
## Changelog
### 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.dart` dan `kasir_screen.dart` (`flutter analyze` clean 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 `profiles` untuk role.
- Sinkronisasi: Realtime stream untuk `products` dan `sales` pada paid tier; pada free tier menggunakan **periodic polling 1 menit** saat app aktif sebagai fallback, ditambah pull-to-refresh manual.
- Penyimpanan lokal: `shared_preferences` untuk cache JSON + session auth dikelola oleh Supabase.
- Startup flow: `AuthGate` mengecek session/login state → `LoginScreen` atau `HomePage`.
## 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 `role` di tabel `profiles` Supabase.
- 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 menampilkan `Rp 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` + `SingleChildScrollView` dengan padding dinamis untuk keyboard.
### 7.2 AuthGate (main.dart)
- `StreamBuilder` + `Selector<AppState>` untuk mengecek:
1. `state.loading` → tampilkan `CircularProgressIndicator`.
2. `state.isLoggedIn` → tampilkan `HomePage`.
3. Selain itu → tampilkan `LoginScreen`.
- Listener `onAuthStateChange` di `AppState` untuk update `userId` dan `userRole` otomatis.
### 7.3 HomePage (bottom nav)
- **AppBar**: judul "Dutra Store" + icon logout.
- **Body**: `IndexedStack` dengan screens sesuai role.
- **BottomNavigationBar**:
- **Kasir**: 2 tab (`Kasir`, `Riwayat`).
- **Admin**: 5 tab (`Kasir`, `Produk`, `Riwayat`, `Akun`, `Pengaturan`).
- Navigasi menggunakan `_index` yang 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 `kasir` di tabel `profiles`.
### 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()` → update `userId` + `userRole` → `AuthGate` otomatis navigate ke `HomePage`.
- Logout: icon logout di AppBar → `AppState.logout()` → clear cart + discount → `AuthGate` otomatis navigate ke `LoginScreen`.
## 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`: `TextInputFormatter` untuk field harga/uang.
- Saat pengguna mengetik angka, field otomatis menampilkan format `Rp XX.XXX` (contoh: `50000` → `Rp 50.000`).
- `RupiahInputFormatter.getRawValue(String)`: mengembalikan nilai `int?` dari string yang diformat, untuk kebutuhan perhitungan.
- Digunakan pada:
- Form tambah/edit produk: `Harga Umum`, `Harga Reseller`.
- Dialog pembayaran: `Uang Dibayar`.
- Keranjang: `Diskon`.
## 9. Alur Transaksi
1. User login → role ditentukan → navigasi sesuai role.
2. **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.
3. **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/dialog `EdgeInsets.all(16)`, gap field `SizedBox(height: 10)`, gap section `SizedBox(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 bottom `MediaQuery.of(context).viewInsets.bottom` agar 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 `childAspectRatio` tetap; pakai `AspectRatio(1.1)` atau biarkan grid menentukan tinggi.
- **Bottom Sheet:** keranjang harus berupa bottom sheet (bukan panel tetap), bisa di-drag, konten selalu scrollable (`SingleChildScrollView` tanpa `Expanded`).
- **Dialog:** input panjang harus scrollable. Jangan pakai `Expanded` di 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 `SnackBar` feedback: 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)
- [x] Setup Flutter + Provider + Supabase.
- [x] Struktur dasar: screens, models, state, utils.
- [x] CRUD Produk + Realtime stream.
- [x] Dual pricing (Umum / Reseller).
- [x] UI redesign (dark theme, amber accent).
### Sprint 1 — Transaksi Kasir (Selesai)
- [x] Tab Kasir: grid produk + search + tipe pembeli.
- [x] Cart: bottom bar + bottom sheet + qty control.
- [x] Diskon transaksi.
- [x] Dialog pembayaran + kalkulasi kembalian.
- [x] Dialog struk.
- [x] Fix overflow (Produk tab, Kasir dialog, Cart sheet).
### Sprint 2 — Auth & Role-Based Access (Selesai)
- [x] Setup Supabase Auth (email + password).
- [x] Tabel `profiles` dengan kolom `role`.
- [x] Screen login + loading state + error handling.
- [x] Logout icon di AppBar.
- [x] Integrasi role ke AppState (`userId`, `userRole`, `isAdmin`, `isKasir`, `isLoggedIn`).
- [x] Auth state listener (`onAuthStateChange`) untuk update otomatis saat login/logout.
- [x] UI adaptif sesuai role:
- Bottom nav: Kasir (2 tab), Admin (3 tab).
- Riwayat: Kasir lihat hari ini, Admin lihat semua.
- Produk: hanya admin.
- [x] Seed admin default via `_seedAdminIfEmpty()`.
- [x] Audit trail: tambah `user_id` pada model `Sale` dan simpan saat checkout.
- [x] Fix bottom nav assertion error untuk role kasir (nav disembunyikan jika < 2 item).
- [x] Fix login screen stuck/overflow dengan `SafeArea` + `SingleChildScrollView` + padding dinamis.
- [x] Optimasi `AuthGate` dengan `Selector` agar tidak rebuild berlebihan.
- [x] Rupiah input formatter untuk field harga/uang di Produk, Pembayaran, dan Diskon.
- [x] Scan barcode produk di form Produk menggunakan `mobile_scanner` + permission kamera.
### Sprint 3 — Admin Features
- [x] Riwayat transaksi dengan filter role-based (kasir: hari ini, admin: semua).
- [x] Summary cards adaptif sesuai role.
- [x] Filter date range untuk admin di riwayat menggunakan date range picker.
- [x] Manajemen akun kasir (tambah/hapus akun kasir dari dalam app).
- [x] Edit akun kasir (ubah nama/email/password via dialog edit, password pakai `auth.admin.updateUserById`).
- [x] Pengaturan toko (nama toko, mata uang, toggle reseller).
- [x] Export laporan ke PDF dan CSV (Excel) menggunakan `pdf`, `csv`, `path_provider`, dan `share_plus`.
### Sprint 4 — Polish & Integration
- [x] Export laporan ke PDF dan CSV (Excel) menggunakan `pdf`, `csv`, `path_provider`, dan `share_plus`.
- [x] Offline mode improvements (queue transaksi menggunakan `OfflineQueueService` + `connectivity_plus`).
- [x] Thermal printer Bluetooth (package `print_bluetooth_thermal` + `esc_pos_utils_plus`, ganti dari `blue_thermal_printer` yang gagal build).
- [x] Cetak struk langsung dari dialog pembayaran kasir.
- [x] Retur / Pembatalan transaksi (admin dapat batalkan transaksi dari riwayat, stok otomatis dipulihkan).
- [x] Stok minimum & notifikasi (field min_stock di produk, badge warning di list produk).
- [x] Laba / Rugi (harga pokok per produk, laporan laba kotor di riwayat).
- [x] 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 66.9MB)
### Fitur yang Didefer / Belum
- ✅ Ganti password akun kasir via Supabase admin API (`auth.admin.updateUserById` tersedia 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 analyze` sekarang **No issues found!** (semua `withOpacity` diganti `withValues(alpha:)`, `use_build_context_synchronously` di-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**:
1. Jalankan SQL di `supabase/migrations/20240101_add_cancel_and_min_stock.sql` untuk kolom `is_cancelled` (sales) dan `min_stock` (products).
2. Jalankan SQL di `supabase/migrations/20240102_add_harga_pokok_and_profit.sql` untuk kolom `harga_pokok` (products) dan `total_cost` (sales).
3. Jalankan SQL di `supabase/migrations/20240103_add_payment_method.sql` untuk kolom `payment_method` dan `split_payment` (sales) — **sudah dijalankan**.
- UI/UX skill tersimpan di `.kilo/agent/uiux-pos-grosir.md` dan 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.