# Apskel POS Flutter Aplikasi Point of Sale (POS) berbasis Flutter untuk manajemen kasir restoran. Mendukung manajemen order, produk, meja, pelanggan, pembayaran, printer bluetooth/network, analitik, dan push notification via FCM. --- ## Fitur Utama - **Autentikasi** — Login dengan email & password, logout, session management - **Order Management** — Buat, kelola, dan proses order - **Produk & Kategori** — Manajemen menu dan kategori produk - **Meja** — Manajemen meja restoran - **Pelanggan** — Data pelanggan dan riwayat transaksi - **Checkout & Pembayaran** — Proses checkout dengan berbagai metode pembayaran - **Split Bill** — Pembagian tagihan - **Void & Refund** — Pembatalan dan pengembalian transaksi - **Printer** — Cetak struk via Bluetooth dan Network printer - **Analitik** — Dashboard, laporan penjualan, produk, kategori, payment method, profit/loss, inventory - **Sinkronisasi** — Sinkronisasi data offline/online - **Push Notification (FCM)** — Notifikasi real-time via Firebase Cloud Messaging --- ## Tech Stack | Kategori | Library | |---|---| | Version Manager | `fvm` — Flutter dipin di `.fvmrc` | | State Management | `flutter_bloc` + `bloc` | | Dependency Injection | `get_it` + `injectable` | | Navigation | `auto_route` | | HTTP Client | `dio` | | Local Database | `sqflite` | | Local Storage | `shared_preferences` | | Firebase | `firebase_core`, `firebase_crashlytics`, `firebase_messaging` | | Push Notification | `flutter_local_notifications` | | Device Info | `device_info_plus`, `package_info_plus` | | Code Generation | `freezed`, `json_serializable` | | Printer | `print_bluetooth_thermal`, `flutter_esc_pos_network` | | Chart | `fl_chart` | | Connectivity | `connectivity_plus` | --- ## Arsitektur Project menggunakan **Clean Architecture** dengan 4 layer: ``` lib/ ├── application/ # BLoC — state management per fitur ├── domain/ # Entity, repository interface, failure ├── infrastructure/ # Implementasi repository, DTO, datasource ├── presentation/ # UI — pages, components, router └── common/ # Shared utilities, DI modules, service, theme ``` ### Struktur per Fitur ``` feature/ ├── application/ │ └── feature_bloc.dart # BLoC ├── domain/ │ ├── entities/ # Domain model (freezed) │ ├── repositories/ # Interface repository │ └── failures/ # Failure types └── infrastructure/ ├── datasources/ │ ├── remote_data_provider.dart │ └── local_data_provider.dart ├── dtos/ # Data Transfer Object (json_serializable) └── repositories/ # Implementasi repository ``` --- ## Setup & Menjalankan ### Prasyarat - **FVM** — project ini mengunci versi Flutter, jangan pakai Flutter global - **Flutter 3.41.9** (Dart 3.11.5) — dipin di `.fvmrc` - Android SDK / Xcode (untuk iOS) - Firebase project yang sudah dikonfigurasi > **Kenapa versinya dikunci?** Stack code generation project ini (`freezed` 2.x, `build_runner` 2.5, `analyzer` 7.7.1) hanya paham bahasa Dart sampai 3.9. Flutter 3.44 ke atas membawa framework yang ditulis di bahasa 3.10+, dan `analyzer` 7.7.1 crash saat membacanya. Selama stack codegen belum dinaikkan, **Flutter 3.41.9 adalah versi terbaru yang masih bisa menjalankan `build_runner`.** Lihat [Troubleshooting](#troubleshooting). ### Instalasi ```bash # Clone repository git clone cd apskel-pos-flutter-v2 # Pasang FVM (sekali per mesin) dart pub global activate fvm # Ambil & pakai versi Flutter yang dipin di .fvmrc fvm install fvm use # Install dependencies fvm flutter pub get # Generate kode fvm dart run build_runner build ``` > Semua perintah Flutter/Dart di project ini **harus diawali `fvm`**. `flutter run` tanpa `fvm` memakai Flutter global dan hasilnya bisa berbeda. ### Menjalankan App Environment **tidak** ditentukan otomatis dari debug/release — harus dioper lewat `--dart-define`: ```bash # Development fvm flutter run --dart-define=ENV=dev # Production fvm flutter run --dart-define=ENV=prod # Production, release mode fvm flutter run --release --dart-define=ENV=prod ``` Kalau `ENV` tidak dioper, nilainya jatuh ke `dev` (lihat `defaultValue` di `lib/main.dart`). Di VSCode sudah tersedia konfigurasi **Dev** dan **Prod** di `.vscode/launch.json`, tinggal pilih di panel Run and Debug. ### Build `--dart-define=ENV=prod` wajib ikut, kalau tidak APK-nya akan memakai database dan base URL milik dev: ```bash # APK fvm flutter build apk --release --dart-define=ENV=prod # App Bundle (Play Store) fvm flutter build appbundle --release --dart-define=ENV=prod # iOS fvm flutter build ipa --release --dart-define=ENV=prod ``` --- ## Code Generation Project ini banyak memakai kode generated (`freezed`, `injectable`, `auto_route`, `json_serializable`, `flutter_gen`). Kode itu **wajib di-generate ulang** setiap kali kamu: - menambah/mengubah kelas `@freezed` (entity, event, state, DTO) - menambah/mengubah anotasi DI (`@injectable`, `@LazySingleton`, `@Injectable(as: ...)`) - menambah halaman baru dengan `@RoutePage()` - menambah aset di `pubspec.yaml` ```bash # Sekali jalan fvm dart run build_runner build # Mode watch — regenerate otomatis saat file berubah, enak dipakai saat ngoding fvm dart run build_runner watch ``` > **Hindari `--delete-conflicting-outputs`.** Flag itu menghapus seluruh file generated **sebelum** mulai build. Kalau build-nya gagal di tengah jalan, kamu berakhir tanpa kode generated sama sekali dan project tidak bisa di-compile. Pakai hanya kalau build_runner benar-benar mengeluh soal konflik output, dan pastikan working tree-mu bersih dulu supaya bisa `git restore`. --- ## Environment Konfigurasi environment ada di `lib/env.dart` — **itu sumber kebenarannya**, jangan disalin ke sini karena gampang basi. | Environment | Database | |---|---| | `dev` | `apskel_pos_staging.db` | | `prod` | `apskel_pos_prod.db` | Environment dipilih dari `--dart-define=ENV=...` di `lib/main.dart`: ```dart const String env = String.fromEnvironment('ENV', defaultValue: 'dev'); await configureDependencies(env); ``` Nilai `env` diteruskan ke `injectable`, yang lalu memilih antara `DevEnv` (`@dev`) dan `ProdEnv` (`@prod`). Menambah environment baru berarti menambah kelas ber-anotasi di `lib/env.dart`, lalu generate ulang. Karena `dev` dan `prod` memakai **nama database berbeda**, berpindah environment di perangkat yang sama tidak akan mencampur datanya. --- ## Dependency Injection DI menggunakan `get_it` + `injectable`. Semua service, repository, dan BLoC didaftarkan via annotation. ### Modul DI | File | Isi | |---|---| | `di_firebase.dart` | `FirebaseMessaging`, `FlutterLocalNotificationsPlugin`, `DeviceInfoPlugin`, `PackageInfo` | | `di_dio.dart` | `Dio` HTTP client | | `di_shared_preferences.dart` | `SharedPreferences` | | `di_database.dart` | `DatabaseHelper` (SQLite) | | `di_auto_route.dart` | `AppRouter` | | `di_connectivity.dart` | `Connectivity` | Setelah menambah atau mengubah class yang menggunakan annotation injectable, jalankan: ```bash dart run build_runner build --delete-conflicting-outputs ``` --- ## Firebase & FCM ### Setup Firebase sudah dikonfigurasi di `android/app/google-services.json`. Inisialisasi dilakukan di `main.dart` sebelum app berjalan. ### FCM Service `FcmService` (`lib/common/service/fcm_service.dart`) menangani: - Request permission notifikasi (Android 13+ / iOS) - Ambil dan log FCM token - Foreground notification via `flutter_local_notifications` - Background & terminated message handler - Subscribe/unsubscribe topic FCM token dikirim ke server saat login bersama device info. ### Login Payload Saat login, app mengirim data berikut ke API: ```json { "email": "user@example.com", "password": "secret", "fcm_token": "dXj3k9...", "device_id": "abc123", "device_name": "Samsung Galaxy Tab S8", "device_type": "tablet", "platform": "android", "app_version": "1.0.4+9", "os_version": "Android 13 (SDK 33)" } ``` Nilai valid: `device_type` → `mobile | tablet | desktop`, `platform` → `android | ios | web` --- ## Printer Mendukung dua jenis printer: - **Bluetooth** — via `print_bluetooth_thermal` - **Network (LAN)** — via `flutter_esc_pos_network` --- ## Orientasi Layar App dikunci ke mode **landscape** (kiri & kanan) karena didesain untuk tablet POS. --- ## Catatan Development - Selalu pakai `fvm flutter` / `fvm dart`, bukan `flutter` / `dart` global - Jangan edit `injection.config.dart` dan file `*.freezed.dart` / `*.g.dart` secara manual — file tersebut di-generate otomatis - Setelah mengubah kelas `@freezed` atau anotasi DI, jalankan build_runner sebelum menjalankan app - Gunakan `log()` dari `dart:developer` untuk logging, bukan `print()` - `print()` dinonaktifkan di release mode --- ## Troubleshooting ### `build_runner` gagal: `Missing implementation of visitDotShorthandPropertyAccess` Kamu sedang memakai Flutter yang terlalu baru. Cek: ```bash fvm flutter --version # harus 3.41.9 flutter --version # ini Flutter global, boleh versi apa saja ``` Kalau versinya bukan 3.41.9, artinya perintahnya jalan tanpa `fvm`. Log build juga memberi petunjuk yang sama: ``` SDK language version 3.13.0 is newer than `analyzer` language version 3.9.0 ``` Perbaikannya: `fvm use`, lalu ulangi dengan awalan `fvm`. Menaikkan Flutter melewati 3.41.x mengharuskan seluruh stack codegen ikut naik — `freezed` 2 → 3/4, `build_runner` → 2.16, `injectable_generator` 2 → 3, `auto_route` 9 → 10. `freezed` 3 mewajibkan tiap kelas dideklarasikan `abstract`/`sealed` dan menghapus `when`/`map` (diganti pattern matching), jadi ini pekerjaan tersendiri, bukan sambilan. ### Project tidak bisa di-compile, banyak error `_$Something` tidak ditemukan Kode generated-nya hilang atau belum dibuat. Jalankan: ```bash fvm dart run build_runner build ``` ### `fvm: command not found` FVM terpasang di `~/AppData/Local/Pub/Cache/bin` (Windows) atau `~/.pub-cache/bin` (macOS/Linux) yang mungkin belum ada di `PATH`. Tambahkan direktori itu ke `PATH`, atau panggil dengan path lengkap.