Compare commits
74
Commits
896ea6c28c
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
924d698640 | ||
|
|
4342af689f | ||
|
|
1505a83dd3 | ||
|
|
9777f63426 | ||
|
|
7986164a17 | ||
|
|
32f64a197a | ||
|
|
cb7139fc3e | ||
|
|
951e154623 | ||
|
|
ebcddca4bb | ||
|
|
1b4caf45d5 | ||
|
|
038f4d65fe | ||
|
|
865897fc04 | ||
|
|
f5b0e66706 | ||
|
|
1ff144a1a2 | ||
|
|
eb031d4c2a | ||
|
|
7161f523c2 | ||
|
|
d097df1592 | ||
|
|
e8bbef03b0 | ||
|
|
b228538725 | ||
|
|
2ab20a1150 | ||
|
|
b9fcd79962 | ||
|
|
9a5f0c7415 | ||
|
|
d345294a2f | ||
|
|
beb86f6259 | ||
|
|
ea29c62af1 | ||
|
|
c8a84ffe5f | ||
|
|
539c70be17 | ||
|
|
c45848bcf2 | ||
|
|
6d9377f4ab | ||
|
|
56e720e253 | ||
|
|
c7ed45a374 | ||
|
|
a0610459bf | ||
|
|
842b7a5041 | ||
|
|
dc09aabbe8 | ||
|
|
cdc65f8f8f | ||
|
|
e7260ea0e2 | ||
|
|
b173654a9c | ||
|
|
699ca4cfb7 | ||
|
|
d790262fd4 | ||
|
|
60fec68a92 | ||
|
|
ab2d7d7be5 | ||
|
|
6ac88b92ae | ||
|
|
9e4e2ebbd1 | ||
|
|
3513c63259 | ||
|
|
46dfb7e44e | ||
|
|
a0db13feb4 | ||
|
|
f626b18d31 | ||
|
|
200f996d3e | ||
|
|
a739e1c29e | ||
|
|
4481d24375 | ||
|
|
59d8f06d75 | ||
|
|
e625bd3d2a | ||
|
|
5058f99fe0 | ||
|
|
f7cafeb583 | ||
|
|
37d747c0ba | ||
|
|
e3b9444a08 | ||
|
|
d8a28c084d | ||
|
|
d3974b8eda | ||
|
|
97313371c3 | ||
|
|
6fa45c8ac0 | ||
|
|
b2b2e2b111 | ||
|
|
0a8f8d93bb | ||
|
|
f5256fb33d | ||
|
|
c5673a147c | ||
|
|
af182b6f5a | ||
|
|
d78b6165c2 | ||
|
|
0886e8c912 | ||
|
|
93560b85cb | ||
|
|
4eece81eb1 | ||
|
|
873dd7e224 | ||
|
|
3e6dce93fb | ||
|
|
8923ce737d | ||
|
|
c0cf63a176 | ||
|
|
bf7a5708f5 |
@@ -32,6 +32,7 @@ migrate_working_dir/
|
|||||||
.pub-cache/
|
.pub-cache/
|
||||||
.pub/
|
.pub/
|
||||||
/build/
|
/build/
|
||||||
|
android/build/
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
@@ -43,3 +44,8 @@ app.*.map.json
|
|||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
# FVM - versi Flutter dipin di .fvmrc (ikut di-commit), SDK-nya tidak
|
||||||
|
|
||||||
|
# FVM Version Cache
|
||||||
|
.fvm/
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Dev",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": ["--dart-define=ENV=dev"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Prod",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": ["--dart-define=ENV=prod"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"dart.flutterSdkPath": ".fvm/versions/3.41.9"
|
||||||
|
}
|
||||||
@@ -1,16 +1,331 @@
|
|||||||
# apskel_pos_flutter_v2
|
# Apskel POS Flutter
|
||||||
|
|
||||||
A new Flutter project.
|
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.
|
||||||
|
|
||||||
## Getting Started
|
---
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
## Fitur Utama
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
- **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
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
---
|
||||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
|
||||||
|
|
||||||
For help getting started with Flutter development, view the
|
## Tech Stack
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
| 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 <repo-url>
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
`dart pub global activate` memasang `fvm` ke folder bin milik pub yang **belum tentu ada di PATH**. Kalau muncul `fvm: command not found` / `The term 'fvm' is not recognized`, tambahkan foldernya sekali saja:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Windows PowerShell — permanen, buka terminal baru setelahnya
|
||||||
|
[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path','User') + ";$env:LOCALAPPDATA\Pub\Cache\bin", 'User')
|
||||||
|
|
||||||
|
# Untuk terminal yang sedang terbuka
|
||||||
|
$env:Path += ";$env:LOCALAPPDATA\Pub\Cache\bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# macOS / Linux — tambahkan ke ~/.zshrc atau ~/.bashrc
|
||||||
|
export PATH="$PATH:$HOME/.pub-cache/bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
Setelah `fvm` bisa dipanggil, lanjutkan:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
|
|
||||||
|
fvm flutter build apk --release --split-per-abi --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.
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ linter:
|
|||||||
rules:
|
rules:
|
||||||
sort_pub_dependencies: false
|
sort_pub_dependencies: false
|
||||||
prefer_relative_imports: true
|
prefer_relative_imports: true
|
||||||
|
prefer_const_constructors: true
|
||||||
|
prefer_const_literals_to_create_immutables: true
|
||||||
|
prefer_const_declarations: true
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
errors:
|
||||||
@@ -18,3 +21,10 @@ analyzer:
|
|||||||
- test/generated/**
|
- test/generated/**
|
||||||
- "**/**.g.dart"
|
- "**/**.g.dart"
|
||||||
- "**/**.freezed.dart"
|
- "**/**.freezed.dart"
|
||||||
|
- build/**
|
||||||
|
- android/**
|
||||||
|
- ios/**
|
||||||
|
- web/**
|
||||||
|
- windows/**
|
||||||
|
- macos/**
|
||||||
|
- linux/**
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ android {
|
|||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
@@ -45,6 +46,8 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||||
|
|
||||||
// Import the Firebase BoM
|
// Import the Firebase BoM
|
||||||
implementation(platform("com.google.firebase:firebase-bom:34.4.0"))
|
implementation(platform("com.google.firebase:firebase-bom:34.4.0"))
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||||
<!-- Izin khusus untuk akses foto (media images) di Android 33 ke atas -->
|
<!-- Izin khusus untuk akses foto (media images) di Android 33 ke atas -->
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<!-- Izin notifikasi untuk Android 13+ -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<application
|
<application
|
||||||
android:label="Apskel POS"
|
android:label="Apskel POS"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
@@ -41,6 +43,13 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
|
<!-- FCM: default notification icon dan channel -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
|
android:resource="@drawable/ic_notification" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||||
|
android:value="high_importance_channel" />
|
||||||
</application>
|
</application>
|
||||||
<!-- Required to query activities that can process text, see:
|
<!-- Required to query activities that can process text, see:
|
||||||
https://developer.android.com/training/package-visibility and
|
https://developer.android.com/training/package-visibility and
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 846 B |
Binary file not shown.
|
After Width: | Height: | Size: 431 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
@@ -1,3 +1,7 @@
|
|||||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
# This builtInKotlin flag was added automatically by Flutter migrator
|
||||||
|
android.builtInKotlin=false
|
||||||
|
# This newDsl flag was added automatically by Flutter migrator
|
||||||
|
android.newDsl=false
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
id("com.android.application") version "8.7.3" apply false
|
id("com.android.application") version "8.11.1" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
||||||
id("com.google.gms.google-services") version "4.4.4" apply false
|
id("com.google.gms.google-services") version "4.4.4" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
description: This file stores settings for Dart & Flutter DevTools.
|
||||||
|
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||||
|
extensions:
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
# Integrasi Produk Timbangan — POS Mobile & Backoffice
|
||||||
|
|
||||||
|
**Migrasi:** `000089` · **Base URL:** `/api/v1` · **Kompatibilitas:** mundur penuh
|
||||||
|
|
||||||
|
Panduan untuk menjual produk per timbangan (ikan, daging, buah) dari sisi klien.
|
||||||
|
Alasan di balik setiap keputusan desain ada di [`rfc-weight-based-products.md`](./rfc-weight-based-products.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Konsep inti
|
||||||
|
|
||||||
|
**Satu penimbangan = satu baris order.**
|
||||||
|
|
||||||
|
Pelanggan memesan Ikan Tude 4,2 ons, lalu memesan Ikan Tude lagi 5,6 ons. Itu **dua
|
||||||
|
baris terpisah**, karena keduanya dua ikan berbeda yang ditimbang sendiri-sendiri.
|
||||||
|
|
||||||
|
| Bukan begini | Melainkan begini |
|
||||||
|
|---|---|
|
||||||
|
| `quantity: 9.8` | `quantity: 1, weight: 4.2`<br>`quantity: 1, weight: 5.6` |
|
||||||
|
| Dua ikan hilang jejaknya, dan `quantity` bertipe integer sehingga pecahan ditolak | Tiap penimbangan berdiri sendiri, bisa di-void atau dibayar terpisah |
|
||||||
|
|
||||||
|
Empat aturan yang berlaku di seluruh dokumen ini:
|
||||||
|
|
||||||
|
1. `quantity` untuk produk timbangan **selalu 1**. Backend memaksanya, dan database
|
||||||
|
menolak nilai lain lewat constraint `chk_order_items_weight_single_line`.
|
||||||
|
2. `weight` menyimpan angka timbangan, dalam satuan produk itu sendiri (ons, kg, gram —
|
||||||
|
apa pun yang dipilih saat setup).
|
||||||
|
3. Harga baris dihitung `weight × unit_price`, bukan `quantity × unit_price`.
|
||||||
|
`unit_price` tetap berarti harga per satu satuan (per ons).
|
||||||
|
4. **Jangan pernah menggabungkan dua baris** produk timbangan menjadi satu, meski
|
||||||
|
produknya sama.
|
||||||
|
|
||||||
|
Untuk produk biasa tidak ada yang berubah: `weight` tidak dikirim, `quantity` tetap
|
||||||
|
cacah seperti sekarang.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Backoffice — setup produk
|
||||||
|
|
||||||
|
### 2.1 Pastikan satuannya ada
|
||||||
|
|
||||||
|
Satuan disimpan per organisasi. Buat sekali, pakai ulang untuk semua produk timbangan.
|
||||||
|
|
||||||
|
`POST /api/v1/units`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Ons",
|
||||||
|
"abbreviation": "ons",
|
||||||
|
"is_active": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`abbreviation` yang dipakai POS untuk mencetak `4,2 ons` di struk — isi dengan bentuk
|
||||||
|
pendek yang benar-benar ingin ditampilkan. Daftar satuan dibaca lewat `GET /api/v1/units`.
|
||||||
|
|
||||||
|
### 2.2 Buat produk sebagai produk timbangan
|
||||||
|
|
||||||
|
`POST /api/v1/products`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"category_id": "…",
|
||||||
|
"name": "Ikan Tude",
|
||||||
|
"price": 4500,
|
||||||
|
"cost": 3000,
|
||||||
|
"sell_by": "weight",
|
||||||
|
"unit_id": "<id satuan Ons>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Tipe | Keterangan |
|
||||||
|
|---|---|---|
|
||||||
|
| `sell_by` | `"unit"` \| `"weight"` | Opsional, default `"unit"`. Nilai lain **ditolak** dengan pesan jelas. |
|
||||||
|
| `unit_id` | UUID | **Wajib** saat `sell_by: "weight"`, ditolak backend bila kosong. Opsional untuk produk satuan. |
|
||||||
|
| `price` | number | Harga per satu satuan. Rp 4.500 per ons, bukan harga per ikan. |
|
||||||
|
|
||||||
|
Pada `PUT /api/v1/products/:id`, `unit_id` **tidak perlu dikirim ulang** bila produknya
|
||||||
|
sudah punya satuan — mengubah `sell_by` menjadi `"weight"` saja sudah cukup. Yang ditolak
|
||||||
|
adalah kondisi akhirnya: produk yang dijual per timbangan tanpa satuan.
|
||||||
|
|
||||||
|
Keduanya juga bisa diubah lewat `PUT /api/v1/products/:id` dengan bentuk yang sama, dan
|
||||||
|
ikut terbaca di setiap response produk (`GET /api/v1/products`, `/products/all`,
|
||||||
|
`/products/:id`).
|
||||||
|
|
||||||
|
### 2.3 Catatan UI
|
||||||
|
|
||||||
|
- Kunci `sell_by` **setelah produk punya transaksi**. Mengubah produk lama dari `unit`
|
||||||
|
ke `weight` tidak mengubah baris order yang sudah ada — baris lama tetap dihitung per
|
||||||
|
cacah — tapi akan membingungkan pengguna yang melihat riwayatnya.
|
||||||
|
- Saat `weight` dipilih, jadikan pemilih satuan sebagai field **wajib** di form. Backend
|
||||||
|
juga menolaknya, tapi ditangkap di form lebih baik daripada baru gagal saat simpan.
|
||||||
|
- Ubah label harga mengikuti satuan yang dipilih: *"Harga per ons"*.
|
||||||
|
- Untuk produk satuan, pemilih satuan boleh disembunyikan — `unit_id` opsional dan belum
|
||||||
|
dikonsumsi apa pun di POS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. POS Mobile — transaksi
|
||||||
|
|
||||||
|
### 3.1 Bentuk input mengikuti `sell_by`
|
||||||
|
|
||||||
|
| `sell_by` | Input di POS | Yang dikirim |
|
||||||
|
|---|---|---|
|
||||||
|
| `"unit"` | Stepper − / + seperti sekarang | `quantity: n`, tanpa `weight` |
|
||||||
|
| `"weight"` | Papan angka desimal, satuan dari `unit` produk | `quantity: 1` + `weight: 4.2` |
|
||||||
|
|
||||||
|
### 3.2 Mengirim order
|
||||||
|
|
||||||
|
`POST /api/v1/orders`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"outlet_id": "…",
|
||||||
|
"user_id": "…",
|
||||||
|
"order_type": "dine_in",
|
||||||
|
"order_items": [
|
||||||
|
{ "product_id": "<ikan-tude>", "quantity": 1, "weight": 4.2 },
|
||||||
|
{ "product_id": "<ikan-tude>", "quantity": 1, "weight": 5.6 },
|
||||||
|
{ "product_id": "<nasi-goreng>", "quantity": 2 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Bentuk yang sama berlaku untuk `POST /api/v1/orders/:id/add-items` dan untuk pemesanan
|
||||||
|
mandiri `POST /api/v1/self-order/orders`.
|
||||||
|
|
||||||
|
**Presisi.** Berat dibulatkan backend ke 3 desimal. Kirim `4.2` atau `4.237`; angka di
|
||||||
|
bawah `0.001` membulat ke nol dan ditolak.
|
||||||
|
|
||||||
|
**Harga.** Field `unit_price` pada request **diabaikan** — harga selalu diambil backend
|
||||||
|
dari master produk. Jangan mengirim harga hasil hitungan sendiri.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Menampilkan baris
|
||||||
|
|
||||||
|
Setiap `order_items[]` di response membawa empat field tambahan:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"product_name": "Ikan Tude",
|
||||||
|
"quantity": 1,
|
||||||
|
"weight": 4.2,
|
||||||
|
"unit_id": "…",
|
||||||
|
"unit_name": "Ons",
|
||||||
|
"unit_abbreviation": "ons",
|
||||||
|
"unit_price": 4500,
|
||||||
|
"total_price": 18900
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Semuanya `null` atau absen untuk produk biasa, jadi cabangkan tampilan pada `weight`:
|
||||||
|
|
||||||
|
| Kondisi | Tampilkan |
|
||||||
|
|---|---|
|
||||||
|
| `weight == null` | `2 × Rp 25.000` |
|
||||||
|
| `weight != null` | `4,2 ons × Rp 4.500` |
|
||||||
|
|
||||||
|
**Jangan menampilkan `quantity` untuk baris berbobot** — nilainya selalu 1 dan akan
|
||||||
|
terbaca seperti "satu ons". Gunakan `weight` dengan `unit_abbreviation`, dan pakai koma
|
||||||
|
desimal sesuai format Indonesia.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Void, refund, split bill
|
||||||
|
|
||||||
|
Baris berbobot bersifat **utuh**: dibatalkan seluruhnya atau tidak sama sekali. Karena
|
||||||
|
`quantity`-nya 1, semua endpoint cukup dikirimi `1`, dan backend menghitung nilai
|
||||||
|
rupiahnya dari `weight`.
|
||||||
|
|
||||||
|
| Aksi | Endpoint | Field untuk baris berbobot |
|
||||||
|
|---|---|---|
|
||||||
|
| Void per item | `POST /orders/void` | `items[].quantity: 1` |
|
||||||
|
| Refund per item | `POST /orders/:id/refund` | `order_items[].refund_quantity: 1` (atau kosongkan) |
|
||||||
|
| Split bill per item | `POST /orders/split-bill` | `items[].quantity: 1` = bayar baris itu penuh |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"order_id": "…",
|
||||||
|
"reason": "Salah timbang",
|
||||||
|
"type": "ITEM",
|
||||||
|
"items": [
|
||||||
|
{ "order_item_id": "<baris 4,2 ons>", "quantity": 1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Untuk split bill, baris berbobot hanya bisa berstatus belum dibayar atau lunas — tidak
|
||||||
|
ada nilai di antaranya. Sembunyikan stepper jumlah pada baris berbobot, ganti dengan
|
||||||
|
tombol pilih baris.
|
||||||
|
|
||||||
|
**Batasan yang disengaja.** Mengembalikan *sebagian berat* — 1 ons dari baris 4,2 ons —
|
||||||
|
tidak didukung. Koreksi salah timbang ditangani dengan void baris itu lalu input ulang,
|
||||||
|
sehingga jejak auditnya tetap jujur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Referensi error
|
||||||
|
|
||||||
|
Semua error mengikuti amplop standar. Pesan validasi baru muncul dengan kode `900`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"data": null,
|
||||||
|
"errors": [
|
||||||
|
{ "code": "900", "entity": "ORDER",
|
||||||
|
"cause": "product Ikan Tude is sold by weight and requires a weight" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Pesan (`cause`) | Penyebab | Perbaikan di klien |
|
||||||
|
|---|---|---|
|
||||||
|
| `… is sold by weight and requires a weight` | Produk `sell_by: "weight"` dikirim tanpa `weight` | Wajibkan input timbangan sebelum item masuk keranjang |
|
||||||
|
| `… is not sold by weight and must not carry a weight` | `weight` dikirim untuk produk satuan | Kirim `weight` hanya bila `sell_by == "weight"` |
|
||||||
|
| `weight for … must be greater than 0` | Berat nol, negatif, atau membulat ke nol | Validasi minimal `0.001` di keypad |
|
||||||
|
| `quantity for … must be at least 1` | Produk satuan dengan `quantity` ≤ 0 | Perilaku lama, tidak berubah |
|
||||||
|
|
||||||
|
Pesan menyebut **nama produk**, sehingga bisa ditampilkan apa adanya ke kasir.
|
||||||
|
|
||||||
|
### Setup produk (Backoffice)
|
||||||
|
|
||||||
|
| Pesan (`cause`) | Penyebab | Perbaikan di klien |
|
||||||
|
|---|---|---|
|
||||||
|
| `unit_id is required when sell_by is 'weight'` | Produk timbangan dibuat tanpa satuan | Wajibkan pemilih satuan saat Timbangan dipilih |
|
||||||
|
| `sell_by must be either 'unit' or 'weight'` | Nilai `sell_by` di luar dua itu | Kirim persis `"unit"` atau `"weight"` |
|
||||||
|
| `product '…' is sold by weight and requires a unit_id` | Update membuat produk jadi timbangan tanpa satuan | Kirim `unit_id` bersama perubahan `sell_by` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Kompatibilitas mundur
|
||||||
|
|
||||||
|
- Semua produk lama otomatis `sell_by: "unit"`. Perilakunya identik dengan sebelumnya.
|
||||||
|
- `weight` opsional di request. Klien yang tidak mengenalnya tetap berfungsi penuh untuk
|
||||||
|
produk satuan.
|
||||||
|
- Field baru di response semuanya `omitempty` — tidak muncul untuk baris biasa, jadi
|
||||||
|
parser lama tidak terganggu.
|
||||||
|
- `quantity` tetap **integer** di seluruh API. Tidak ada field yang berubah tipe.
|
||||||
|
|
||||||
|
Yang tidak berfungsi di klien lama hanyalah menjual produk timbangan itu sendiri —
|
||||||
|
permintaannya ditolak dengan pesan jelas, bukan gagal diam-diam.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Batasan yang diketahui
|
||||||
|
|
||||||
|
- **Pembulatan uang ke 2 desimal.** `4,237 ons × Rp 4.500` tersimpan `Rp 19.066,50`,
|
||||||
|
bukan dibulatkan ke rupiah utuh. Bila kasir harus menerima rupiah penuh, ini perlu
|
||||||
|
diputuskan dan diubah di backend lebih dulu (`RoundMoney`, satu tempat).
|
||||||
|
- **Stok belum otomatis berkurang** saat penjualan — untuk produk timbangan maupun
|
||||||
|
produk biasa. Pengurangan stok belum tersambung di backend, jadi jangan menampilkan
|
||||||
|
sisa stok yang mengandaikan itu berjalan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Checklist per tim
|
||||||
|
|
||||||
|
**Backoffice Website**
|
||||||
|
|
||||||
|
- [ ] CRUD satuan tersedia di menu master data
|
||||||
|
- [ ] Form produk punya pilihan cara jual: Satuan / Timbangan
|
||||||
|
- [ ] Pemilih satuan menjadi wajib saat Timbangan dipilih
|
||||||
|
- [ ] Label harga ikut satuan — "Harga per ons"
|
||||||
|
- [ ] `sell_by` dikunci untuk produk yang sudah bertransaksi
|
||||||
|
- [ ] Daftar produk menandai mana yang dijual per timbangan
|
||||||
|
- [ ] Laporan harian menampilkan kolom Berat
|
||||||
|
|
||||||
|
**POS Mobile**
|
||||||
|
|
||||||
|
- [ ] Menu membaca `sell_by` tiap produk
|
||||||
|
- [ ] Papan angka desimal untuk produk timbangan
|
||||||
|
- [ ] Kirim `quantity: 1` + `weight`
|
||||||
|
- [ ] Dua penimbangan menjadi dua baris, tidak digabung
|
||||||
|
- [ ] Keranjang & struk menampilkan `4,2 ons × Rp 4.500`
|
||||||
|
- [ ] Void & refund baris berbobot bersifat utuh
|
||||||
|
- [ ] Split bill: pilih baris, bukan stepper jumlah
|
||||||
|
- [ ] Pesan error validasi ditampilkan ke kasir
|
||||||
@@ -33,16 +33,16 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
state.copyWith(
|
state.copyWith(
|
||||||
failureOption: optionOf(f),
|
failureOption: optionOf(f),
|
||||||
status: token
|
status: token
|
||||||
? AuthStatus.authenticated()
|
? const AuthStatus.authenticated()
|
||||||
: AuthStatus.unauthenticated(),
|
: const AuthStatus.unauthenticated(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(user) => emit(
|
(user) => emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
user: user,
|
user: user,
|
||||||
status: token
|
status: token
|
||||||
? AuthStatus.authenticated()
|
? const AuthStatus.authenticated()
|
||||||
: AuthStatus.unauthenticated(),
|
: const AuthStatus.unauthenticated(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../../common/service/device_info_service.dart';
|
||||||
|
import '../../../common/service/unit_registry.dart';
|
||||||
|
import '../../../common/service/fcm_service.dart';
|
||||||
import '../../../domain/auth/auth.dart';
|
import '../../../domain/auth/auth.dart';
|
||||||
|
|
||||||
part 'login_form_event.dart';
|
part 'login_form_event.dart';
|
||||||
@@ -14,7 +17,16 @@ part 'login_form_bloc.freezed.dart';
|
|||||||
@injectable
|
@injectable
|
||||||
class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
|
class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
|
||||||
final IAuthRepository _authRepository;
|
final IAuthRepository _authRepository;
|
||||||
LoginFormBloc(this._authRepository) : super(LoginFormState.initial()) {
|
final FcmService _fcmService;
|
||||||
|
final DeviceInfoService _deviceInfoService;
|
||||||
|
final UnitRegistry _unitRegistry;
|
||||||
|
|
||||||
|
LoginFormBloc(
|
||||||
|
this._authRepository,
|
||||||
|
this._fcmService,
|
||||||
|
this._deviceInfoService,
|
||||||
|
this._unitRegistry,
|
||||||
|
) : super(LoginFormState.initial()) {
|
||||||
on<LoginFormEvent>(_onLoginFormEvent);
|
on<LoginFormEvent>(_onLoginFormEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,11 +55,22 @@ class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (emailValid && passwordValid) {
|
if (emailValid && passwordValid) {
|
||||||
|
final fcmToken = await _fcmService.getToken() ?? '';
|
||||||
|
final devicePayload = await _deviceInfoService.getDevicePayload();
|
||||||
|
|
||||||
failureOrLogin = await _authRepository.login(
|
failureOrLogin = await _authRepository.login(
|
||||||
email: state.email,
|
email: state.email,
|
||||||
password: state.password,
|
password: state.password,
|
||||||
|
fcmToken: fcmToken,
|
||||||
|
devicePayload: devicePayload,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Master satuan diambil sekali per sesi supaya keranjang bisa
|
||||||
|
// menulis "4,2 ons" tanpa menunggu request saat kasir menimbang.
|
||||||
|
if (failureOrLogin.isRight()) {
|
||||||
|
await _unitRegistry.load(forceRefresh: true);
|
||||||
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../../domain/auth/auth.dart';
|
||||||
|
|
||||||
|
part 'logout_event.dart';
|
||||||
|
part 'logout_state.dart';
|
||||||
|
part 'logout_bloc.freezed.dart';
|
||||||
|
|
||||||
|
@injectable
|
||||||
|
class LogoutBloc extends Bloc<LogoutEvent, LogoutState> {
|
||||||
|
final IAuthRepository _authRepository;
|
||||||
|
LogoutBloc(this._authRepository) : super(LogoutState.initial()) {
|
||||||
|
on<LogoutEvent>(_onLogoutEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onLogoutEvent(LogoutEvent event, Emitter<LogoutState> emit) {
|
||||||
|
return event.map(
|
||||||
|
logout: (e) async {
|
||||||
|
Either<AuthFailure, Unit>? failureOrSuccess;
|
||||||
|
emit(
|
||||||
|
state.copyWith(isLoggingOut: true, logoutFailureOrSuccess: none()),
|
||||||
|
);
|
||||||
|
|
||||||
|
failureOrSuccess = await _authRepository.logout();
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoggingOut: false,
|
||||||
|
logoutFailureOrSuccess: optionOf(failureOrSuccess),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'logout_bloc.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$LogoutEvent {
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({required TResult Function() logout}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({TResult? Function()? logout}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? logout,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Logout value) logout,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Logout value)? logout,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Logout value)? logout,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $LogoutEventCopyWith<$Res> {
|
||||||
|
factory $LogoutEventCopyWith(
|
||||||
|
LogoutEvent value,
|
||||||
|
$Res Function(LogoutEvent) then,
|
||||||
|
) = _$LogoutEventCopyWithImpl<$Res, LogoutEvent>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$LogoutEventCopyWithImpl<$Res, $Val extends LogoutEvent>
|
||||||
|
implements $LogoutEventCopyWith<$Res> {
|
||||||
|
_$LogoutEventCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of LogoutEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$LogoutImplCopyWith<$Res> {
|
||||||
|
factory _$$LogoutImplCopyWith(
|
||||||
|
_$LogoutImpl value,
|
||||||
|
$Res Function(_$LogoutImpl) then,
|
||||||
|
) = __$$LogoutImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$LogoutImplCopyWithImpl<$Res>
|
||||||
|
extends _$LogoutEventCopyWithImpl<$Res, _$LogoutImpl>
|
||||||
|
implements _$$LogoutImplCopyWith<$Res> {
|
||||||
|
__$$LogoutImplCopyWithImpl(
|
||||||
|
_$LogoutImpl _value,
|
||||||
|
$Res Function(_$LogoutImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of LogoutEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$LogoutImpl implements _Logout {
|
||||||
|
const _$LogoutImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LogoutEvent.logout()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType && other is _$LogoutImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({required TResult Function() logout}) {
|
||||||
|
return logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({TResult? Function()? logout}) {
|
||||||
|
return logout?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? logout,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (logout != null) {
|
||||||
|
return logout();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Logout value) logout,
|
||||||
|
}) {
|
||||||
|
return logout(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Logout value)? logout,
|
||||||
|
}) {
|
||||||
|
return logout?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Logout value)? logout,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (logout != null) {
|
||||||
|
return logout(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Logout implements LogoutEvent {
|
||||||
|
const factory _Logout() = _$LogoutImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$LogoutState {
|
||||||
|
Option<Either<AuthFailure, Unit>> get logoutFailureOrSuccess =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
bool get isLoggingOut => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of LogoutState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$LogoutStateCopyWith<LogoutState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $LogoutStateCopyWith<$Res> {
|
||||||
|
factory $LogoutStateCopyWith(
|
||||||
|
LogoutState value,
|
||||||
|
$Res Function(LogoutState) then,
|
||||||
|
) = _$LogoutStateCopyWithImpl<$Res, LogoutState>;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
Option<Either<AuthFailure, Unit>> logoutFailureOrSuccess,
|
||||||
|
bool isLoggingOut,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$LogoutStateCopyWithImpl<$Res, $Val extends LogoutState>
|
||||||
|
implements $LogoutStateCopyWith<$Res> {
|
||||||
|
_$LogoutStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of LogoutState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? logoutFailureOrSuccess = null,
|
||||||
|
Object? isLoggingOut = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_value.copyWith(
|
||||||
|
logoutFailureOrSuccess: null == logoutFailureOrSuccess
|
||||||
|
? _value.logoutFailureOrSuccess
|
||||||
|
: logoutFailureOrSuccess // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<Either<AuthFailure, Unit>>,
|
||||||
|
isLoggingOut: null == isLoggingOut
|
||||||
|
? _value.isLoggingOut
|
||||||
|
: isLoggingOut // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
)
|
||||||
|
as $Val,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$LogoutStateImplCopyWith<$Res>
|
||||||
|
implements $LogoutStateCopyWith<$Res> {
|
||||||
|
factory _$$LogoutStateImplCopyWith(
|
||||||
|
_$LogoutStateImpl value,
|
||||||
|
$Res Function(_$LogoutStateImpl) then,
|
||||||
|
) = __$$LogoutStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
Option<Either<AuthFailure, Unit>> logoutFailureOrSuccess,
|
||||||
|
bool isLoggingOut,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$LogoutStateImplCopyWithImpl<$Res>
|
||||||
|
extends _$LogoutStateCopyWithImpl<$Res, _$LogoutStateImpl>
|
||||||
|
implements _$$LogoutStateImplCopyWith<$Res> {
|
||||||
|
__$$LogoutStateImplCopyWithImpl(
|
||||||
|
_$LogoutStateImpl _value,
|
||||||
|
$Res Function(_$LogoutStateImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of LogoutState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? logoutFailureOrSuccess = null,
|
||||||
|
Object? isLoggingOut = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_$LogoutStateImpl(
|
||||||
|
logoutFailureOrSuccess: null == logoutFailureOrSuccess
|
||||||
|
? _value.logoutFailureOrSuccess
|
||||||
|
: logoutFailureOrSuccess // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<Either<AuthFailure, Unit>>,
|
||||||
|
isLoggingOut: null == isLoggingOut
|
||||||
|
? _value.isLoggingOut
|
||||||
|
: isLoggingOut // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$LogoutStateImpl implements _LogoutState {
|
||||||
|
_$LogoutStateImpl({
|
||||||
|
required this.logoutFailureOrSuccess,
|
||||||
|
this.isLoggingOut = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Option<Either<AuthFailure, Unit>> logoutFailureOrSuccess;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isLoggingOut;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LogoutState(logoutFailureOrSuccess: $logoutFailureOrSuccess, isLoggingOut: $isLoggingOut)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$LogoutStateImpl &&
|
||||||
|
(identical(other.logoutFailureOrSuccess, logoutFailureOrSuccess) ||
|
||||||
|
other.logoutFailureOrSuccess == logoutFailureOrSuccess) &&
|
||||||
|
(identical(other.isLoggingOut, isLoggingOut) ||
|
||||||
|
other.isLoggingOut == isLoggingOut));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, logoutFailureOrSuccess, isLoggingOut);
|
||||||
|
|
||||||
|
/// Create a copy of LogoutState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$LogoutStateImplCopyWith<_$LogoutStateImpl> get copyWith =>
|
||||||
|
__$$LogoutStateImplCopyWithImpl<_$LogoutStateImpl>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _LogoutState implements LogoutState {
|
||||||
|
factory _LogoutState({
|
||||||
|
required final Option<Either<AuthFailure, Unit>> logoutFailureOrSuccess,
|
||||||
|
final bool isLoggingOut,
|
||||||
|
}) = _$LogoutStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Option<Either<AuthFailure, Unit>> get logoutFailureOrSuccess;
|
||||||
|
@override
|
||||||
|
bool get isLoggingOut;
|
||||||
|
|
||||||
|
/// Create a copy of LogoutState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$LogoutStateImplCopyWith<_$LogoutStateImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
part of 'logout_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class LogoutEvent with _$LogoutEvent {
|
||||||
|
const factory LogoutEvent.logout() = _Logout;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
part of 'logout_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class LogoutState with _$LogoutState {
|
||||||
|
factory LogoutState({
|
||||||
|
required Option<Either<AuthFailure, Unit>> logoutFailureOrSuccess,
|
||||||
|
@Default(false) bool isLoggingOut,
|
||||||
|
}) = _LogoutState;
|
||||||
|
|
||||||
|
factory LogoutState.initial() => LogoutState(logoutFailureOrSuccess: none());
|
||||||
|
}
|
||||||
@@ -175,7 +175,7 @@ class CategoryLoaderBloc
|
|||||||
_searchDebounce?.cancel();
|
_searchDebounce?.cancel();
|
||||||
|
|
||||||
// Debounce search for better UX
|
// Debounce search for better UX
|
||||||
_searchDebounce = Timer(Duration(milliseconds: 300), () async {
|
_searchDebounce = Timer(const Duration(milliseconds: 300), () async {
|
||||||
emit(state.copyWith(isLoadingMore: true));
|
emit(state.copyWith(isLoadingMore: true));
|
||||||
_isLoadingMore = false;
|
_isLoadingMore = false;
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ class CategoryLoaderBloc
|
|||||||
);
|
);
|
||||||
|
|
||||||
// After sync error, try to load local data
|
// After sync error, try to load local data
|
||||||
Timer(Duration(seconds: 2), () {
|
Timer(const Duration(seconds: 2), () {
|
||||||
add(const CategoryLoaderEvent.getCategories());
|
add(const CategoryLoaderEvent.getCategories());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -251,7 +251,7 @@ class CategoryLoaderBloc
|
|||||||
);
|
);
|
||||||
|
|
||||||
// After successful sync, load the updated data
|
// After successful sync, load the updated data
|
||||||
Timer(Duration(seconds: 1), () {
|
Timer(const Duration(seconds: 1), () {
|
||||||
add(const CategoryLoaderEvent.getCategories());
|
add(const CategoryLoaderEvent.getCategories());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:developer';
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart' hide Order;
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../common/types/order_type.dart';
|
import '../../../common/types/order_type.dart';
|
||||||
import '../../../domain/delivery/delivery.dart';
|
import '../../../domain/delivery/delivery.dart';
|
||||||
@@ -19,6 +20,33 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
CheckoutFormBloc() : super(CheckoutFormState.initial()) {
|
CheckoutFormBloc() : super(CheckoutFormState.initial()) {
|
||||||
on<CheckoutFormEvent>(_onCheckoutFormEvent);
|
on<CheckoutFormEvent>(_onCheckoutFormEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Baris berbobot dihitung `weight × harga`, baris cacah `quantity × harga`.
|
||||||
|
/// Satu tempat saja supaya total keranjang, subtotal, dan ringkasan tidak
|
||||||
|
/// pernah berbeda hitungan.
|
||||||
|
CheckoutFormState _recalculate(
|
||||||
|
CheckoutFormState state,
|
||||||
|
List<ProductQuantity> items,
|
||||||
|
) {
|
||||||
|
final totalQuantity = items.fold<int>(
|
||||||
|
0,
|
||||||
|
// Baris berbobot selalu satu penimbangan, jadi dihitung satu.
|
||||||
|
(sum, item) => sum + (item.isWeighted ? 1 : item.quantity),
|
||||||
|
);
|
||||||
|
|
||||||
|
final totalPrice = items.fold<double>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + item.lineTotal,
|
||||||
|
);
|
||||||
|
|
||||||
|
return state.copyWith(
|
||||||
|
items: items,
|
||||||
|
totalQuantity: totalQuantity,
|
||||||
|
totalPrice: totalPrice.toInt(),
|
||||||
|
isLoading: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onCheckoutFormEvent(
|
Future<void> _onCheckoutFormEvent(
|
||||||
CheckoutFormEvent event,
|
CheckoutFormEvent event,
|
||||||
Emitter<CheckoutFormState> emit,
|
Emitter<CheckoutFormState> emit,
|
||||||
@@ -26,12 +54,21 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
return event.map(
|
return event.map(
|
||||||
addItem: (e) async {
|
addItem: (e) async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
|
// Produk timbangan tidak punya stepper — masuk lewat addWeightedItem
|
||||||
|
// setelah kasir memasukkan angka timbangan.
|
||||||
|
if (e.product.isSoldByWeight) {
|
||||||
|
log('⚖️ ${e.product.name} dijual per timbangan, butuh berat');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
emit(currentState.copyWith(isLoading: true));
|
emit(currentState.copyWith(isLoading: true));
|
||||||
|
|
||||||
List<ProductQuantity> items = [...currentState.items];
|
List<ProductQuantity> items = [...currentState.items];
|
||||||
|
|
||||||
final index = items.indexWhere(
|
final index = items.indexWhere(
|
||||||
(element) =>
|
(element) =>
|
||||||
|
!element.isWeighted &&
|
||||||
element.product.id == e.product.id &&
|
element.product.id == e.product.id &&
|
||||||
element.variant?.id == e.variant?.id,
|
element.variant?.id == e.variant?.id,
|
||||||
);
|
);
|
||||||
@@ -49,35 +86,47 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
quantity: 1,
|
quantity: 1,
|
||||||
variant: e.variant,
|
variant: e.variant,
|
||||||
notes: '',
|
notes: '',
|
||||||
|
lineId: const Uuid().v4(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log('🛒 Items updated: ${items.length} items total ${items.toList()}');
|
log('🛒 Items updated: ${items.length} items total ${items.toList()}');
|
||||||
|
|
||||||
final totalQuantity = items.fold<int>(
|
emit(_recalculate(currentState, items));
|
||||||
0,
|
},
|
||||||
(sum, item) => sum + item.quantity,
|
addWeightedItem: (e) async {
|
||||||
);
|
final currentState = state;
|
||||||
|
emit(currentState.copyWith(isLoading: true));
|
||||||
|
|
||||||
final totalPrice = state.items.isEmpty
|
// Sengaja tidak digabung dengan baris lain: dua penimbangan adalah dua
|
||||||
? 0.0
|
// ikan berbeda, masing-masing bisa di-void atau dibayar terpisah.
|
||||||
: state.items
|
final items = [
|
||||||
.map(
|
...currentState.items,
|
||||||
(e) =>
|
ProductQuantity(
|
||||||
(e.product.price * e.quantity) +
|
product: e.product,
|
||||||
(e.variant?.priceModifier ?? 0),
|
variant: e.variant,
|
||||||
)
|
quantity: 1,
|
||||||
.reduce((value, element) => value + element);
|
notes: '',
|
||||||
|
lineId: const Uuid().v4(),
|
||||||
emit(
|
weight: e.weight,
|
||||||
currentState.copyWith(
|
|
||||||
items: items,
|
|
||||||
totalQuantity: totalQuantity,
|
|
||||||
totalPrice: totalPrice.toInt(),
|
|
||||||
isLoading: false,
|
|
||||||
),
|
),
|
||||||
);
|
];
|
||||||
|
|
||||||
|
log('⚖️ Penimbangan baru: ${e.product.name} ${e.weight}');
|
||||||
|
|
||||||
|
emit(_recalculate(currentState, items));
|
||||||
|
},
|
||||||
|
updateItemWeight: (e) async {
|
||||||
|
final currentState = state;
|
||||||
|
final items = [...currentState.items];
|
||||||
|
|
||||||
|
final index = items.indexWhere((item) => item.lineId == e.lineId);
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
items[index] = items[index].copyWith(weight: e.weight);
|
||||||
|
|
||||||
|
emit(_recalculate(currentState, items));
|
||||||
},
|
},
|
||||||
removeItem: (e) async {
|
removeItem: (e) async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
@@ -86,6 +135,7 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
|
|
||||||
final index = items.indexWhere(
|
final index = items.indexWhere(
|
||||||
(element) =>
|
(element) =>
|
||||||
|
!element.isWeighted &&
|
||||||
element.product.id == e.product.id &&
|
element.product.id == e.product.id &&
|
||||||
element.variant?.id == e.variant?.id,
|
element.variant?.id == e.variant?.id,
|
||||||
);
|
);
|
||||||
@@ -103,32 +153,18 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final totalQuantity = items.fold<int>(
|
log('🗑️ Item removed. Total items: ${items.length}');
|
||||||
0,
|
|
||||||
(sum, item) => sum + item.quantity,
|
|
||||||
);
|
|
||||||
final totalPrice = items.fold<int>(
|
|
||||||
0,
|
|
||||||
(sum, item) =>
|
|
||||||
sum +
|
|
||||||
(item.quantity *
|
|
||||||
(item.variant?.priceModifier.toInt() ??
|
|
||||||
item.product.price.toInt())),
|
|
||||||
);
|
|
||||||
|
|
||||||
log(
|
emit(_recalculate(currentState, items));
|
||||||
'🗑️ Item removed. Total items: ${items.length}, totalQuantity: $totalQuantity, totalPrice: $totalPrice',
|
},
|
||||||
);
|
removeLine: (e) async {
|
||||||
|
final currentState = state;
|
||||||
|
final items = [...currentState.items]
|
||||||
|
..removeWhere((item) => item.lineId == e.lineId);
|
||||||
|
|
||||||
// Emit state baru
|
log('🗑️ Baris dihapus. Sisa ${items.length} baris');
|
||||||
emit(
|
|
||||||
currentState.copyWith(
|
emit(_recalculate(currentState, items));
|
||||||
items: items,
|
|
||||||
totalQuantity: totalQuantity,
|
|
||||||
totalPrice: totalPrice,
|
|
||||||
isLoading: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
started: (e) async {
|
started: (e) async {
|
||||||
emit(CheckoutFormState.initial().copyWith(isLoading: true));
|
emit(CheckoutFormState.initial().copyWith(isLoading: true));
|
||||||
@@ -153,14 +189,13 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateItemNotes: (e) async {
|
updateLineNotes: (e) async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
// Clone list items agar tidak mutasi langsung
|
// Catatan diikat ke baris, bukan ke produk: dua penimbangan produk yang
|
||||||
|
// sama bisa punya catatan berbeda.
|
||||||
final items = [...currentState.items];
|
final items = [...currentState.items];
|
||||||
final index = items.indexWhere(
|
final index = items.indexWhere((item) => item.lineId == e.lineId);
|
||||||
(element) => element.product.id == e.product.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
items[index] = items[index].copyWith(notes: e.notes);
|
items[index] = items[index].copyWith(notes: e.notes);
|
||||||
@@ -180,6 +215,9 @@ class CheckoutFormBloc extends Bloc<CheckoutFormEvent, CheckoutFormState> {
|
|||||||
orderAddedItems: (e) async {
|
orderAddedItems: (e) async {
|
||||||
emit(state.copyWith(orderAdded: e.order));
|
emit(state.copyWith(orderAdded: e.order));
|
||||||
},
|
},
|
||||||
|
customerNameChanged: (e) async {
|
||||||
|
emit(state.copyWith(customerName: e.name));
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,15 +10,32 @@ class CheckoutFormEvent with _$CheckoutFormEvent {
|
|||||||
ProductVariant? variant,
|
ProductVariant? variant,
|
||||||
) = _AddItem;
|
) = _AddItem;
|
||||||
|
|
||||||
|
/// Satu penimbangan = satu baris baru. Dipanggil setiap kali kasir selesai
|
||||||
|
/// memasukkan angka timbangan, meski produknya sama dengan baris lain.
|
||||||
|
const factory CheckoutFormEvent.addWeightedItem(
|
||||||
|
Product product,
|
||||||
|
ProductVariant? variant,
|
||||||
|
double weight,
|
||||||
|
) = _AddWeightedItem;
|
||||||
|
|
||||||
|
/// Koreksi angka timbangan pada satu baris tertentu.
|
||||||
|
const factory CheckoutFormEvent.updateItemWeight(
|
||||||
|
String lineId,
|
||||||
|
double weight,
|
||||||
|
) = _UpdateItemWeight;
|
||||||
|
|
||||||
const factory CheckoutFormEvent.removeItem(
|
const factory CheckoutFormEvent.removeItem(
|
||||||
Product product,
|
Product product,
|
||||||
ProductVariant? variant,
|
ProductVariant? variant,
|
||||||
) = _RemoveItem;
|
) = _RemoveItem;
|
||||||
|
|
||||||
const factory CheckoutFormEvent.updateItemNotes(
|
/// Hapus satu baris utuh — satu-satunya cara membatalkan baris berbobot.
|
||||||
Product product,
|
const factory CheckoutFormEvent.removeLine(String lineId) = _RemoveLine;
|
||||||
|
|
||||||
|
const factory CheckoutFormEvent.updateLineNotes(
|
||||||
|
String lineId,
|
||||||
String notes,
|
String notes,
|
||||||
) = _UpdateItemNotes;
|
) = _UpdateLineNotes;
|
||||||
|
|
||||||
const factory CheckoutFormEvent.updateOrderType(OrderType orderType) =
|
const factory CheckoutFormEvent.updateOrderType(OrderType orderType) =
|
||||||
_UpdateOrderType;
|
_UpdateOrderType;
|
||||||
@@ -30,4 +47,6 @@ class CheckoutFormEvent with _$CheckoutFormEvent {
|
|||||||
|
|
||||||
const factory CheckoutFormEvent.orderAddedItems(Order? order) =
|
const factory CheckoutFormEvent.orderAddedItems(Order? order) =
|
||||||
_OrderAddedItems;
|
_OrderAddedItems;
|
||||||
|
const factory CheckoutFormEvent.customerNameChanged(String name) =
|
||||||
|
_CustomerNameChanged;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class CheckoutFormState with _$CheckoutFormState {
|
|||||||
required int totalPrice,
|
required int totalPrice,
|
||||||
required String draftName,
|
required String draftName,
|
||||||
required OrderType orderType,
|
required OrderType orderType,
|
||||||
|
required String customerName,
|
||||||
Delivery? delivery,
|
Delivery? delivery,
|
||||||
Table? table,
|
Table? table,
|
||||||
@Default(false) bool isLoading,
|
@Default(false) bool isLoading,
|
||||||
@@ -27,6 +28,7 @@ class CheckoutFormState with _$CheckoutFormState {
|
|||||||
totalQuantity: 0,
|
totalQuantity: 0,
|
||||||
totalPrice: 0,
|
totalPrice: 0,
|
||||||
draftName: '',
|
draftName: '',
|
||||||
|
customerName: '',
|
||||||
orderType: OrderType.dineIn,
|
orderType: OrderType.dineIn,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:dartz/dartz.dart' hide Order;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart' hide Order;
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../common/types/order_type.dart';
|
import '../../../common/types/order_type.dart';
|
||||||
import '../../../domain/customer/customer.dart';
|
import '../../../domain/customer/customer.dart';
|
||||||
@@ -59,15 +60,7 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
|
|||||||
notes: '',
|
notes: '',
|
||||||
customerName: state.customerName ?? "",
|
customerName: state.customerName ?? "",
|
||||||
orderItems: e.items
|
orderItems: e.items
|
||||||
.map(
|
.map(OrderItemRequest.fromProductQuantity)
|
||||||
(item) => OrderItemRequest(
|
|
||||||
productId: item.product.id,
|
|
||||||
productVariantId: item.variant?.id ?? "",
|
|
||||||
quantity: item.quantity,
|
|
||||||
unitPrice: item.product.price.toInt(),
|
|
||||||
notes: item.notes,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -99,15 +92,7 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
|
|||||||
notes: '',
|
notes: '',
|
||||||
customerName: state.customerName ?? "",
|
customerName: state.customerName ?? "",
|
||||||
orderItems: e.items
|
orderItems: e.items
|
||||||
.map(
|
.map(OrderItemRequest.fromProductQuantity)
|
||||||
(item) => OrderItemRequest(
|
|
||||||
productId: item.product.id,
|
|
||||||
productVariantId: item.variant?.id ?? "",
|
|
||||||
quantity: item.quantity,
|
|
||||||
unitPrice: item.product.price.toInt(),
|
|
||||||
notes: item.notes,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -123,34 +108,35 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
|
|||||||
addItemOrder: (e) async {
|
addItemOrder: (e) async {
|
||||||
Either<OrderFailure, Order> failureOrAddItemOrder;
|
Either<OrderFailure, Order> failureOrAddItemOrder;
|
||||||
|
|
||||||
|
// Generate new idempotency key saat user intent (tap Add Items)
|
||||||
|
// Kalau sedang retry, pakai key yang sama
|
||||||
|
final idempotencyKey = state.addItemIdempotencyKey ?? const Uuid().v4();
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isAddingItemOrder: true,
|
isAddingItemOrder: true,
|
||||||
failureOrAddItemOrder: none(),
|
failureOrAddItemOrder: none(),
|
||||||
|
addItemIdempotencyKey: idempotencyKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final request = e.items
|
final request = e.items
|
||||||
.map(
|
.map(AddItemOrderRequest.fromProductQuantity)
|
||||||
(item) => AddItemOrderRequest(
|
|
||||||
productId: item.product.id,
|
|
||||||
productVariantId: item.variant?.id ?? '',
|
|
||||||
quantity: item.quantity,
|
|
||||||
unitPrice: item.product.price.toInt(),
|
|
||||||
notes: item.notes,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
failureOrAddItemOrder = await _repository.addItemOrder(
|
failureOrAddItemOrder = await _repository.addItemOrder(
|
||||||
id: e.orderId,
|
id: e.orderId,
|
||||||
request: request,
|
request: request,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isAddingItemOrder: false,
|
isAddingItemOrder: false,
|
||||||
failureOrAddItemOrder: optionOf(failureOrAddItemOrder),
|
failureOrAddItemOrder: optionOf(failureOrAddItemOrder),
|
||||||
|
// Clear key on success, keep on failure for retry
|
||||||
|
addItemIdempotencyKey:
|
||||||
|
failureOrAddItemOrder.isRight() ? null : idempotencyKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1584,6 +1584,11 @@ mixin _$OrderFormState {
|
|||||||
bool get isCreatingWithPayment => throw _privateConstructorUsedError;
|
bool get isCreatingWithPayment => throw _privateConstructorUsedError;
|
||||||
bool get isAddingItemOrder => throw _privateConstructorUsedError;
|
bool get isAddingItemOrder => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Idempotency key untuk add item order.
|
||||||
|
/// Di-generate saat user tap "Add Items", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
String? get addItemIdempotencyKey => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of OrderFormState
|
/// Create a copy of OrderFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -1608,6 +1613,7 @@ abstract class $OrderFormStateCopyWith<$Res> {
|
|||||||
bool isCreating,
|
bool isCreating,
|
||||||
bool isCreatingWithPayment,
|
bool isCreatingWithPayment,
|
||||||
bool isAddingItemOrder,
|
bool isAddingItemOrder,
|
||||||
|
String? addItemIdempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
$PaymentMethodCopyWith<$Res>? get paymentMethod;
|
$PaymentMethodCopyWith<$Res>? get paymentMethod;
|
||||||
@@ -1638,6 +1644,7 @@ class _$OrderFormStateCopyWithImpl<$Res, $Val extends OrderFormState>
|
|||||||
Object? isCreating = null,
|
Object? isCreating = null,
|
||||||
Object? isCreatingWithPayment = null,
|
Object? isCreatingWithPayment = null,
|
||||||
Object? isAddingItemOrder = null,
|
Object? isAddingItemOrder = null,
|
||||||
|
Object? addItemIdempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -1678,6 +1685,10 @@ class _$OrderFormStateCopyWithImpl<$Res, $Val extends OrderFormState>
|
|||||||
? _value.isAddingItemOrder
|
? _value.isAddingItemOrder
|
||||||
: isAddingItemOrder // ignore: cast_nullable_to_non_nullable
|
: isAddingItemOrder // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
addItemIdempotencyKey: freezed == addItemIdempotencyKey
|
||||||
|
? _value.addItemIdempotencyKey
|
||||||
|
: addItemIdempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -1731,6 +1742,7 @@ abstract class _$$OrderFormStateImplCopyWith<$Res>
|
|||||||
bool isCreating,
|
bool isCreating,
|
||||||
bool isCreatingWithPayment,
|
bool isCreatingWithPayment,
|
||||||
bool isAddingItemOrder,
|
bool isAddingItemOrder,
|
||||||
|
String? addItemIdempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1762,6 +1774,7 @@ class __$$OrderFormStateImplCopyWithImpl<$Res>
|
|||||||
Object? isCreating = null,
|
Object? isCreating = null,
|
||||||
Object? isCreatingWithPayment = null,
|
Object? isCreatingWithPayment = null,
|
||||||
Object? isAddingItemOrder = null,
|
Object? isAddingItemOrder = null,
|
||||||
|
Object? addItemIdempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$OrderFormStateImpl(
|
_$OrderFormStateImpl(
|
||||||
@@ -1801,6 +1814,10 @@ class __$$OrderFormStateImplCopyWithImpl<$Res>
|
|||||||
? _value.isAddingItemOrder
|
? _value.isAddingItemOrder
|
||||||
: isAddingItemOrder // ignore: cast_nullable_to_non_nullable
|
: isAddingItemOrder // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
addItemIdempotencyKey: freezed == addItemIdempotencyKey
|
||||||
|
? _value.addItemIdempotencyKey
|
||||||
|
: addItemIdempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1821,6 +1838,7 @@ class _$OrderFormStateImpl
|
|||||||
this.isCreating = false,
|
this.isCreating = false,
|
||||||
this.isCreatingWithPayment = false,
|
this.isCreatingWithPayment = false,
|
||||||
this.isAddingItemOrder = false,
|
this.isAddingItemOrder = false,
|
||||||
|
this.addItemIdempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1845,9 +1863,15 @@ class _$OrderFormStateImpl
|
|||||||
@JsonKey()
|
@JsonKey()
|
||||||
final bool isAddingItemOrder;
|
final bool isAddingItemOrder;
|
||||||
|
|
||||||
|
/// Idempotency key untuk add item order.
|
||||||
|
/// Di-generate saat user tap "Add Items", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
@override
|
||||||
|
final String? addItemIdempotencyKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
return 'OrderFormState(paymentMethod: $paymentMethod, customerName: $customerName, customer: $customer, failureOrCreateOrder: $failureOrCreateOrder, failureOrCreateOrderWithPayment: $failureOrCreateOrderWithPayment, failureOrAddItemOrder: $failureOrAddItemOrder, isCreating: $isCreating, isCreatingWithPayment: $isCreatingWithPayment, isAddingItemOrder: $isAddingItemOrder)';
|
return 'OrderFormState(paymentMethod: $paymentMethod, customerName: $customerName, customer: $customer, failureOrCreateOrder: $failureOrCreateOrder, failureOrCreateOrderWithPayment: $failureOrCreateOrderWithPayment, failureOrAddItemOrder: $failureOrAddItemOrder, isCreating: $isCreating, isCreatingWithPayment: $isCreatingWithPayment, isAddingItemOrder: $isAddingItemOrder, addItemIdempotencyKey: $addItemIdempotencyKey)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1868,7 +1892,10 @@ class _$OrderFormStateImpl
|
|||||||
..add(DiagnosticsProperty('failureOrAddItemOrder', failureOrAddItemOrder))
|
..add(DiagnosticsProperty('failureOrAddItemOrder', failureOrAddItemOrder))
|
||||||
..add(DiagnosticsProperty('isCreating', isCreating))
|
..add(DiagnosticsProperty('isCreating', isCreating))
|
||||||
..add(DiagnosticsProperty('isCreatingWithPayment', isCreatingWithPayment))
|
..add(DiagnosticsProperty('isCreatingWithPayment', isCreatingWithPayment))
|
||||||
..add(DiagnosticsProperty('isAddingItemOrder', isAddingItemOrder));
|
..add(DiagnosticsProperty('isAddingItemOrder', isAddingItemOrder))
|
||||||
|
..add(
|
||||||
|
DiagnosticsProperty('addItemIdempotencyKey', addItemIdempotencyKey),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1897,7 +1924,9 @@ class _$OrderFormStateImpl
|
|||||||
(identical(other.isCreatingWithPayment, isCreatingWithPayment) ||
|
(identical(other.isCreatingWithPayment, isCreatingWithPayment) ||
|
||||||
other.isCreatingWithPayment == isCreatingWithPayment) &&
|
other.isCreatingWithPayment == isCreatingWithPayment) &&
|
||||||
(identical(other.isAddingItemOrder, isAddingItemOrder) ||
|
(identical(other.isAddingItemOrder, isAddingItemOrder) ||
|
||||||
other.isAddingItemOrder == isAddingItemOrder));
|
other.isAddingItemOrder == isAddingItemOrder) &&
|
||||||
|
(identical(other.addItemIdempotencyKey, addItemIdempotencyKey) ||
|
||||||
|
other.addItemIdempotencyKey == addItemIdempotencyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1912,6 +1941,7 @@ class _$OrderFormStateImpl
|
|||||||
isCreating,
|
isCreating,
|
||||||
isCreatingWithPayment,
|
isCreatingWithPayment,
|
||||||
isAddingItemOrder,
|
isAddingItemOrder,
|
||||||
|
addItemIdempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of OrderFormState
|
/// Create a copy of OrderFormState
|
||||||
@@ -1938,6 +1968,7 @@ abstract class _OrderFormState implements OrderFormState {
|
|||||||
final bool isCreating,
|
final bool isCreating,
|
||||||
final bool isCreatingWithPayment,
|
final bool isCreatingWithPayment,
|
||||||
final bool isAddingItemOrder,
|
final bool isAddingItemOrder,
|
||||||
|
final String? addItemIdempotencyKey,
|
||||||
}) = _$OrderFormStateImpl;
|
}) = _$OrderFormStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1959,6 +1990,12 @@ abstract class _OrderFormState implements OrderFormState {
|
|||||||
@override
|
@override
|
||||||
bool get isAddingItemOrder;
|
bool get isAddingItemOrder;
|
||||||
|
|
||||||
|
/// Idempotency key untuk add item order.
|
||||||
|
/// Di-generate saat user tap "Add Items", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
@override
|
||||||
|
String? get addItemIdempotencyKey;
|
||||||
|
|
||||||
/// Create a copy of OrderFormState
|
/// Create a copy of OrderFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ class OrderFormState with _$OrderFormState {
|
|||||||
@Default(false) bool isCreating,
|
@Default(false) bool isCreating,
|
||||||
@Default(false) bool isCreatingWithPayment,
|
@Default(false) bool isCreatingWithPayment,
|
||||||
@Default(false) bool isAddingItemOrder,
|
@Default(false) bool isAddingItemOrder,
|
||||||
|
/// Idempotency key untuk add item order.
|
||||||
|
/// Di-generate saat user tap "Add Items", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
String? addItemIdempotencyKey,
|
||||||
}) = _OrderFormState;
|
}) = _OrderFormState;
|
||||||
|
|
||||||
factory OrderFormState.initial() => OrderFormState(
|
factory OrderFormState.initial() => OrderFormState(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:bloc/bloc.dart';
|
|||||||
import 'package:dartz/dartz.dart' hide Order;
|
import 'package:dartz/dartz.dart' hide Order;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart' hide Order;
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../common/types/split_type.dart';
|
import '../../../common/types/split_type.dart';
|
||||||
import '../../../domain/order/order.dart';
|
import '../../../domain/order/order.dart';
|
||||||
@@ -38,7 +39,15 @@ class PaymentFormBloc extends Bloc<PaymentFormEvent, PaymentFormState> {
|
|||||||
submitted: (e) async {
|
submitted: (e) async {
|
||||||
Either<OrderFailure, Payment> failureOrPayment;
|
Either<OrderFailure, Payment> failureOrPayment;
|
||||||
|
|
||||||
emit(state.copyWith(isSubmitting: true, failureOrPayment: none()));
|
// Generate new idempotency key saat user intent (tap Bayar)
|
||||||
|
// Kalau sedang retry (isSubmitting sebelumnya gagal), pakai key yang sama
|
||||||
|
final idempotencyKey = state.idempotencyKey ?? const Uuid().v4();
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
isSubmitting: true,
|
||||||
|
failureOrPayment: none(),
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
|
));
|
||||||
|
|
||||||
final request = PaymentRequest(
|
final request = PaymentRequest(
|
||||||
orderId: state.order.id,
|
orderId: state.order.id,
|
||||||
@@ -58,20 +67,32 @@ class PaymentFormBloc extends Bloc<PaymentFormEvent, PaymentFormState> {
|
|||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
failureOrPayment = await _repository.createPayment(request: request);
|
failureOrPayment = await _repository.createPayment(
|
||||||
|
request: request,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
failureOrPayment: optionOf(failureOrPayment),
|
failureOrPayment: optionOf(failureOrPayment),
|
||||||
|
// Clear key on success, keep on failure for retry
|
||||||
|
idempotencyKey: failureOrPayment.isRight() ? null : idempotencyKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
submittedSplitBill: (e) async {
|
submittedSplitBill: (e) async {
|
||||||
Either<OrderFailure, Payment> failureOrPayment;
|
Either<OrderFailure, Payment> failureOrPayment;
|
||||||
|
|
||||||
|
// Generate new idempotency key untuk split bill
|
||||||
|
final idempotencyKey = state.splitBillIdempotencyKey ?? const Uuid().v4();
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(isSubmitting: true, failureOrPaymentSplitBill: none()),
|
state.copyWith(
|
||||||
|
isSubmitting: true,
|
||||||
|
failureOrPaymentSplitBill: none(),
|
||||||
|
splitBillIdempotencyKey: idempotencyKey,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final request = PaymentSplitBillRequest(
|
final request = PaymentSplitBillRequest(
|
||||||
@@ -93,12 +114,18 @@ class PaymentFormBloc extends Bloc<PaymentFormEvent, PaymentFormState> {
|
|||||||
|
|
||||||
log(request.toString());
|
log(request.toString());
|
||||||
|
|
||||||
failureOrPayment = await _repository.createSplitBill(request);
|
failureOrPayment = await _repository.createSplitBill(
|
||||||
|
request,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
failureOrPaymentSplitBill: optionOf(failureOrPayment),
|
failureOrPaymentSplitBill: optionOf(failureOrPayment),
|
||||||
|
// Clear key on success, keep on failure for retry
|
||||||
|
splitBillIdempotencyKey:
|
||||||
|
failureOrPayment.isRight() ? null : idempotencyKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -813,6 +813,14 @@ mixin _$PaymentFormState {
|
|||||||
PaymentMethod? get paymentMethod => throw _privateConstructorUsedError;
|
PaymentMethod? get paymentMethod => throw _privateConstructorUsedError;
|
||||||
bool get isSubmitting => throw _privateConstructorUsedError;
|
bool get isSubmitting => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Idempotency key untuk payment submission.
|
||||||
|
/// Di-generate saat user tap "Bayar", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
String? get idempotencyKey => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Idempotency key untuk split bill submission.
|
||||||
|
String? get splitBillIdempotencyKey => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of PaymentFormState
|
/// Create a copy of PaymentFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -834,6 +842,8 @@ abstract class $PaymentFormStateCopyWith<$Res> {
|
|||||||
Option<Either<OrderFailure, Payment>> failureOrPaymentSplitBill,
|
Option<Either<OrderFailure, Payment>> failureOrPaymentSplitBill,
|
||||||
PaymentMethod? paymentMethod,
|
PaymentMethod? paymentMethod,
|
||||||
bool isSubmitting,
|
bool isSubmitting,
|
||||||
|
String? idempotencyKey,
|
||||||
|
String? splitBillIdempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
$OrderCopyWith<$Res> get order;
|
$OrderCopyWith<$Res> get order;
|
||||||
@@ -861,6 +871,8 @@ class _$PaymentFormStateCopyWithImpl<$Res, $Val extends PaymentFormState>
|
|||||||
Object? failureOrPaymentSplitBill = null,
|
Object? failureOrPaymentSplitBill = null,
|
||||||
Object? paymentMethod = freezed,
|
Object? paymentMethod = freezed,
|
||||||
Object? isSubmitting = null,
|
Object? isSubmitting = null,
|
||||||
|
Object? idempotencyKey = freezed,
|
||||||
|
Object? splitBillIdempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -888,6 +900,14 @@ class _$PaymentFormStateCopyWithImpl<$Res, $Val extends PaymentFormState>
|
|||||||
? _value.isSubmitting
|
? _value.isSubmitting
|
||||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
idempotencyKey: freezed == idempotencyKey
|
||||||
|
? _value.idempotencyKey
|
||||||
|
: idempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
splitBillIdempotencyKey: freezed == splitBillIdempotencyKey
|
||||||
|
? _value.splitBillIdempotencyKey
|
||||||
|
: splitBillIdempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -934,6 +954,8 @@ abstract class _$$PaymentFormStateImplCopyWith<$Res>
|
|||||||
Option<Either<OrderFailure, Payment>> failureOrPaymentSplitBill,
|
Option<Either<OrderFailure, Payment>> failureOrPaymentSplitBill,
|
||||||
PaymentMethod? paymentMethod,
|
PaymentMethod? paymentMethod,
|
||||||
bool isSubmitting,
|
bool isSubmitting,
|
||||||
|
String? idempotencyKey,
|
||||||
|
String? splitBillIdempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -962,6 +984,8 @@ class __$$PaymentFormStateImplCopyWithImpl<$Res>
|
|||||||
Object? failureOrPaymentSplitBill = null,
|
Object? failureOrPaymentSplitBill = null,
|
||||||
Object? paymentMethod = freezed,
|
Object? paymentMethod = freezed,
|
||||||
Object? isSubmitting = null,
|
Object? isSubmitting = null,
|
||||||
|
Object? idempotencyKey = freezed,
|
||||||
|
Object? splitBillIdempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$PaymentFormStateImpl(
|
_$PaymentFormStateImpl(
|
||||||
@@ -989,6 +1013,14 @@ class __$$PaymentFormStateImplCopyWithImpl<$Res>
|
|||||||
? _value.isSubmitting
|
? _value.isSubmitting
|
||||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
idempotencyKey: freezed == idempotencyKey
|
||||||
|
? _value.idempotencyKey
|
||||||
|
: idempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
splitBillIdempotencyKey: freezed == splitBillIdempotencyKey
|
||||||
|
? _value.splitBillIdempotencyKey
|
||||||
|
: splitBillIdempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1004,6 +1036,8 @@ class _$PaymentFormStateImpl implements _PaymentFormState {
|
|||||||
required this.failureOrPaymentSplitBill,
|
required this.failureOrPaymentSplitBill,
|
||||||
this.paymentMethod,
|
this.paymentMethod,
|
||||||
this.isSubmitting = false,
|
this.isSubmitting = false,
|
||||||
|
this.idempotencyKey,
|
||||||
|
this.splitBillIdempotencyKey,
|
||||||
}) : _pendingItems = pendingItems;
|
}) : _pendingItems = pendingItems;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1026,9 +1060,19 @@ class _$PaymentFormStateImpl implements _PaymentFormState {
|
|||||||
@JsonKey()
|
@JsonKey()
|
||||||
final bool isSubmitting;
|
final bool isSubmitting;
|
||||||
|
|
||||||
|
/// Idempotency key untuk payment submission.
|
||||||
|
/// Di-generate saat user tap "Bayar", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
@override
|
||||||
|
final String? idempotencyKey;
|
||||||
|
|
||||||
|
/// Idempotency key untuk split bill submission.
|
||||||
|
@override
|
||||||
|
final String? splitBillIdempotencyKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'PaymentFormState(order: $order, pendingItems: $pendingItems, failureOrPayment: $failureOrPayment, failureOrPaymentSplitBill: $failureOrPaymentSplitBill, paymentMethod: $paymentMethod, isSubmitting: $isSubmitting)';
|
return 'PaymentFormState(order: $order, pendingItems: $pendingItems, failureOrPayment: $failureOrPayment, failureOrPaymentSplitBill: $failureOrPaymentSplitBill, paymentMethod: $paymentMethod, isSubmitting: $isSubmitting, idempotencyKey: $idempotencyKey, splitBillIdempotencyKey: $splitBillIdempotencyKey)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1051,7 +1095,14 @@ class _$PaymentFormStateImpl implements _PaymentFormState {
|
|||||||
(identical(other.paymentMethod, paymentMethod) ||
|
(identical(other.paymentMethod, paymentMethod) ||
|
||||||
other.paymentMethod == paymentMethod) &&
|
other.paymentMethod == paymentMethod) &&
|
||||||
(identical(other.isSubmitting, isSubmitting) ||
|
(identical(other.isSubmitting, isSubmitting) ||
|
||||||
other.isSubmitting == isSubmitting));
|
other.isSubmitting == isSubmitting) &&
|
||||||
|
(identical(other.idempotencyKey, idempotencyKey) ||
|
||||||
|
other.idempotencyKey == idempotencyKey) &&
|
||||||
|
(identical(
|
||||||
|
other.splitBillIdempotencyKey,
|
||||||
|
splitBillIdempotencyKey,
|
||||||
|
) ||
|
||||||
|
other.splitBillIdempotencyKey == splitBillIdempotencyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1063,6 +1114,8 @@ class _$PaymentFormStateImpl implements _PaymentFormState {
|
|||||||
failureOrPaymentSplitBill,
|
failureOrPaymentSplitBill,
|
||||||
paymentMethod,
|
paymentMethod,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
idempotencyKey,
|
||||||
|
splitBillIdempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of PaymentFormState
|
/// Create a copy of PaymentFormState
|
||||||
@@ -1086,6 +1139,8 @@ abstract class _PaymentFormState implements PaymentFormState {
|
|||||||
failureOrPaymentSplitBill,
|
failureOrPaymentSplitBill,
|
||||||
final PaymentMethod? paymentMethod,
|
final PaymentMethod? paymentMethod,
|
||||||
final bool isSubmitting,
|
final bool isSubmitting,
|
||||||
|
final String? idempotencyKey,
|
||||||
|
final String? splitBillIdempotencyKey,
|
||||||
}) = _$PaymentFormStateImpl;
|
}) = _$PaymentFormStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1101,6 +1156,16 @@ abstract class _PaymentFormState implements PaymentFormState {
|
|||||||
@override
|
@override
|
||||||
bool get isSubmitting;
|
bool get isSubmitting;
|
||||||
|
|
||||||
|
/// Idempotency key untuk payment submission.
|
||||||
|
/// Di-generate saat user tap "Bayar", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
@override
|
||||||
|
String? get idempotencyKey;
|
||||||
|
|
||||||
|
/// Idempotency key untuk split bill submission.
|
||||||
|
@override
|
||||||
|
String? get splitBillIdempotencyKey;
|
||||||
|
|
||||||
/// Create a copy of PaymentFormState
|
/// Create a copy of PaymentFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ class PaymentFormState with _$PaymentFormState {
|
|||||||
required Option<Either<OrderFailure, Payment>> failureOrPaymentSplitBill,
|
required Option<Either<OrderFailure, Payment>> failureOrPaymentSplitBill,
|
||||||
PaymentMethod? paymentMethod,
|
PaymentMethod? paymentMethod,
|
||||||
@Default(false) bool isSubmitting,
|
@Default(false) bool isSubmitting,
|
||||||
|
/// Idempotency key untuk payment submission.
|
||||||
|
/// Di-generate saat user tap "Bayar", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
String? idempotencyKey,
|
||||||
|
/// Idempotency key untuk split bill submission.
|
||||||
|
String? splitBillIdempotencyKey,
|
||||||
}) = _PaymentFormState;
|
}) = _PaymentFormState;
|
||||||
|
|
||||||
factory PaymentFormState.initial() => PaymentFormState(
|
factory PaymentFormState.initial() => PaymentFormState(
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
|
||||||
|
import '../../../domain/order/order.dart';
|
||||||
|
import '../../../domain/printer/printer.dart';
|
||||||
|
|
||||||
|
part 'print_struck_event.dart';
|
||||||
|
part 'print_struck_state.dart';
|
||||||
|
part 'print_struck_bloc.freezed.dart';
|
||||||
|
|
||||||
|
@injectable
|
||||||
|
class PrintStruckBloc extends Bloc<PrintStruckEvent, PrintStruckState> {
|
||||||
|
final IPrinterRepository _printerRepository;
|
||||||
|
|
||||||
|
PrintStruckBloc(this._printerRepository) : super(PrintStruckState.initial()) {
|
||||||
|
on<PrintStruckEvent>(_onPrintStruckEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onPrintStruckEvent(
|
||||||
|
PrintStruckEvent event,
|
||||||
|
Emitter<PrintStruckState> emit,
|
||||||
|
) => event.map(
|
||||||
|
order: (e) => _print(emit, e.order, _stationsForOrder(e.order)),
|
||||||
|
payment: (e) => _print(emit, e.order, const [PrintStation.receipt]),
|
||||||
|
cashier: (e) => _print(emit, e.order, const [PrintStation.cashier]),
|
||||||
|
checker: (e) => _print(emit, e.order, const [PrintStation.checker]),
|
||||||
|
kitchen: (e) => _print(emit, e.order, const [PrintStation.kitchen]),
|
||||||
|
bar: (e) => _print(emit, e.order, const [PrintStation.bar]),
|
||||||
|
voided: (e) => _print(emit, e.order, const [PrintStation.voided]),
|
||||||
|
splitBill: (e) => _print(emit, e.order, const [PrintStation.splitBill]),
|
||||||
|
retryFailed: (_) {
|
||||||
|
final order = state.order;
|
||||||
|
final failed = [
|
||||||
|
for (final station in state.failedStations) station.station,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (order == null || failed.isEmpty) return Future.value();
|
||||||
|
|
||||||
|
return _print(emit, order, failed);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Struk pelanggan hanya ikut dicetak kalau ordernya sudah dibayar.
|
||||||
|
List<PrintStation> _stationsForOrder(Order order) => [
|
||||||
|
if (order.totalPaid > 0) PrintStation.receipt,
|
||||||
|
PrintStation.cashier,
|
||||||
|
PrintStation.checker,
|
||||||
|
PrintStation.kitchen,
|
||||||
|
PrintStation.bar,
|
||||||
|
];
|
||||||
|
|
||||||
|
Future<void> _print(
|
||||||
|
Emitter<PrintStruckState> emit,
|
||||||
|
Order order,
|
||||||
|
List<PrintStation> stations,
|
||||||
|
) async {
|
||||||
|
// Tombol yang tertekan dua kali tidak boleh memulai job kedua — repository
|
||||||
|
// akan mengantrikannya dan progres yang tampil jadi membingungkan.
|
||||||
|
if (state.isPrinting) return;
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isPrinting: true,
|
||||||
|
order: order,
|
||||||
|
jobId: state.jobId + 1,
|
||||||
|
stations: [
|
||||||
|
for (final station in stations) PrintStationResult.pending(station),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await emit.forEach<List<PrintStationResult>>(
|
||||||
|
_printerRepository.printStations(order: order, stations: stations),
|
||||||
|
onData: (results) => state.copyWith(stations: results),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cetak bisa selesai setelah bloc ditutup (kasir keluar dari aplikasi).
|
||||||
|
if (isClosed) return;
|
||||||
|
emit(state.copyWith(isPrinting: false));
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
part of 'print_struck_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class PrintStruckEvent with _$PrintStruckEvent {
|
||||||
|
/// Cetak seluruh station untuk satu order.
|
||||||
|
const factory PrintStruckEvent.order(Order order) = _Order;
|
||||||
|
|
||||||
|
const factory PrintStruckEvent.cashier(Order order) = _Cashier;
|
||||||
|
const factory PrintStruckEvent.payment(Order order) = _Payment;
|
||||||
|
const factory PrintStruckEvent.voided(Order order) = _Voided;
|
||||||
|
const factory PrintStruckEvent.checker(Order order) = _Checker;
|
||||||
|
const factory PrintStruckEvent.kitchen(Order order) = _Kitchen;
|
||||||
|
const factory PrintStruckEvent.bar(Order order) = _Bar;
|
||||||
|
const factory PrintStruckEvent.splitBill(Order order) = _SplitBill;
|
||||||
|
|
||||||
|
/// Ulangi hanya station yang gagal di job terakhir, tanpa mencetak ulang
|
||||||
|
/// station yang sudah keluar.
|
||||||
|
const factory PrintStruckEvent.retryFailed() = _RetryFailed;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
part of 'print_struck_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class PrintStruckState with _$PrintStruckState {
|
||||||
|
const PrintStruckState._();
|
||||||
|
|
||||||
|
const factory PrintStruckState({
|
||||||
|
/// Status seluruh station di job cetak terakhir, urut sesuai urutan cetak.
|
||||||
|
@Default(<PrintStationResult>[]) List<PrintStationResult> stations,
|
||||||
|
@Default(false) bool isPrinting,
|
||||||
|
|
||||||
|
/// Order yang sedang dicetak, disimpan supaya station yang gagal bisa
|
||||||
|
/// diulang tanpa perlu dikirim ulang dari halaman pemanggil.
|
||||||
|
Order? order,
|
||||||
|
|
||||||
|
/// Naik tiap job baru dimulai. UI memakainya untuk tahu kapan harus
|
||||||
|
/// membuka dialog progres.
|
||||||
|
@Default(0) int jobId,
|
||||||
|
}) = _PrintStruckState;
|
||||||
|
|
||||||
|
factory PrintStruckState.initial() => const PrintStruckState();
|
||||||
|
|
||||||
|
List<PrintStationResult> get failedStations =>
|
||||||
|
stations.where((station) => station.isFailed).toList();
|
||||||
|
|
||||||
|
bool get hasFailure => failedStations.isNotEmpty;
|
||||||
|
|
||||||
|
/// Station yang benar-benar dicetak — yang dilewati tidak ikut dihitung.
|
||||||
|
int get plannedCount =>
|
||||||
|
stations.where((station) => !station.isSkipped).length;
|
||||||
|
|
||||||
|
int get successCount =>
|
||||||
|
stations.where((station) => station.isSuccess).length;
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart' hide Order;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
|
||||||
import '../../../common/function/app_function.dart';
|
import '../../../common/function/app_function.dart';
|
||||||
|
import '../../../domain/order/order.dart';
|
||||||
|
import '../../../domain/outlet/outlet.dart';
|
||||||
import '../../../domain/printer/printer.dart';
|
import '../../../domain/printer/printer.dart';
|
||||||
|
import '../../../presentation/components/print/print_ui.dart';
|
||||||
|
|
||||||
part 'printer_form_event.dart';
|
part 'printer_form_event.dart';
|
||||||
part 'printer_form_state.dart';
|
part 'printer_form_state.dart';
|
||||||
@@ -13,7 +16,10 @@ part 'printer_form_bloc.freezed.dart';
|
|||||||
@injectable
|
@injectable
|
||||||
class PrinterFormBloc extends Bloc<PrinterFormEvent, PrinterFormState> {
|
class PrinterFormBloc extends Bloc<PrinterFormEvent, PrinterFormState> {
|
||||||
final IPrinterRepository _printerRepository;
|
final IPrinterRepository _printerRepository;
|
||||||
PrinterFormBloc(this._printerRepository) : super(PrinterFormState.initial()) {
|
final IOutletRepository _outletRepository;
|
||||||
|
|
||||||
|
PrinterFormBloc(this._printerRepository, this._outletRepository)
|
||||||
|
: super(PrinterFormState.initial()) {
|
||||||
on<PrinterFormEvent>(_onPrinterFormEvent);
|
on<PrinterFormEvent>(_onPrinterFormEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +110,78 @@ class PrinterFormBloc extends Bloc<PrinterFormEvent, PrinterFormState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
printTest: (e) async {
|
||||||
|
Either<PrinterFailure, bool> failureOrPrinter;
|
||||||
|
|
||||||
|
emit(state.copyWith(isPrinting: true, failureOrPrintTest: none()));
|
||||||
|
|
||||||
|
final currentOutlet = await _outletRepository.currentOutlet();
|
||||||
|
|
||||||
|
// Ambil paper size dari DB, fallback ke state.paper jika belum tersimpan
|
||||||
|
final savedPrinter = await _printerRepository.getPrinterByCode(e.code);
|
||||||
|
final paper = savedPrinter.fold(
|
||||||
|
(_) => int.tryParse(state.paper) ?? 58,
|
||||||
|
(p) => int.tryParse(p.paper) ?? 58,
|
||||||
|
);
|
||||||
|
|
||||||
|
List<int> printValue = [];
|
||||||
|
|
||||||
|
if (e.code == "receipt") {
|
||||||
|
printValue = await PrintUi().printOrder(
|
||||||
|
order: Order.mockOrder(),
|
||||||
|
outlet: currentOutlet,
|
||||||
|
cashierName: 'Kasir Test',
|
||||||
|
paper: paper,
|
||||||
|
);
|
||||||
|
} else if (e.code == "checker") {
|
||||||
|
printValue = await PrintUi().printChecker(
|
||||||
|
order: Order.mockOrder(),
|
||||||
|
outlet: currentOutlet,
|
||||||
|
cashierName: 'Kasir Test',
|
||||||
|
paper: paper,
|
||||||
|
);
|
||||||
|
} else if (e.code == 'kitchen') {
|
||||||
|
printValue = await PrintUi().printKitchen(
|
||||||
|
order: Order.mockOrder(),
|
||||||
|
outlet: currentOutlet,
|
||||||
|
cashierName: 'Kasir Test',
|
||||||
|
paper: paper,
|
||||||
|
);
|
||||||
|
} else if (e.code == 'bar') {
|
||||||
|
printValue = await PrintUi().printBar(
|
||||||
|
order: Order.mockOrder(),
|
||||||
|
outlet: currentOutlet,
|
||||||
|
cashierName: 'Kasir Test',
|
||||||
|
paper: paper,
|
||||||
|
);
|
||||||
|
} else if (e.code == 'ticket') {
|
||||||
|
printValue = await PrintUi().printTicket(
|
||||||
|
order: Order.mockOrder(),
|
||||||
|
outlet: currentOutlet,
|
||||||
|
paper: paper,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final printer = Printer.fromTest(
|
||||||
|
code: e.code,
|
||||||
|
name: state.name,
|
||||||
|
address: e.macAccdress,
|
||||||
|
paper: paper.toString(),
|
||||||
|
type: state.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
failureOrPrinter = await _printerRepository.printStruct(
|
||||||
|
printer,
|
||||||
|
printValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isPrinting: false,
|
||||||
|
failureOrPrintTest: optionOf(failureOrPrinter),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ mixin _$PrinterFormEvent {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
@@ -38,6 +39,7 @@ mixin _$PrinterFormEvent {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
@@ -49,6 +51,7 @@ mixin _$PrinterFormEvent {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
@@ -61,6 +64,7 @@ mixin _$PrinterFormEvent {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
@@ -72,6 +76,7 @@ mixin _$PrinterFormEvent {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
@@ -83,6 +88,7 @@ mixin _$PrinterFormEvent {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) => throw _privateConstructorUsedError;
|
}) => throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
@@ -187,6 +193,7 @@ class _$CodeChangedImpl implements _CodeChanged {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return codeChanged(code);
|
return codeChanged(code);
|
||||||
}
|
}
|
||||||
@@ -202,6 +209,7 @@ class _$CodeChangedImpl implements _CodeChanged {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return codeChanged?.call(code);
|
return codeChanged?.call(code);
|
||||||
}
|
}
|
||||||
@@ -217,6 +225,7 @@ class _$CodeChangedImpl implements _CodeChanged {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (codeChanged != null) {
|
if (codeChanged != null) {
|
||||||
@@ -236,6 +245,7 @@ class _$CodeChangedImpl implements _CodeChanged {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return codeChanged(this);
|
return codeChanged(this);
|
||||||
}
|
}
|
||||||
@@ -251,6 +261,7 @@ class _$CodeChangedImpl implements _CodeChanged {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return codeChanged?.call(this);
|
return codeChanged?.call(this);
|
||||||
}
|
}
|
||||||
@@ -266,6 +277,7 @@ class _$CodeChangedImpl implements _CodeChanged {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (codeChanged != null) {
|
if (codeChanged != null) {
|
||||||
@@ -365,6 +377,7 @@ class _$NameChangedImpl implements _NameChanged {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return nameChanged(name);
|
return nameChanged(name);
|
||||||
}
|
}
|
||||||
@@ -380,6 +393,7 @@ class _$NameChangedImpl implements _NameChanged {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return nameChanged?.call(name);
|
return nameChanged?.call(name);
|
||||||
}
|
}
|
||||||
@@ -395,6 +409,7 @@ class _$NameChangedImpl implements _NameChanged {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (nameChanged != null) {
|
if (nameChanged != null) {
|
||||||
@@ -414,6 +429,7 @@ class _$NameChangedImpl implements _NameChanged {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return nameChanged(this);
|
return nameChanged(this);
|
||||||
}
|
}
|
||||||
@@ -429,6 +445,7 @@ class _$NameChangedImpl implements _NameChanged {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return nameChanged?.call(this);
|
return nameChanged?.call(this);
|
||||||
}
|
}
|
||||||
@@ -444,6 +461,7 @@ class _$NameChangedImpl implements _NameChanged {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (nameChanged != null) {
|
if (nameChanged != null) {
|
||||||
@@ -546,6 +564,7 @@ class _$AddressChangedImpl implements _AddressChanged {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return addressChanged(address);
|
return addressChanged(address);
|
||||||
}
|
}
|
||||||
@@ -561,6 +580,7 @@ class _$AddressChangedImpl implements _AddressChanged {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return addressChanged?.call(address);
|
return addressChanged?.call(address);
|
||||||
}
|
}
|
||||||
@@ -576,6 +596,7 @@ class _$AddressChangedImpl implements _AddressChanged {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (addressChanged != null) {
|
if (addressChanged != null) {
|
||||||
@@ -595,6 +616,7 @@ class _$AddressChangedImpl implements _AddressChanged {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return addressChanged(this);
|
return addressChanged(this);
|
||||||
}
|
}
|
||||||
@@ -610,6 +632,7 @@ class _$AddressChangedImpl implements _AddressChanged {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return addressChanged?.call(this);
|
return addressChanged?.call(this);
|
||||||
}
|
}
|
||||||
@@ -625,6 +648,7 @@ class _$AddressChangedImpl implements _AddressChanged {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (addressChanged != null) {
|
if (addressChanged != null) {
|
||||||
@@ -724,6 +748,7 @@ class _$TypeChangedImpl implements _TypeChanged {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return typeChanged(type);
|
return typeChanged(type);
|
||||||
}
|
}
|
||||||
@@ -739,6 +764,7 @@ class _$TypeChangedImpl implements _TypeChanged {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return typeChanged?.call(type);
|
return typeChanged?.call(type);
|
||||||
}
|
}
|
||||||
@@ -754,6 +780,7 @@ class _$TypeChangedImpl implements _TypeChanged {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (typeChanged != null) {
|
if (typeChanged != null) {
|
||||||
@@ -773,6 +800,7 @@ class _$TypeChangedImpl implements _TypeChanged {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return typeChanged(this);
|
return typeChanged(this);
|
||||||
}
|
}
|
||||||
@@ -788,6 +816,7 @@ class _$TypeChangedImpl implements _TypeChanged {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return typeChanged?.call(this);
|
return typeChanged?.call(this);
|
||||||
}
|
}
|
||||||
@@ -803,6 +832,7 @@ class _$TypeChangedImpl implements _TypeChanged {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (typeChanged != null) {
|
if (typeChanged != null) {
|
||||||
@@ -902,6 +932,7 @@ class _$PaperChangedImpl implements _PaperChanged {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return paperChanged(paper);
|
return paperChanged(paper);
|
||||||
}
|
}
|
||||||
@@ -917,6 +948,7 @@ class _$PaperChangedImpl implements _PaperChanged {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return paperChanged?.call(paper);
|
return paperChanged?.call(paper);
|
||||||
}
|
}
|
||||||
@@ -932,6 +964,7 @@ class _$PaperChangedImpl implements _PaperChanged {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (paperChanged != null) {
|
if (paperChanged != null) {
|
||||||
@@ -951,6 +984,7 @@ class _$PaperChangedImpl implements _PaperChanged {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return paperChanged(this);
|
return paperChanged(this);
|
||||||
}
|
}
|
||||||
@@ -966,6 +1000,7 @@ class _$PaperChangedImpl implements _PaperChanged {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return paperChanged?.call(this);
|
return paperChanged?.call(this);
|
||||||
}
|
}
|
||||||
@@ -981,6 +1016,7 @@ class _$PaperChangedImpl implements _PaperChanged {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (paperChanged != null) {
|
if (paperChanged != null) {
|
||||||
@@ -1053,6 +1089,7 @@ class _$CreatedImpl implements _Created {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return created();
|
return created();
|
||||||
}
|
}
|
||||||
@@ -1068,6 +1105,7 @@ class _$CreatedImpl implements _Created {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return created?.call();
|
return created?.call();
|
||||||
}
|
}
|
||||||
@@ -1083,6 +1121,7 @@ class _$CreatedImpl implements _Created {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (created != null) {
|
if (created != null) {
|
||||||
@@ -1102,6 +1141,7 @@ class _$CreatedImpl implements _Created {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return created(this);
|
return created(this);
|
||||||
}
|
}
|
||||||
@@ -1117,6 +1157,7 @@ class _$CreatedImpl implements _Created {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return created?.call(this);
|
return created?.call(this);
|
||||||
}
|
}
|
||||||
@@ -1132,6 +1173,7 @@ class _$CreatedImpl implements _Created {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (created != null) {
|
if (created != null) {
|
||||||
@@ -1223,6 +1265,7 @@ class _$UpdatedImpl implements _Updated {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return updated(id);
|
return updated(id);
|
||||||
}
|
}
|
||||||
@@ -1238,6 +1281,7 @@ class _$UpdatedImpl implements _Updated {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return updated?.call(id);
|
return updated?.call(id);
|
||||||
}
|
}
|
||||||
@@ -1253,6 +1297,7 @@ class _$UpdatedImpl implements _Updated {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (updated != null) {
|
if (updated != null) {
|
||||||
@@ -1272,6 +1317,7 @@ class _$UpdatedImpl implements _Updated {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return updated(this);
|
return updated(this);
|
||||||
}
|
}
|
||||||
@@ -1287,6 +1333,7 @@ class _$UpdatedImpl implements _Updated {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return updated?.call(this);
|
return updated?.call(this);
|
||||||
}
|
}
|
||||||
@@ -1302,6 +1349,7 @@ class _$UpdatedImpl implements _Updated {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (updated != null) {
|
if (updated != null) {
|
||||||
@@ -1401,6 +1449,7 @@ class _$DeletedImpl implements _Deleted {
|
|||||||
required TResult Function() created,
|
required TResult Function() created,
|
||||||
required TResult Function(int id) updated,
|
required TResult Function(int id) updated,
|
||||||
required TResult Function(int id) deleted,
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
}) {
|
}) {
|
||||||
return deleted(id);
|
return deleted(id);
|
||||||
}
|
}
|
||||||
@@ -1416,6 +1465,7 @@ class _$DeletedImpl implements _Deleted {
|
|||||||
TResult? Function()? created,
|
TResult? Function()? created,
|
||||||
TResult? Function(int id)? updated,
|
TResult? Function(int id)? updated,
|
||||||
TResult? Function(int id)? deleted,
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return deleted?.call(id);
|
return deleted?.call(id);
|
||||||
}
|
}
|
||||||
@@ -1431,6 +1481,7 @@ class _$DeletedImpl implements _Deleted {
|
|||||||
TResult Function()? created,
|
TResult Function()? created,
|
||||||
TResult Function(int id)? updated,
|
TResult Function(int id)? updated,
|
||||||
TResult Function(int id)? deleted,
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (deleted != null) {
|
if (deleted != null) {
|
||||||
@@ -1450,6 +1501,7 @@ class _$DeletedImpl implements _Deleted {
|
|||||||
required TResult Function(_Created value) created,
|
required TResult Function(_Created value) created,
|
||||||
required TResult Function(_Updated value) updated,
|
required TResult Function(_Updated value) updated,
|
||||||
required TResult Function(_Deleted value) deleted,
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
}) {
|
}) {
|
||||||
return deleted(this);
|
return deleted(this);
|
||||||
}
|
}
|
||||||
@@ -1465,6 +1517,7 @@ class _$DeletedImpl implements _Deleted {
|
|||||||
TResult? Function(_Created value)? created,
|
TResult? Function(_Created value)? created,
|
||||||
TResult? Function(_Updated value)? updated,
|
TResult? Function(_Updated value)? updated,
|
||||||
TResult? Function(_Deleted value)? deleted,
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
}) {
|
}) {
|
||||||
return deleted?.call(this);
|
return deleted?.call(this);
|
||||||
}
|
}
|
||||||
@@ -1480,6 +1533,7 @@ class _$DeletedImpl implements _Deleted {
|
|||||||
TResult Function(_Created value)? created,
|
TResult Function(_Created value)? created,
|
||||||
TResult Function(_Updated value)? updated,
|
TResult Function(_Updated value)? updated,
|
||||||
TResult Function(_Deleted value)? deleted,
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (deleted != null) {
|
if (deleted != null) {
|
||||||
@@ -1501,6 +1555,202 @@ abstract class _Deleted implements PrinterFormEvent {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$PrintTestImplCopyWith<$Res> {
|
||||||
|
factory _$$PrintTestImplCopyWith(
|
||||||
|
_$PrintTestImpl value,
|
||||||
|
$Res Function(_$PrintTestImpl) then,
|
||||||
|
) = __$$PrintTestImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({String code, String macAccdress});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$PrintTestImplCopyWithImpl<$Res>
|
||||||
|
extends _$PrinterFormEventCopyWithImpl<$Res, _$PrintTestImpl>
|
||||||
|
implements _$$PrintTestImplCopyWith<$Res> {
|
||||||
|
__$$PrintTestImplCopyWithImpl(
|
||||||
|
_$PrintTestImpl _value,
|
||||||
|
$Res Function(_$PrintTestImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of PrinterFormEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({Object? code = null, Object? macAccdress = null}) {
|
||||||
|
return _then(
|
||||||
|
_$PrintTestImpl(
|
||||||
|
code: null == code
|
||||||
|
? _value.code
|
||||||
|
: code // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
macAccdress: null == macAccdress
|
||||||
|
? _value.macAccdress
|
||||||
|
: macAccdress // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$PrintTestImpl implements _PrintTest {
|
||||||
|
const _$PrintTestImpl({required this.code, required this.macAccdress});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String code;
|
||||||
|
@override
|
||||||
|
final String macAccdress;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'PrinterFormEvent.printTest(code: $code, macAccdress: $macAccdress)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$PrintTestImpl &&
|
||||||
|
(identical(other.code, code) || other.code == code) &&
|
||||||
|
(identical(other.macAccdress, macAccdress) ||
|
||||||
|
other.macAccdress == macAccdress));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, code, macAccdress);
|
||||||
|
|
||||||
|
/// Create a copy of PrinterFormEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$PrintTestImplCopyWith<_$PrintTestImpl> get copyWith =>
|
||||||
|
__$$PrintTestImplCopyWithImpl<_$PrintTestImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(String code) codeChanged,
|
||||||
|
required TResult Function(String name) nameChanged,
|
||||||
|
required TResult Function(String address) addressChanged,
|
||||||
|
required TResult Function(String type) typeChanged,
|
||||||
|
required TResult Function(String paper) paperChanged,
|
||||||
|
required TResult Function() created,
|
||||||
|
required TResult Function(int id) updated,
|
||||||
|
required TResult Function(int id) deleted,
|
||||||
|
required TResult Function(String code, String macAccdress) printTest,
|
||||||
|
}) {
|
||||||
|
return printTest(code, macAccdress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(String code)? codeChanged,
|
||||||
|
TResult? Function(String name)? nameChanged,
|
||||||
|
TResult? Function(String address)? addressChanged,
|
||||||
|
TResult? Function(String type)? typeChanged,
|
||||||
|
TResult? Function(String paper)? paperChanged,
|
||||||
|
TResult? Function()? created,
|
||||||
|
TResult? Function(int id)? updated,
|
||||||
|
TResult? Function(int id)? deleted,
|
||||||
|
TResult? Function(String code, String macAccdress)? printTest,
|
||||||
|
}) {
|
||||||
|
return printTest?.call(code, macAccdress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(String code)? codeChanged,
|
||||||
|
TResult Function(String name)? nameChanged,
|
||||||
|
TResult Function(String address)? addressChanged,
|
||||||
|
TResult Function(String type)? typeChanged,
|
||||||
|
TResult Function(String paper)? paperChanged,
|
||||||
|
TResult Function()? created,
|
||||||
|
TResult Function(int id)? updated,
|
||||||
|
TResult Function(int id)? deleted,
|
||||||
|
TResult Function(String code, String macAccdress)? printTest,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (printTest != null) {
|
||||||
|
return printTest(code, macAccdress);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_CodeChanged value) codeChanged,
|
||||||
|
required TResult Function(_NameChanged value) nameChanged,
|
||||||
|
required TResult Function(_AddressChanged value) addressChanged,
|
||||||
|
required TResult Function(_TypeChanged value) typeChanged,
|
||||||
|
required TResult Function(_PaperChanged value) paperChanged,
|
||||||
|
required TResult Function(_Created value) created,
|
||||||
|
required TResult Function(_Updated value) updated,
|
||||||
|
required TResult Function(_Deleted value) deleted,
|
||||||
|
required TResult Function(_PrintTest value) printTest,
|
||||||
|
}) {
|
||||||
|
return printTest(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_CodeChanged value)? codeChanged,
|
||||||
|
TResult? Function(_NameChanged value)? nameChanged,
|
||||||
|
TResult? Function(_AddressChanged value)? addressChanged,
|
||||||
|
TResult? Function(_TypeChanged value)? typeChanged,
|
||||||
|
TResult? Function(_PaperChanged value)? paperChanged,
|
||||||
|
TResult? Function(_Created value)? created,
|
||||||
|
TResult? Function(_Updated value)? updated,
|
||||||
|
TResult? Function(_Deleted value)? deleted,
|
||||||
|
TResult? Function(_PrintTest value)? printTest,
|
||||||
|
}) {
|
||||||
|
return printTest?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_CodeChanged value)? codeChanged,
|
||||||
|
TResult Function(_NameChanged value)? nameChanged,
|
||||||
|
TResult Function(_AddressChanged value)? addressChanged,
|
||||||
|
TResult Function(_TypeChanged value)? typeChanged,
|
||||||
|
TResult Function(_PaperChanged value)? paperChanged,
|
||||||
|
TResult Function(_Created value)? created,
|
||||||
|
TResult Function(_Updated value)? updated,
|
||||||
|
TResult Function(_Deleted value)? deleted,
|
||||||
|
TResult Function(_PrintTest value)? printTest,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (printTest != null) {
|
||||||
|
return printTest(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _PrintTest implements PrinterFormEvent {
|
||||||
|
const factory _PrintTest({
|
||||||
|
required final String code,
|
||||||
|
required final String macAccdress,
|
||||||
|
}) = _$PrintTestImpl;
|
||||||
|
|
||||||
|
String get code;
|
||||||
|
String get macAccdress;
|
||||||
|
|
||||||
|
/// Create a copy of PrinterFormEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$PrintTestImplCopyWith<_$PrintTestImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$PrinterFormState {
|
mixin _$PrinterFormState {
|
||||||
String get code => throw _privateConstructorUsedError;
|
String get code => throw _privateConstructorUsedError;
|
||||||
@@ -1514,9 +1764,12 @@ mixin _$PrinterFormState {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
Option<Either<PrinterFailure, Unit>> get failureOrDeleteSuccess =>
|
Option<Either<PrinterFailure, Unit>> get failureOrDeleteSuccess =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
Option<Either<PrinterFailure, bool>> get failureOrPrintTest =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
dynamic get isCreating => throw _privateConstructorUsedError;
|
dynamic get isCreating => throw _privateConstructorUsedError;
|
||||||
dynamic get isUpdate => throw _privateConstructorUsedError;
|
dynamic get isUpdate => throw _privateConstructorUsedError;
|
||||||
dynamic get isDeleting => throw _privateConstructorUsedError;
|
dynamic get isDeleting => throw _privateConstructorUsedError;
|
||||||
|
dynamic get isPrinting => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of PrinterFormState
|
/// Create a copy of PrinterFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1541,9 +1794,11 @@ abstract class $PrinterFormStateCopyWith<$Res> {
|
|||||||
Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
||||||
Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
||||||
Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
||||||
|
Option<Either<PrinterFailure, bool>> failureOrPrintTest,
|
||||||
dynamic isCreating,
|
dynamic isCreating,
|
||||||
dynamic isUpdate,
|
dynamic isUpdate,
|
||||||
dynamic isDeleting,
|
dynamic isDeleting,
|
||||||
|
dynamic isPrinting,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1570,9 +1825,11 @@ class _$PrinterFormStateCopyWithImpl<$Res, $Val extends PrinterFormState>
|
|||||||
Object? failureOrCreateSuccess = null,
|
Object? failureOrCreateSuccess = null,
|
||||||
Object? failureOrUpdateSuccess = null,
|
Object? failureOrUpdateSuccess = null,
|
||||||
Object? failureOrDeleteSuccess = null,
|
Object? failureOrDeleteSuccess = null,
|
||||||
|
Object? failureOrPrintTest = null,
|
||||||
Object? isCreating = freezed,
|
Object? isCreating = freezed,
|
||||||
Object? isUpdate = freezed,
|
Object? isUpdate = freezed,
|
||||||
Object? isDeleting = freezed,
|
Object? isDeleting = freezed,
|
||||||
|
Object? isPrinting = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -1608,6 +1865,10 @@ class _$PrinterFormStateCopyWithImpl<$Res, $Val extends PrinterFormState>
|
|||||||
? _value.failureOrDeleteSuccess
|
? _value.failureOrDeleteSuccess
|
||||||
: failureOrDeleteSuccess // ignore: cast_nullable_to_non_nullable
|
: failureOrDeleteSuccess // ignore: cast_nullable_to_non_nullable
|
||||||
as Option<Either<PrinterFailure, Unit>>,
|
as Option<Either<PrinterFailure, Unit>>,
|
||||||
|
failureOrPrintTest: null == failureOrPrintTest
|
||||||
|
? _value.failureOrPrintTest
|
||||||
|
: failureOrPrintTest // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<Either<PrinterFailure, bool>>,
|
||||||
isCreating: freezed == isCreating
|
isCreating: freezed == isCreating
|
||||||
? _value.isCreating
|
? _value.isCreating
|
||||||
: isCreating // ignore: cast_nullable_to_non_nullable
|
: isCreating // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -1620,6 +1881,10 @@ class _$PrinterFormStateCopyWithImpl<$Res, $Val extends PrinterFormState>
|
|||||||
? _value.isDeleting
|
? _value.isDeleting
|
||||||
: isDeleting // ignore: cast_nullable_to_non_nullable
|
: isDeleting // ignore: cast_nullable_to_non_nullable
|
||||||
as dynamic,
|
as dynamic,
|
||||||
|
isPrinting: freezed == isPrinting
|
||||||
|
? _value.isPrinting
|
||||||
|
: isPrinting // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -1644,9 +1909,11 @@ abstract class _$$PrinterFormStateImplCopyWith<$Res>
|
|||||||
Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
||||||
Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
||||||
Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
||||||
|
Option<Either<PrinterFailure, bool>> failureOrPrintTest,
|
||||||
dynamic isCreating,
|
dynamic isCreating,
|
||||||
dynamic isUpdate,
|
dynamic isUpdate,
|
||||||
dynamic isDeleting,
|
dynamic isDeleting,
|
||||||
|
dynamic isPrinting,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1672,9 +1939,11 @@ class __$$PrinterFormStateImplCopyWithImpl<$Res>
|
|||||||
Object? failureOrCreateSuccess = null,
|
Object? failureOrCreateSuccess = null,
|
||||||
Object? failureOrUpdateSuccess = null,
|
Object? failureOrUpdateSuccess = null,
|
||||||
Object? failureOrDeleteSuccess = null,
|
Object? failureOrDeleteSuccess = null,
|
||||||
|
Object? failureOrPrintTest = null,
|
||||||
Object? isCreating = freezed,
|
Object? isCreating = freezed,
|
||||||
Object? isUpdate = freezed,
|
Object? isUpdate = freezed,
|
||||||
Object? isDeleting = freezed,
|
Object? isDeleting = freezed,
|
||||||
|
Object? isPrinting = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$PrinterFormStateImpl(
|
_$PrinterFormStateImpl(
|
||||||
@@ -1710,9 +1979,14 @@ class __$$PrinterFormStateImplCopyWithImpl<$Res>
|
|||||||
? _value.failureOrDeleteSuccess
|
? _value.failureOrDeleteSuccess
|
||||||
: failureOrDeleteSuccess // ignore: cast_nullable_to_non_nullable
|
: failureOrDeleteSuccess // ignore: cast_nullable_to_non_nullable
|
||||||
as Option<Either<PrinterFailure, Unit>>,
|
as Option<Either<PrinterFailure, Unit>>,
|
||||||
|
failureOrPrintTest: null == failureOrPrintTest
|
||||||
|
? _value.failureOrPrintTest
|
||||||
|
: failureOrPrintTest // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<Either<PrinterFailure, bool>>,
|
||||||
isCreating: freezed == isCreating ? _value.isCreating! : isCreating,
|
isCreating: freezed == isCreating ? _value.isCreating! : isCreating,
|
||||||
isUpdate: freezed == isUpdate ? _value.isUpdate! : isUpdate,
|
isUpdate: freezed == isUpdate ? _value.isUpdate! : isUpdate,
|
||||||
isDeleting: freezed == isDeleting ? _value.isDeleting! : isDeleting,
|
isDeleting: freezed == isDeleting ? _value.isDeleting! : isDeleting,
|
||||||
|
isPrinting: freezed == isPrinting ? _value.isPrinting! : isPrinting,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1730,9 +2004,11 @@ class _$PrinterFormStateImpl implements _PrinterFormState {
|
|||||||
required this.failureOrCreateSuccess,
|
required this.failureOrCreateSuccess,
|
||||||
required this.failureOrUpdateSuccess,
|
required this.failureOrUpdateSuccess,
|
||||||
required this.failureOrDeleteSuccess,
|
required this.failureOrDeleteSuccess,
|
||||||
|
required this.failureOrPrintTest,
|
||||||
this.isCreating = false,
|
this.isCreating = false,
|
||||||
this.isUpdate = false,
|
this.isUpdate = false,
|
||||||
this.isDeleting = false,
|
this.isDeleting = false,
|
||||||
|
this.isPrinting = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1752,6 +2028,8 @@ class _$PrinterFormStateImpl implements _PrinterFormState {
|
|||||||
@override
|
@override
|
||||||
final Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess;
|
final Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess;
|
||||||
@override
|
@override
|
||||||
|
final Option<Either<PrinterFailure, bool>> failureOrPrintTest;
|
||||||
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final dynamic isCreating;
|
final dynamic isCreating;
|
||||||
@override
|
@override
|
||||||
@@ -1760,10 +2038,13 @@ class _$PrinterFormStateImpl implements _PrinterFormState {
|
|||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final dynamic isDeleting;
|
final dynamic isDeleting;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final dynamic isPrinting;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'PrinterFormState(code: $code, name: $name, address: $address, paper: $paper, type: $type, failureOrCreateSuccess: $failureOrCreateSuccess, failureOrUpdateSuccess: $failureOrUpdateSuccess, failureOrDeleteSuccess: $failureOrDeleteSuccess, isCreating: $isCreating, isUpdate: $isUpdate, isDeleting: $isDeleting)';
|
return 'PrinterFormState(code: $code, name: $name, address: $address, paper: $paper, type: $type, failureOrCreateSuccess: $failureOrCreateSuccess, failureOrUpdateSuccess: $failureOrUpdateSuccess, failureOrDeleteSuccess: $failureOrDeleteSuccess, failureOrPrintTest: $failureOrPrintTest, isCreating: $isCreating, isUpdate: $isUpdate, isDeleting: $isDeleting, isPrinting: $isPrinting)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1782,6 +2063,8 @@ class _$PrinterFormStateImpl implements _PrinterFormState {
|
|||||||
other.failureOrUpdateSuccess == failureOrUpdateSuccess) &&
|
other.failureOrUpdateSuccess == failureOrUpdateSuccess) &&
|
||||||
(identical(other.failureOrDeleteSuccess, failureOrDeleteSuccess) ||
|
(identical(other.failureOrDeleteSuccess, failureOrDeleteSuccess) ||
|
||||||
other.failureOrDeleteSuccess == failureOrDeleteSuccess) &&
|
other.failureOrDeleteSuccess == failureOrDeleteSuccess) &&
|
||||||
|
(identical(other.failureOrPrintTest, failureOrPrintTest) ||
|
||||||
|
other.failureOrPrintTest == failureOrPrintTest) &&
|
||||||
const DeepCollectionEquality().equals(
|
const DeepCollectionEquality().equals(
|
||||||
other.isCreating,
|
other.isCreating,
|
||||||
isCreating,
|
isCreating,
|
||||||
@@ -1790,6 +2073,10 @@ class _$PrinterFormStateImpl implements _PrinterFormState {
|
|||||||
const DeepCollectionEquality().equals(
|
const DeepCollectionEquality().equals(
|
||||||
other.isDeleting,
|
other.isDeleting,
|
||||||
isDeleting,
|
isDeleting,
|
||||||
|
) &&
|
||||||
|
const DeepCollectionEquality().equals(
|
||||||
|
other.isPrinting,
|
||||||
|
isPrinting,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1804,9 +2091,11 @@ class _$PrinterFormStateImpl implements _PrinterFormState {
|
|||||||
failureOrCreateSuccess,
|
failureOrCreateSuccess,
|
||||||
failureOrUpdateSuccess,
|
failureOrUpdateSuccess,
|
||||||
failureOrDeleteSuccess,
|
failureOrDeleteSuccess,
|
||||||
|
failureOrPrintTest,
|
||||||
const DeepCollectionEquality().hash(isCreating),
|
const DeepCollectionEquality().hash(isCreating),
|
||||||
const DeepCollectionEquality().hash(isUpdate),
|
const DeepCollectionEquality().hash(isUpdate),
|
||||||
const DeepCollectionEquality().hash(isDeleting),
|
const DeepCollectionEquality().hash(isDeleting),
|
||||||
|
const DeepCollectionEquality().hash(isPrinting),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of PrinterFormState
|
/// Create a copy of PrinterFormState
|
||||||
@@ -1831,9 +2120,11 @@ abstract class _PrinterFormState implements PrinterFormState {
|
|||||||
required final Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
required final Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
||||||
required final Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
required final Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
||||||
required final Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
required final Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
||||||
|
required final Option<Either<PrinterFailure, bool>> failureOrPrintTest,
|
||||||
final dynamic isCreating,
|
final dynamic isCreating,
|
||||||
final dynamic isUpdate,
|
final dynamic isUpdate,
|
||||||
final dynamic isDeleting,
|
final dynamic isDeleting,
|
||||||
|
final dynamic isPrinting,
|
||||||
}) = _$PrinterFormStateImpl;
|
}) = _$PrinterFormStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1853,11 +2144,15 @@ abstract class _PrinterFormState implements PrinterFormState {
|
|||||||
@override
|
@override
|
||||||
Option<Either<PrinterFailure, Unit>> get failureOrDeleteSuccess;
|
Option<Either<PrinterFailure, Unit>> get failureOrDeleteSuccess;
|
||||||
@override
|
@override
|
||||||
|
Option<Either<PrinterFailure, bool>> get failureOrPrintTest;
|
||||||
|
@override
|
||||||
dynamic get isCreating;
|
dynamic get isCreating;
|
||||||
@override
|
@override
|
||||||
dynamic get isUpdate;
|
dynamic get isUpdate;
|
||||||
@override
|
@override
|
||||||
dynamic get isDeleting;
|
dynamic get isDeleting;
|
||||||
|
@override
|
||||||
|
dynamic get isPrinting;
|
||||||
|
|
||||||
/// Create a copy of PrinterFormState
|
/// Create a copy of PrinterFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|||||||
@@ -11,4 +11,8 @@ class PrinterFormEvent with _$PrinterFormEvent {
|
|||||||
const factory PrinterFormEvent.created() = _Created;
|
const factory PrinterFormEvent.created() = _Created;
|
||||||
const factory PrinterFormEvent.updated(int id) = _Updated;
|
const factory PrinterFormEvent.updated(int id) = _Updated;
|
||||||
const factory PrinterFormEvent.deleted(int id) = _Deleted;
|
const factory PrinterFormEvent.deleted(int id) = _Deleted;
|
||||||
|
const factory PrinterFormEvent.printTest({
|
||||||
|
required String code,
|
||||||
|
required String macAccdress,
|
||||||
|
}) = _PrintTest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ class PrinterFormState with _$PrinterFormState {
|
|||||||
required Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
required Option<Either<PrinterFailure, Unit>> failureOrCreateSuccess,
|
||||||
required Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
required Option<Either<PrinterFailure, Unit>> failureOrUpdateSuccess,
|
||||||
required Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
required Option<Either<PrinterFailure, Unit>> failureOrDeleteSuccess,
|
||||||
|
required Option<Either<PrinterFailure, bool>> failureOrPrintTest,
|
||||||
@Default(false) isCreating,
|
@Default(false) isCreating,
|
||||||
@Default(false) isUpdate,
|
@Default(false) isUpdate,
|
||||||
@Default(false) isDeleting,
|
@Default(false) isDeleting,
|
||||||
|
@Default(false) isPrinting,
|
||||||
}) = _PrinterFormState;
|
}) = _PrinterFormState;
|
||||||
|
|
||||||
factory PrinterFormState.initial() => PrinterFormState(
|
factory PrinterFormState.initial() => PrinterFormState(
|
||||||
@@ -25,5 +27,6 @@ class PrinterFormState with _$PrinterFormState {
|
|||||||
failureOrCreateSuccess: none(),
|
failureOrCreateSuccess: none(),
|
||||||
failureOrUpdateSuccess: none(),
|
failureOrUpdateSuccess: none(),
|
||||||
failureOrDeleteSuccess: none(),
|
failureOrDeleteSuccess: none(),
|
||||||
|
failureOrPrintTest: none(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
|||||||
state.copyWith(
|
state.copyWith(
|
||||||
isLoadingMore: false,
|
isLoadingMore: false,
|
||||||
failureOptionProduct: optionOf(
|
failureOptionProduct: optionOf(
|
||||||
ProductFailure.dynamicErrorMessage(
|
const ProductFailure.dynamicErrorMessage(
|
||||||
'Database lokal belum siap. Silakan lakukan sinkronisasi data terlebih dahulu.',
|
'Database lokal belum siap. Silakan lakukan sinkronisasi data terlebih dahulu.',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:bloc/bloc.dart';
|
|||||||
import 'package:dartz/dartz.dart' hide Order;
|
import 'package:dartz/dartz.dart' hide Order;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart' hide Order;
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../common/data/refund_data.dart';
|
import '../../../common/data/refund_data.dart';
|
||||||
import '../../../domain/order/order.dart';
|
import '../../../domain/order/order.dart';
|
||||||
@@ -34,7 +35,15 @@ class RefundFormBloc extends Bloc<RefundFormEvent, RefundFormState> {
|
|||||||
submitted: (e) async {
|
submitted: (e) async {
|
||||||
Either<OrderFailure, Unit>? failureOrRefund;
|
Either<OrderFailure, Unit>? failureOrRefund;
|
||||||
|
|
||||||
emit(state.copyWith(isSubmitting: true, failureOrRefund: none()));
|
// Generate new idempotency key saat user intent (tap Refund)
|
||||||
|
// Kalau sedang retry, pakai key yang sama
|
||||||
|
final idempotencyKey = state.idempotencyKey ?? const Uuid().v4();
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
isSubmitting: true,
|
||||||
|
failureOrRefund: none(),
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
|
));
|
||||||
|
|
||||||
failureOrRefund = await _repository.refundOrder(
|
failureOrRefund = await _repository.refundOrder(
|
||||||
id: state.order.id,
|
id: state.order.id,
|
||||||
@@ -42,12 +51,15 @@ class RefundFormBloc extends Bloc<RefundFormEvent, RefundFormState> {
|
|||||||
? state.reason
|
? state.reason
|
||||||
: state.refundReason?.value ?? '',
|
: state.refundReason?.value ?? '',
|
||||||
refundAmount: state.order.totalAmount,
|
refundAmount: state.order.totalAmount,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
failureOrRefund: optionOf(failureOrRefund),
|
failureOrRefund: optionOf(failureOrRefund),
|
||||||
|
// Clear key on success, keep on failure for retry
|
||||||
|
idempotencyKey: failureOrRefund.isRight() ? null : idempotencyKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -692,6 +692,11 @@ mixin _$RefundFormState {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
bool get isSubmitting => throw _privateConstructorUsedError;
|
bool get isSubmitting => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Idempotency key untuk refund submission.
|
||||||
|
/// Di-generate saat user tap "Refund", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
String? get idempotencyKey => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of RefundFormState
|
/// Create a copy of RefundFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -712,6 +717,7 @@ abstract class $RefundFormStateCopyWith<$Res> {
|
|||||||
RefundReason? refundReason,
|
RefundReason? refundReason,
|
||||||
Option<Either<OrderFailure, Unit>> failureOrRefund,
|
Option<Either<OrderFailure, Unit>> failureOrRefund,
|
||||||
bool isSubmitting,
|
bool isSubmitting,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
$OrderCopyWith<$Res> get order;
|
$OrderCopyWith<$Res> get order;
|
||||||
@@ -737,6 +743,7 @@ class _$RefundFormStateCopyWithImpl<$Res, $Val extends RefundFormState>
|
|||||||
Object? refundReason = freezed,
|
Object? refundReason = freezed,
|
||||||
Object? failureOrRefund = null,
|
Object? failureOrRefund = null,
|
||||||
Object? isSubmitting = null,
|
Object? isSubmitting = null,
|
||||||
|
Object? idempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -760,6 +767,10 @@ class _$RefundFormStateCopyWithImpl<$Res, $Val extends RefundFormState>
|
|||||||
? _value.isSubmitting
|
? _value.isSubmitting
|
||||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
idempotencyKey: freezed == idempotencyKey
|
||||||
|
? _value.idempotencyKey
|
||||||
|
: idempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -791,6 +802,7 @@ abstract class _$$RefundFormStateImplCopyWith<$Res>
|
|||||||
RefundReason? refundReason,
|
RefundReason? refundReason,
|
||||||
Option<Either<OrderFailure, Unit>> failureOrRefund,
|
Option<Either<OrderFailure, Unit>> failureOrRefund,
|
||||||
bool isSubmitting,
|
bool isSubmitting,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -816,6 +828,7 @@ class __$$RefundFormStateImplCopyWithImpl<$Res>
|
|||||||
Object? refundReason = freezed,
|
Object? refundReason = freezed,
|
||||||
Object? failureOrRefund = null,
|
Object? failureOrRefund = null,
|
||||||
Object? isSubmitting = null,
|
Object? isSubmitting = null,
|
||||||
|
Object? idempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$RefundFormStateImpl(
|
_$RefundFormStateImpl(
|
||||||
@@ -839,6 +852,10 @@ class __$$RefundFormStateImplCopyWithImpl<$Res>
|
|||||||
? _value.isSubmitting
|
? _value.isSubmitting
|
||||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
idempotencyKey: freezed == idempotencyKey
|
||||||
|
? _value.idempotencyKey
|
||||||
|
: idempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -853,6 +870,7 @@ class _$RefundFormStateImpl implements _RefundFormState {
|
|||||||
this.refundReason,
|
this.refundReason,
|
||||||
required this.failureOrRefund,
|
required this.failureOrRefund,
|
||||||
this.isSubmitting = false,
|
this.isSubmitting = false,
|
||||||
|
this.idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -867,9 +885,15 @@ class _$RefundFormStateImpl implements _RefundFormState {
|
|||||||
@JsonKey()
|
@JsonKey()
|
||||||
final bool isSubmitting;
|
final bool isSubmitting;
|
||||||
|
|
||||||
|
/// Idempotency key untuk refund submission.
|
||||||
|
/// Di-generate saat user tap "Refund", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
@override
|
||||||
|
final String? idempotencyKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'RefundFormState(order: $order, reason: $reason, refundReason: $refundReason, failureOrRefund: $failureOrRefund, isSubmitting: $isSubmitting)';
|
return 'RefundFormState(order: $order, reason: $reason, refundReason: $refundReason, failureOrRefund: $failureOrRefund, isSubmitting: $isSubmitting, idempotencyKey: $idempotencyKey)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -884,7 +908,9 @@ class _$RefundFormStateImpl implements _RefundFormState {
|
|||||||
(identical(other.failureOrRefund, failureOrRefund) ||
|
(identical(other.failureOrRefund, failureOrRefund) ||
|
||||||
other.failureOrRefund == failureOrRefund) &&
|
other.failureOrRefund == failureOrRefund) &&
|
||||||
(identical(other.isSubmitting, isSubmitting) ||
|
(identical(other.isSubmitting, isSubmitting) ||
|
||||||
other.isSubmitting == isSubmitting));
|
other.isSubmitting == isSubmitting) &&
|
||||||
|
(identical(other.idempotencyKey, idempotencyKey) ||
|
||||||
|
other.idempotencyKey == idempotencyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -895,6 +921,7 @@ class _$RefundFormStateImpl implements _RefundFormState {
|
|||||||
refundReason,
|
refundReason,
|
||||||
failureOrRefund,
|
failureOrRefund,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
idempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of RefundFormState
|
/// Create a copy of RefundFormState
|
||||||
@@ -916,6 +943,7 @@ abstract class _RefundFormState implements RefundFormState {
|
|||||||
final RefundReason? refundReason,
|
final RefundReason? refundReason,
|
||||||
required final Option<Either<OrderFailure, Unit>> failureOrRefund,
|
required final Option<Either<OrderFailure, Unit>> failureOrRefund,
|
||||||
final bool isSubmitting,
|
final bool isSubmitting,
|
||||||
|
final String? idempotencyKey,
|
||||||
}) = _$RefundFormStateImpl;
|
}) = _$RefundFormStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -929,6 +957,12 @@ abstract class _RefundFormState implements RefundFormState {
|
|||||||
@override
|
@override
|
||||||
bool get isSubmitting;
|
bool get isSubmitting;
|
||||||
|
|
||||||
|
/// Idempotency key untuk refund submission.
|
||||||
|
/// Di-generate saat user tap "Refund", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
@override
|
||||||
|
String? get idempotencyKey;
|
||||||
|
|
||||||
/// Create a copy of RefundFormState
|
/// Create a copy of RefundFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ class RefundFormState with _$RefundFormState {
|
|||||||
RefundReason? refundReason,
|
RefundReason? refundReason,
|
||||||
required Option<Either<OrderFailure, Unit>> failureOrRefund,
|
required Option<Either<OrderFailure, Unit>> failureOrRefund,
|
||||||
@Default(false) bool isSubmitting,
|
@Default(false) bool isSubmitting,
|
||||||
|
/// Idempotency key untuk refund submission.
|
||||||
|
/// Di-generate saat user tap "Refund", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses.
|
||||||
|
String? idempotencyKey,
|
||||||
}) = _RefundFormState;
|
}) = _RefundFormState;
|
||||||
|
|
||||||
factory RefundFormState.initial() => RefundFormState(
|
factory RefundFormState.initial() => RefundFormState(
|
||||||
|
|||||||
@@ -67,7 +67,11 @@ class SplitBillFormBloc extends Bloc<SplitBillFormEvent, SplitBillFormState> {
|
|||||||
final item = state.pendingItems.firstWhere(
|
final item = state.pendingItems.firstWhere(
|
||||||
(item) => item.id == entry.key,
|
(item) => item.id == entry.key,
|
||||||
);
|
);
|
||||||
newTotal += item.unitPrice.toInt() * entry.value;
|
// Baris berbobot dibayar utuh: nilainya weight x harga, bukan
|
||||||
|
// quantity x harga.
|
||||||
|
newTotal += item.isWeighted
|
||||||
|
? item.totalPrice
|
||||||
|
: item.unitPrice.toInt() * entry.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
|
|||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../../common/service/unit_registry.dart';
|
||||||
|
import '../../../domain/category/category.dart';
|
||||||
|
import '../../../domain/product/product.dart';
|
||||||
|
|
||||||
|
part 'sync_setting_event.dart';
|
||||||
|
part 'sync_setting_state.dart';
|
||||||
|
part 'sync_setting_bloc.freezed.dart';
|
||||||
|
|
||||||
|
@injectable
|
||||||
|
class SyncSettingBloc extends Bloc<SyncSettingEvent, SyncSettingState> {
|
||||||
|
final IProductRepository _productRepository;
|
||||||
|
final ICategoryRepository _categoryRepository;
|
||||||
|
final UnitRegistry _unitRegistry;
|
||||||
|
|
||||||
|
SyncSettingBloc(
|
||||||
|
this._productRepository,
|
||||||
|
this._categoryRepository,
|
||||||
|
this._unitRegistry,
|
||||||
|
) : super(SyncSettingState.initial()) {
|
||||||
|
on<SyncSettingEvent>(_onSyncSettingEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSyncSettingEvent(
|
||||||
|
SyncSettingEvent event,
|
||||||
|
Emitter<SyncSettingState> emit,
|
||||||
|
) {
|
||||||
|
return event.map(
|
||||||
|
loadStats: (e) => _onLoadStats(emit),
|
||||||
|
syncAllData: (e) => _onSyncAllData(emit),
|
||||||
|
clearAllData: (e) => _onClearAllData(emit),
|
||||||
|
syncProducts: (e) => _onSyncProducts(emit),
|
||||||
|
syncCategories: (e) => _onSyncCategories(emit),
|
||||||
|
clearCategories: (e) => _onClearCategories(emit),
|
||||||
|
clearProducts: (e) => _onClearProducts(emit),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onLoadStats(Emitter<SyncSettingState> emit) async {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: true,
|
||||||
|
failureOptionProduct: none(),
|
||||||
|
failureOptionCategory: none(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final productStats = await _productRepository.getDatabaseStats();
|
||||||
|
final categoryStats = await _categoryRepository.getDatabaseStats();
|
||||||
|
|
||||||
|
productStats.fold(
|
||||||
|
(failure) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
failureOptionProduct: optionOf(failure),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(data) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
productStats: data,
|
||||||
|
failureOptionProduct: none(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
categoryStats.fold(
|
||||||
|
(failure) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
failureOptionCategory: optionOf(failure),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(data) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
categoryStats: data,
|
||||||
|
failureOptionCategory: none(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSyncAllData(Emitter<SyncSettingState> emit) async {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isSyncing: true,
|
||||||
|
failureOptionSyncCategory: none(),
|
||||||
|
failureOptionSyncProduct: none(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Satuan lebih dulu: produk timbangan butuh singkatannya untuk bisa
|
||||||
|
// ditulis "4,2 ons" begitu selesai disinkronkan.
|
||||||
|
await _unitRegistry.load(forceRefresh: true);
|
||||||
|
|
||||||
|
// Sync categories first
|
||||||
|
final categoryResult = await _categoryRepository.syncAllCategories();
|
||||||
|
|
||||||
|
bool categorySuccess = false;
|
||||||
|
categoryResult.fold(
|
||||||
|
(failure) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(failureOptionSyncCategory: optionOf(left(failure))),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
categorySuccess = true;
|
||||||
|
emit(
|
||||||
|
state.copyWith(failureOptionSyncCategory: optionOf(right(success))),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only sync products if categories synced successfully
|
||||||
|
if (categorySuccess) {
|
||||||
|
final productResult = await _productRepository.syncAllProducts();
|
||||||
|
|
||||||
|
productResult.fold(
|
||||||
|
(failure) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isSyncing: false,
|
||||||
|
failureOptionSyncProduct: optionOf(left(failure)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isSyncing: false,
|
||||||
|
failureOptionSyncProduct: optionOf(right(success)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emit(state.copyWith(isSyncing: false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload stats after sync
|
||||||
|
add(const SyncSettingEvent.loadStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onClearAllData(Emitter<SyncSettingState> emit) async {
|
||||||
|
emit(state.copyWith(isLoading: true));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Clear products and categories
|
||||||
|
await _productRepository.clearAllProducts();
|
||||||
|
await _categoryRepository.clearAllCategories();
|
||||||
|
|
||||||
|
// Clear caches
|
||||||
|
_productRepository.clearCache();
|
||||||
|
_categoryRepository.clearCache();
|
||||||
|
|
||||||
|
emit(state.copyWith(isLoading: false));
|
||||||
|
|
||||||
|
// Reload stats after clearing
|
||||||
|
add(const SyncSettingEvent.loadStats());
|
||||||
|
} catch (e) {
|
||||||
|
emit(state.copyWith(isLoading: false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSyncProducts(Emitter<SyncSettingState> emit) async {
|
||||||
|
emit(state.copyWith(isLoading: true, failureOptionSyncProduct: none()));
|
||||||
|
|
||||||
|
await _unitRegistry.load(forceRefresh: true);
|
||||||
|
|
||||||
|
final result = await _productRepository.syncAllProducts();
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
failureOptionSyncProduct: optionOf(left(failure)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
failureOptionSyncProduct: optionOf(right(success)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reload stats after sync
|
||||||
|
add(const SyncSettingEvent.loadStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSyncCategories(Emitter<SyncSettingState> emit) async {
|
||||||
|
emit(state.copyWith(isLoading: true, failureOptionSyncCategory: none()));
|
||||||
|
|
||||||
|
final result = await _categoryRepository.syncAllCategories();
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
failureOptionSyncCategory: optionOf(left(failure)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(success) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
failureOptionSyncCategory: optionOf(right(success)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reload stats after sync
|
||||||
|
add(const SyncSettingEvent.loadStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onClearCategories(Emitter<SyncSettingState> emit) async {
|
||||||
|
await _categoryRepository.clearAllCategories();
|
||||||
|
_categoryRepository.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onClearProducts(Emitter<SyncSettingState> emit) async {
|
||||||
|
await _productRepository.clearAllProducts();
|
||||||
|
_productRepository.clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
part of 'sync_setting_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SyncSettingEvent with _$SyncSettingEvent {
|
||||||
|
const factory SyncSettingEvent.loadStats() = _LoadStats;
|
||||||
|
const factory SyncSettingEvent.syncAllData() = _SyncAllData;
|
||||||
|
const factory SyncSettingEvent.clearAllData() = _ClearAllData;
|
||||||
|
const factory SyncSettingEvent.syncProducts() = _SyncProducts;
|
||||||
|
const factory SyncSettingEvent.syncCategories() = _SyncCategories;
|
||||||
|
const factory SyncSettingEvent.clearCategories() = _ClearCategories;
|
||||||
|
const factory SyncSettingEvent.clearProducts() = _ClearProducts;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
part of 'sync_setting_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SyncSettingState with _$SyncSettingState {
|
||||||
|
factory SyncSettingState({
|
||||||
|
required Map<String, dynamic> productStats,
|
||||||
|
required Map<String, dynamic> categoryStats,
|
||||||
|
required Option<CategoryFailure> failureOptionCategory,
|
||||||
|
required Option<ProductFailure> failureOptionProduct,
|
||||||
|
required Option<Either<ProductFailure, String>> failureOptionSyncProduct,
|
||||||
|
required Option<Either<CategoryFailure, String>> failureOptionSyncCategory,
|
||||||
|
@Default(false) bool isSyncing,
|
||||||
|
@Default(false) bool isLoading,
|
||||||
|
}) = _SyncSettingState;
|
||||||
|
|
||||||
|
factory SyncSettingState.initial() => SyncSettingState(
|
||||||
|
productStats: {},
|
||||||
|
categoryStats: {},
|
||||||
|
failureOptionCategory: none(),
|
||||||
|
failureOptionProduct: none(),
|
||||||
|
failureOptionSyncProduct: none(),
|
||||||
|
failureOptionSyncCategory: none(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dartz/dartz.dart' hide Order;
|
import 'package:dartz/dartz.dart' hide Order;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart' hide Order;
|
import 'package:injectable/injectable.dart' hide Order;
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../common/types/void_type.dart';
|
import '../../../common/types/void_type.dart';
|
||||||
import '../../../domain/order/order.dart';
|
import '../../../domain/order/order.dart';
|
||||||
@@ -49,10 +51,14 @@ class VoidFormBloc extends Bloc<VoidFormEvent, VoidFormState> {
|
|||||||
// Recalculate total price
|
// Recalculate total price
|
||||||
int newTotal = 0;
|
int newTotal = 0;
|
||||||
for (var entry in newQuantities.entries) {
|
for (var entry in newQuantities.entries) {
|
||||||
final item = state.pendingItems.firstWhere(
|
final item = state.pendingItems.firstWhereOrNull(
|
||||||
(item) => item.id == entry.key,
|
(item) => item.id == entry.key,
|
||||||
);
|
);
|
||||||
newTotal += item.unitPrice.toInt() * entry.value;
|
if (item == null) continue;
|
||||||
|
// Baris berbobot di-void utuh; rupiahnya sudah weight x harga.
|
||||||
|
newTotal += item.isWeighted
|
||||||
|
? item.totalPrice
|
||||||
|
: item.unitPrice.toInt() * entry.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
@@ -71,21 +77,31 @@ class VoidFormBloc extends Bloc<VoidFormEvent, VoidFormState> {
|
|||||||
|
|
||||||
if (state.voidType.isItem) {
|
if (state.voidType.isItem) {
|
||||||
state.selectedItemQuantities.forEach((itemId, voidQty) {
|
state.selectedItemQuantities.forEach((itemId, voidQty) {
|
||||||
final originalItem = state.order.orderItems.firstWhere(
|
final originalItem = state.order.orderItems.firstWhereOrNull(
|
||||||
(item) => item.id == itemId,
|
(item) => item.id == itemId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (originalItem == null) return;
|
||||||
|
|
||||||
voidItems.add(
|
voidItems.add(
|
||||||
OrderItem(
|
OrderItem(
|
||||||
id: originalItem.id,
|
id: originalItem.id,
|
||||||
orderId: originalItem.orderId,
|
orderId: originalItem.orderId,
|
||||||
productId: originalItem.productId,
|
productId: originalItem.productId,
|
||||||
productName: originalItem.productName,
|
productName: originalItem.productName,
|
||||||
|
categoryId: originalItem.categoryId,
|
||||||
|
categoryName: originalItem.status,
|
||||||
productVariantId: originalItem.productVariantId,
|
productVariantId: originalItem.productVariantId,
|
||||||
productVariantName: originalItem.productVariantName,
|
productVariantName: originalItem.productVariantName,
|
||||||
quantity: voidQty,
|
quantity: voidQty,
|
||||||
unitPrice: originalItem.unitPrice,
|
unitPrice: originalItem.unitPrice,
|
||||||
totalPrice: (originalItem.unitPrice) * voidQty,
|
totalPrice: originalItem.isWeighted
|
||||||
|
? originalItem.totalPrice
|
||||||
|
: (originalItem.unitPrice) * voidQty,
|
||||||
|
weight: originalItem.weight,
|
||||||
|
unitId: originalItem.unitId,
|
||||||
|
unitName: originalItem.unitName,
|
||||||
|
unitAbbreviation: originalItem.unitAbbreviation,
|
||||||
modifiers: originalItem.modifiers,
|
modifiers: originalItem.modifiers,
|
||||||
notes: originalItem.notes,
|
notes: originalItem.notes,
|
||||||
status: originalItem.status,
|
status: originalItem.status,
|
||||||
@@ -93,18 +109,28 @@ class VoidFormBloc extends Bloc<VoidFormEvent, VoidFormState> {
|
|||||||
updatedAt: originalItem.updatedAt,
|
updatedAt: originalItem.updatedAt,
|
||||||
printerType: originalItem.printerType,
|
printerType: originalItem.printerType,
|
||||||
paidQuantity: originalItem.paidQuantity,
|
paidQuantity: originalItem.paidQuantity,
|
||||||
|
printToChecker: originalItem.printToChecker,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(isSubmitting: true, failureOrVoid: none()));
|
// Generate new idempotency key saat user intent (tap Void)
|
||||||
|
// Kalau sedang retry, pakai key yang sama
|
||||||
|
final idempotencyKey = state.idempotencyKey ?? const Uuid().v4();
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
isSubmitting: true,
|
||||||
|
failureOrVoid: none(),
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
|
));
|
||||||
|
|
||||||
failureOrVoid = await _repository.voidOrder(
|
failureOrVoid = await _repository.voidOrder(
|
||||||
orderId: state.order.id,
|
orderId: state.order.id,
|
||||||
reason: state.voidReason ?? '',
|
reason: state.voidReason ?? '',
|
||||||
orderItems: voidItems,
|
orderItems: voidItems,
|
||||||
type: state.voidType.toStringType(),
|
type: state.voidType.toStringType(),
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
@@ -112,6 +138,8 @@ class VoidFormBloc extends Bloc<VoidFormEvent, VoidFormState> {
|
|||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
failureOrVoid: optionOf(failureOrVoid),
|
failureOrVoid: optionOf(failureOrVoid),
|
||||||
voidItems: state.voidType.isItem ? voidItems : state.pendingItems,
|
voidItems: state.voidType.isItem ? voidItems : state.pendingItems,
|
||||||
|
// Clear key on success, keep on failure for retry
|
||||||
|
idempotencyKey: failureOrVoid.isRight() ? null : idempotencyKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1256,6 +1256,11 @@ mixin _$VoidFormState {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
bool get isSubmitting => throw _privateConstructorUsedError;
|
bool get isSubmitting => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Idempotency key untuk void submission.
|
||||||
|
/// Di-generate saat user tap "Void", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses atau clearState.
|
||||||
|
String? get idempotencyKey => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of VoidFormState
|
/// Create a copy of VoidFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -1280,6 +1285,7 @@ abstract class $VoidFormStateCopyWith<$Res> {
|
|||||||
int totalPriceVoid,
|
int totalPriceVoid,
|
||||||
Option<Either<OrderFailure, Unit>> failureOrVoid,
|
Option<Either<OrderFailure, Unit>> failureOrVoid,
|
||||||
bool isSubmitting,
|
bool isSubmitting,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
$OrderCopyWith<$Res> get order;
|
$OrderCopyWith<$Res> get order;
|
||||||
@@ -1309,6 +1315,7 @@ class _$VoidFormStateCopyWithImpl<$Res, $Val extends VoidFormState>
|
|||||||
Object? totalPriceVoid = null,
|
Object? totalPriceVoid = null,
|
||||||
Object? failureOrVoid = null,
|
Object? failureOrVoid = null,
|
||||||
Object? isSubmitting = null,
|
Object? isSubmitting = null,
|
||||||
|
Object? idempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -1348,6 +1355,10 @@ class _$VoidFormStateCopyWithImpl<$Res, $Val extends VoidFormState>
|
|||||||
? _value.isSubmitting
|
? _value.isSubmitting
|
||||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
idempotencyKey: freezed == idempotencyKey
|
||||||
|
? _value.idempotencyKey
|
||||||
|
: idempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -1383,6 +1394,7 @@ abstract class _$$VoidFormStateImplCopyWith<$Res>
|
|||||||
int totalPriceVoid,
|
int totalPriceVoid,
|
||||||
Option<Either<OrderFailure, Unit>> failureOrVoid,
|
Option<Either<OrderFailure, Unit>> failureOrVoid,
|
||||||
bool isSubmitting,
|
bool isSubmitting,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1412,6 +1424,7 @@ class __$$VoidFormStateImplCopyWithImpl<$Res>
|
|||||||
Object? totalPriceVoid = null,
|
Object? totalPriceVoid = null,
|
||||||
Object? failureOrVoid = null,
|
Object? failureOrVoid = null,
|
||||||
Object? isSubmitting = null,
|
Object? isSubmitting = null,
|
||||||
|
Object? idempotencyKey = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$VoidFormStateImpl(
|
_$VoidFormStateImpl(
|
||||||
@@ -1451,6 +1464,10 @@ class __$$VoidFormStateImplCopyWithImpl<$Res>
|
|||||||
? _value.isSubmitting
|
? _value.isSubmitting
|
||||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
idempotencyKey: freezed == idempotencyKey
|
||||||
|
? _value.idempotencyKey
|
||||||
|
: idempotencyKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1469,6 +1486,7 @@ class _$VoidFormStateImpl implements _VoidFormState {
|
|||||||
required this.totalPriceVoid,
|
required this.totalPriceVoid,
|
||||||
required this.failureOrVoid,
|
required this.failureOrVoid,
|
||||||
this.isSubmitting = false,
|
this.isSubmitting = false,
|
||||||
|
this.idempotencyKey,
|
||||||
}) : _pendingItems = pendingItems,
|
}) : _pendingItems = pendingItems,
|
||||||
_voidItems = voidItems,
|
_voidItems = voidItems,
|
||||||
_selectedItemQuantities = selectedItemQuantities;
|
_selectedItemQuantities = selectedItemQuantities;
|
||||||
@@ -1512,9 +1530,15 @@ class _$VoidFormStateImpl implements _VoidFormState {
|
|||||||
@JsonKey()
|
@JsonKey()
|
||||||
final bool isSubmitting;
|
final bool isSubmitting;
|
||||||
|
|
||||||
|
/// Idempotency key untuk void submission.
|
||||||
|
/// Di-generate saat user tap "Void", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses atau clearState.
|
||||||
|
@override
|
||||||
|
final String? idempotencyKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'VoidFormState(order: $order, pendingItems: $pendingItems, voidItems: $voidItems, voidType: $voidType, selectedItemQuantities: $selectedItemQuantities, voidReason: $voidReason, totalPriceVoid: $totalPriceVoid, failureOrVoid: $failureOrVoid, isSubmitting: $isSubmitting)';
|
return 'VoidFormState(order: $order, pendingItems: $pendingItems, voidItems: $voidItems, voidType: $voidType, selectedItemQuantities: $selectedItemQuantities, voidReason: $voidReason, totalPriceVoid: $totalPriceVoid, failureOrVoid: $failureOrVoid, isSubmitting: $isSubmitting, idempotencyKey: $idempotencyKey)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1544,7 +1568,9 @@ class _$VoidFormStateImpl implements _VoidFormState {
|
|||||||
(identical(other.failureOrVoid, failureOrVoid) ||
|
(identical(other.failureOrVoid, failureOrVoid) ||
|
||||||
other.failureOrVoid == failureOrVoid) &&
|
other.failureOrVoid == failureOrVoid) &&
|
||||||
(identical(other.isSubmitting, isSubmitting) ||
|
(identical(other.isSubmitting, isSubmitting) ||
|
||||||
other.isSubmitting == isSubmitting));
|
other.isSubmitting == isSubmitting) &&
|
||||||
|
(identical(other.idempotencyKey, idempotencyKey) ||
|
||||||
|
other.idempotencyKey == idempotencyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1559,6 +1585,7 @@ class _$VoidFormStateImpl implements _VoidFormState {
|
|||||||
totalPriceVoid,
|
totalPriceVoid,
|
||||||
failureOrVoid,
|
failureOrVoid,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
idempotencyKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of VoidFormState
|
/// Create a copy of VoidFormState
|
||||||
@@ -1581,6 +1608,7 @@ abstract class _VoidFormState implements VoidFormState {
|
|||||||
required final int totalPriceVoid,
|
required final int totalPriceVoid,
|
||||||
required final Option<Either<OrderFailure, Unit>> failureOrVoid,
|
required final Option<Either<OrderFailure, Unit>> failureOrVoid,
|
||||||
final bool isSubmitting,
|
final bool isSubmitting,
|
||||||
|
final String? idempotencyKey,
|
||||||
}) = _$VoidFormStateImpl;
|
}) = _$VoidFormStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1602,6 +1630,12 @@ abstract class _VoidFormState implements VoidFormState {
|
|||||||
@override
|
@override
|
||||||
bool get isSubmitting;
|
bool get isSubmitting;
|
||||||
|
|
||||||
|
/// Idempotency key untuk void submission.
|
||||||
|
/// Di-generate saat user tap "Void", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses atau clearState.
|
||||||
|
@override
|
||||||
|
String? get idempotencyKey;
|
||||||
|
|
||||||
/// Create a copy of VoidFormState
|
/// Create a copy of VoidFormState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ class VoidFormState with _$VoidFormState {
|
|||||||
required int totalPriceVoid,
|
required int totalPriceVoid,
|
||||||
required Option<Either<OrderFailure, Unit>> failureOrVoid,
|
required Option<Either<OrderFailure, Unit>> failureOrVoid,
|
||||||
@Default(false) bool isSubmitting,
|
@Default(false) bool isSubmitting,
|
||||||
|
/// Idempotency key untuk void submission.
|
||||||
|
/// Di-generate saat user tap "Void", dipakai ulang saat retry,
|
||||||
|
/// di-clear setelah sukses atau clearState.
|
||||||
|
String? idempotencyKey,
|
||||||
}) = _VoidFormState;
|
}) = _VoidFormState;
|
||||||
|
|
||||||
factory VoidFormState.initial() => VoidFormState(
|
factory VoidFormState.initial() => VoidFormState(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import 'interceptors/bad_network_interceptor.dart';
|
|||||||
import 'interceptors/bad_request_interceptor.dart';
|
import 'interceptors/bad_request_interceptor.dart';
|
||||||
import 'interceptors/connection_timeout_interceptor.dart';
|
import 'interceptors/connection_timeout_interceptor.dart';
|
||||||
import 'interceptors/crashlytic_interceptor.dart';
|
import 'interceptors/crashlytic_interceptor.dart';
|
||||||
|
import 'interceptors/idempotency_interceptor.dart';
|
||||||
import 'interceptors/internal_server_interceptor.dart';
|
import 'interceptors/internal_server_interceptor.dart';
|
||||||
import 'interceptors/not_found_interceptor.dart';
|
import 'interceptors/not_found_interceptor.dart';
|
||||||
import 'interceptors/unauthorized_interceptor.dart';
|
import 'interceptors/unauthorized_interceptor.dart';
|
||||||
@@ -30,6 +31,7 @@ class ApiClient {
|
|||||||
_dio.options.connectTimeout = const Duration(seconds: 20);
|
_dio.options.connectTimeout = const Duration(seconds: 20);
|
||||||
_dio.options.validateStatus = (status) =>
|
_dio.options.validateStatus = (status) =>
|
||||||
status != null && status >= 200 && status < 500;
|
status != null && status >= 200 && status < 500;
|
||||||
|
_dio.interceptors.add(IdempotencyInterceptor(_dio));
|
||||||
_dio.interceptors.add(BadNetworkErrorInterceptor());
|
_dio.interceptors.add(BadNetworkErrorInterceptor());
|
||||||
_dio.interceptors.add(BadRequestErrorInterceptor());
|
_dio.interceptors.add(BadRequestErrorInterceptor());
|
||||||
_dio.interceptors.add(InternalServerErrorInterceptor());
|
_dio.interceptors.add(InternalServerErrorInterceptor());
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
|||||||
class CrashlyticsInterceptor extends Interceptor {
|
class CrashlyticsInterceptor extends Interceptor {
|
||||||
@override
|
@override
|
||||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||||
// Log semua error ke Crashlytics
|
|
||||||
FirebaseCrashlytics.instance.recordError(
|
FirebaseCrashlytics.instance.recordError(
|
||||||
err,
|
err,
|
||||||
err.stackTrace,
|
err.stackTrace,
|
||||||
@@ -46,6 +45,32 @@ class CrashlyticsInterceptor extends Interceptor {
|
|||||||
'Status Code: ${err.response?.statusCode ?? 'N/A'}',
|
'Status Code: ${err.response?.statusCode ?? 'N/A'}',
|
||||||
'Error Type: ${err.type.name}',
|
'Error Type: ${err.type.name}',
|
||||||
'Message: ${err.message ?? 'No message'}',
|
'Message: ${err.message ?? 'No message'}',
|
||||||
|
// Tambahan
|
||||||
|
'Request Data: ${_sanitizeData(err.requestOptions.data)}',
|
||||||
|
'Response Body: ${err.response?.data ?? 'No response'}',
|
||||||
|
'Headers: ${err.requestOptions.headers.entries.where((e) => e.key.toLowerCase() != 'authorization').toList()}',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hindari log data sensitif
|
||||||
|
dynamic _sanitizeData(dynamic data) {
|
||||||
|
if (data == null) return 'No data';
|
||||||
|
if (data is Map) {
|
||||||
|
final sanitized = Map.from(data);
|
||||||
|
const sensitiveKeys = [
|
||||||
|
'password',
|
||||||
|
'token',
|
||||||
|
'secret',
|
||||||
|
'pin',
|
||||||
|
'card_number',
|
||||||
|
];
|
||||||
|
for (final key in sensitiveKeys) {
|
||||||
|
if (sanitized.containsKey(key)) {
|
||||||
|
sanitized[key] = '***';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
/// Interceptor yang secara otomatis menambahkan header X-Idempotency-Key
|
||||||
|
/// untuk endpoint-endpoint kritis yang memerlukan idempotency.
|
||||||
|
///
|
||||||
|
/// Key sebaiknya di-generate di level BLoC/Cubit saat user trigger action,
|
||||||
|
/// lalu dikirim via headers. Interceptor ini hanya berfungsi sebagai fallback
|
||||||
|
/// jika key belum di-set.
|
||||||
|
///
|
||||||
|
/// Jika response 409 dengan error code `request_in_progress`, interceptor
|
||||||
|
/// akan retry setelah delay 1-2 detik.
|
||||||
|
class IdempotencyInterceptor extends Interceptor {
|
||||||
|
static const _headerKey = 'X-Idempotency-Key';
|
||||||
|
static const _replayHeader = 'X-Idempotent-Replay';
|
||||||
|
|
||||||
|
// Endpoints that require idempotency keys (exact match)
|
||||||
|
static const _idempotentPaths = [
|
||||||
|
'/api/v1/payments',
|
||||||
|
'/api/v1/orders/void',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Endpoints that require idempotency keys (pattern match)
|
||||||
|
static final _idempotentPathPatterns = [
|
||||||
|
RegExp(r'/api/v1/orders/.+/add-items$'),
|
||||||
|
RegExp(r'/api/v1/orders/.+/refund$'),
|
||||||
|
RegExp(r'/api/v1/payments/.+/refund$'),
|
||||||
|
];
|
||||||
|
|
||||||
|
static const _maxRetries = 3;
|
||||||
|
static const _retryDelay = Duration(seconds: 2);
|
||||||
|
|
||||||
|
final Dio _dio;
|
||||||
|
|
||||||
|
IdempotencyInterceptor(this._dio);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||||
|
if (options.method == 'POST' && _requiresIdempotencyKey(options.path)) {
|
||||||
|
// Gunakan key yang sudah di-set dari BLoC, atau generate baru sebagai fallback
|
||||||
|
options.headers[_headerKey] ??= const Uuid().v4();
|
||||||
|
}
|
||||||
|
handler.next(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||||
|
// Log jika response adalah replay dari request sebelumnya
|
||||||
|
final isReplay = response.headers.value(_replayHeader);
|
||||||
|
if (isReplay == 'true') {
|
||||||
|
// Response ini adalah hasil dari request pertama yang sudah diproses.
|
||||||
|
// Treat as success — tidak perlu perlakuan khusus.
|
||||||
|
}
|
||||||
|
handler.next(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||||
|
final response = err.response;
|
||||||
|
final options = err.requestOptions;
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
handler.next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 409 Conflict with request_in_progress
|
||||||
|
if (response.statusCode == 409 &&
|
||||||
|
_isRequestInProgress(response.data) &&
|
||||||
|
_requiresIdempotencyKey(options.path)) {
|
||||||
|
final retryCount = options.extra['_idempotency_retry_count'] ?? 0;
|
||||||
|
|
||||||
|
if (retryCount < _maxRetries) {
|
||||||
|
await Future.delayed(_retryDelay);
|
||||||
|
|
||||||
|
options.extra['_idempotency_retry_count'] = retryCount + 1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final retryResponse = await _dio.fetch(options);
|
||||||
|
handler.resolve(retryResponse);
|
||||||
|
} on DioException catch (e) {
|
||||||
|
handler.next(e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _requiresIdempotencyKey(String path) {
|
||||||
|
if (_idempotentPaths.any((p) => path.endsWith(p))) return true;
|
||||||
|
if (_idempotentPathPatterns.any((r) => r.hasMatch(path))) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isRequestInProgress(dynamic data) {
|
||||||
|
if (data is Map<String, dynamic>) {
|
||||||
|
final errorCode = data['error_code'] ?? data['code'] ?? '';
|
||||||
|
return errorCode == 'request_in_progress';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
|
import '../../env.dart';
|
||||||
|
import '../../injection.dart';
|
||||||
|
|
||||||
class AppConstant {
|
class AppConstant {
|
||||||
static const String appName = "Apskel POS";
|
static const String appName = "Apskel POS";
|
||||||
static const String dbName = "apskel_pos.db";
|
static String dbName = getIt<Env>().dbName;
|
||||||
static const int cacheExpire = 10; // in minutes
|
static const int cacheExpire = 10; // in minutes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ class LocalStorageKey {
|
|||||||
static const String token = 'token';
|
static const String token = 'token';
|
||||||
static const String user = 'user';
|
static const String user = 'user';
|
||||||
static const String outlet = 'outlet';
|
static const String outlet = 'outlet';
|
||||||
|
static const String units = 'units';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class DatabaseHelper {
|
|||||||
|
|
||||||
return await openDatabase(
|
return await openDatabase(
|
||||||
path,
|
path,
|
||||||
version: 2, // Updated version for categories table
|
version: 3,
|
||||||
onCreate: _onCreate,
|
onCreate: _onCreate,
|
||||||
onUpgrade: _onUpgrade,
|
onUpgrade: _onUpgrade,
|
||||||
);
|
);
|
||||||
@@ -38,10 +38,15 @@ class DatabaseHelper {
|
|||||||
business_type TEXT,
|
business_type TEXT,
|
||||||
image_url TEXT,
|
image_url TEXT,
|
||||||
printer_type TEXT,
|
printer_type TEXT,
|
||||||
|
print_to_checker INTEGER DEFAULT 0,
|
||||||
metadata TEXT,
|
metadata TEXT,
|
||||||
is_active INTEGER,
|
is_active INTEGER,
|
||||||
created_at TEXT,
|
created_at TEXT,
|
||||||
updated_at TEXT
|
updated_at TEXT,
|
||||||
|
sell_by TEXT DEFAULT 'unit',
|
||||||
|
unit_id TEXT,
|
||||||
|
unit_name TEXT,
|
||||||
|
unit_abbreviation TEXT
|
||||||
)
|
)
|
||||||
''');
|
''');
|
||||||
|
|
||||||
@@ -68,6 +73,7 @@ class DatabaseHelper {
|
|||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
business_type TEXT,
|
business_type TEXT,
|
||||||
|
sort_order INTEGER DEFAULT 0,
|
||||||
metadata TEXT,
|
metadata TEXT,
|
||||||
is_active INTEGER DEFAULT 1,
|
is_active INTEGER DEFAULT 1,
|
||||||
created_at TEXT,
|
created_at TEXT,
|
||||||
@@ -109,9 +115,20 @@ class DatabaseHelper {
|
|||||||
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
||||||
if (oldVersion < 2) {
|
if (oldVersion < 2) {
|
||||||
await db.execute(
|
await db.execute(
|
||||||
'ALTER TABLE categories ADD COLUMN "order" INTEGER DEFAULT 0',
|
'ALTER TABLE products ADD COLUMN print_to_checker INTEGER DEFAULT 0',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Produk timbangan: cara jual dan satuan ikut disimpan lokal supaya POS
|
||||||
|
// tetap bisa menampilkan "4,2 ons" saat offline.
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
await db.execute(
|
||||||
|
"ALTER TABLE products ADD COLUMN sell_by TEXT DEFAULT 'unit'",
|
||||||
|
);
|
||||||
|
await db.execute('ALTER TABLE products ADD COLUMN unit_id TEXT');
|
||||||
|
await db.execute('ALTER TABLE products ADD COLUMN unit_name TEXT');
|
||||||
|
await db.execute('ALTER TABLE products ADD COLUMN unit_abbreviation TEXT');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
|
@module
|
||||||
|
abstract class FirebaseDi {
|
||||||
|
@lazySingleton
|
||||||
|
FirebaseMessaging get messaging => FirebaseMessaging.instance;
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
FlutterLocalNotificationsPlugin get localNotifications =>
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
DeviceInfoPlugin get deviceInfo => DeviceInfoPlugin();
|
||||||
|
|
||||||
|
@preResolve
|
||||||
|
Future<PackageInfo> get packageInfo => PackageInfo.fromPlatform();
|
||||||
|
}
|
||||||
@@ -13,3 +13,10 @@ extension DoubleExt on double {
|
|||||||
decimalDigits: 0,
|
decimalDigits: 0,
|
||||||
).format(this);
|
).format(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension WeightExt on double {
|
||||||
|
/// Format berat gaya Indonesia: koma desimal, tanpa nol di belakang.
|
||||||
|
/// `4.2` → `4,2`, `4.237` → `4,237`, `5.0` → `5`.
|
||||||
|
String get weightFormat =>
|
||||||
|
NumberFormat('#,##0.###', 'id').format(this);
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,3 +75,25 @@ int generateRandomNumber() {
|
|||||||
final random = Random();
|
final random = Random();
|
||||||
return 1 + random.nextInt(10000);
|
return 1 + random.nextInt(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ambil pesan validasi dari amplop error standar backend.
|
||||||
|
///
|
||||||
|
/// Untuk produk timbangan pesannya menyebut nama produk — misalnya
|
||||||
|
/// "product Ikan Tude is sold by weight and requires a weight" — jadi layak
|
||||||
|
/// ditampilkan apa adanya ke kasir alih-alih diganti pesan generik.
|
||||||
|
String? extractApiErrorCause(dynamic responseData) {
|
||||||
|
if (responseData is! Map) return null;
|
||||||
|
|
||||||
|
final errors = responseData['errors'];
|
||||||
|
if (errors is! List || errors.isEmpty) return null;
|
||||||
|
|
||||||
|
final causes = errors
|
||||||
|
.whereType<Map>()
|
||||||
|
.map((e) => (e['cause'] ?? e['message'])?.toString())
|
||||||
|
.where((cause) => cause != null && cause.isNotEmpty)
|
||||||
|
.cast<String>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (causes.isEmpty) return null;
|
||||||
|
return causes.join('\n');
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
class DeviceInfoService {
|
||||||
|
DeviceInfoService(this._deviceInfo, this._packageInfo);
|
||||||
|
|
||||||
|
final DeviceInfoPlugin _deviceInfo;
|
||||||
|
final PackageInfo _packageInfo;
|
||||||
|
|
||||||
|
String get appVersion => '${_packageInfo.version}+${_packageInfo.buildNumber}';
|
||||||
|
|
||||||
|
Future<Map<String, String>> getDevicePayload() async {
|
||||||
|
final info = await _getDeviceInfo();
|
||||||
|
return {
|
||||||
|
'device_id': info['device_id'] ?? '',
|
||||||
|
'device_name': info['device_name'] ?? '',
|
||||||
|
'device_type': info['device_type'] ?? 'mobile',
|
||||||
|
'platform': _getPlatform(),
|
||||||
|
'app_version': appVersion,
|
||||||
|
'os_version': info['os_version'] ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getPlatform() {
|
||||||
|
if (Platform.isAndroid) return 'android';
|
||||||
|
if (Platform.isIOS) return 'ios';
|
||||||
|
return 'web';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> _getDeviceInfo() async {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
final android = await _deviceInfo.androidInfo;
|
||||||
|
return {
|
||||||
|
'device_id': android.id,
|
||||||
|
'device_name': '${android.manufacturer} ${android.model}',
|
||||||
|
'device_type': _resolveDeviceType(android.model),
|
||||||
|
'os_version': 'Android ${android.version.release} (SDK ${android.version.sdkInt})',
|
||||||
|
};
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
final ios = await _deviceInfo.iosInfo;
|
||||||
|
return {
|
||||||
|
'device_id': ios.identifierForVendor ?? '',
|
||||||
|
'device_name': ios.name,
|
||||||
|
'device_type': _resolveDeviceType(ios.model),
|
||||||
|
'os_version': '${ios.systemName} ${ios.systemVersion}',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {'device_type': 'desktop'};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deteksi device_type berdasarkan nama model
|
||||||
|
/// Nilai valid: mobile | tablet | desktop
|
||||||
|
String _resolveDeviceType(String model) {
|
||||||
|
final lower = model.toLowerCase();
|
||||||
|
if (lower.contains('tablet') || lower.contains('tab') || lower.contains('ipad')) {
|
||||||
|
return 'tablet';
|
||||||
|
}
|
||||||
|
return 'mobile';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
/// Background message handler — harus top-level function
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
|
log('[FCM] Background message: ${message.messageId}');
|
||||||
|
}
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
class FcmService {
|
||||||
|
FcmService(this._messaging, this._localNotifications);
|
||||||
|
|
||||||
|
final FirebaseMessaging _messaging;
|
||||||
|
final FlutterLocalNotificationsPlugin _localNotifications;
|
||||||
|
|
||||||
|
/// Inisialisasi FCM: minta permission, set handler, ambil token
|
||||||
|
Future<void> initialize() async {
|
||||||
|
// Setup local notifications
|
||||||
|
await _setupLocalNotifications();
|
||||||
|
|
||||||
|
// Register background handler
|
||||||
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||||
|
|
||||||
|
// Minta permission notifikasi (iOS & Android 13+)
|
||||||
|
final settings = await _messaging.requestPermission(
|
||||||
|
alert: true,
|
||||||
|
badge: true,
|
||||||
|
sound: true,
|
||||||
|
provisional: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
log('[FCM] Permission status: ${settings.authorizationStatus}');
|
||||||
|
|
||||||
|
if (settings.authorizationStatus == AuthorizationStatus.authorized ||
|
||||||
|
settings.authorizationStatus == AuthorizationStatus.provisional) {
|
||||||
|
await _setupTokenHandling();
|
||||||
|
_setupForegroundHandler();
|
||||||
|
_setupOpenedAppHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setupLocalNotifications() async {
|
||||||
|
const androidSettings = AndroidInitializationSettings('@drawable/ic_notification');
|
||||||
|
const iosSettings = DarwinInitializationSettings(
|
||||||
|
requestAlertPermission: true,
|
||||||
|
requestBadgePermission: true,
|
||||||
|
requestSoundPermission: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const initSettings = InitializationSettings(
|
||||||
|
android: androidSettings,
|
||||||
|
iOS: iosSettings,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _localNotifications.initialize(
|
||||||
|
initSettings,
|
||||||
|
onDidReceiveNotificationResponse: _onNotificationTapped,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Android notification channel
|
||||||
|
const channel = AndroidNotificationChannel(
|
||||||
|
'high_importance_channel',
|
||||||
|
'High Importance Notifications',
|
||||||
|
description: 'This channel is used for important notifications.',
|
||||||
|
importance: Importance.high,
|
||||||
|
playSound: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _localNotifications
|
||||||
|
.resolvePlatformSpecificImplementation<
|
||||||
|
AndroidFlutterLocalNotificationsPlugin>()
|
||||||
|
?.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onNotificationTapped(NotificationResponse response) {
|
||||||
|
log('[FCM] Notification tapped: ${response.payload}');
|
||||||
|
// TODO: handle navigation berdasarkan payload
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setupTokenHandling() async {
|
||||||
|
final token = await getToken();
|
||||||
|
log('========================================');
|
||||||
|
log('[FCM] TOKEN: $token');
|
||||||
|
log('========================================');
|
||||||
|
// TODO: kirim token ke server jika diperlukan
|
||||||
|
|
||||||
|
_messaging.onTokenRefresh.listen((newToken) {
|
||||||
|
log('========================================');
|
||||||
|
log('[FCM] TOKEN REFRESHED: $newToken');
|
||||||
|
log('========================================');
|
||||||
|
// TODO: kirim token baru ke server jika diperlukan
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setupForegroundHandler() {
|
||||||
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
||||||
|
log('[FCM] Foreground message: ${message.messageId}');
|
||||||
|
log('[FCM] Title: ${message.notification?.title}');
|
||||||
|
log('[FCM] Body: ${message.notification?.body}');
|
||||||
|
log('[FCM] Data: ${message.data}');
|
||||||
|
|
||||||
|
// Tampilkan notifikasi lokal saat app di foreground
|
||||||
|
final notification = message.notification;
|
||||||
|
if (notification != null) {
|
||||||
|
_showLocalNotification(
|
||||||
|
id: message.hashCode,
|
||||||
|
title: notification.title ?? '',
|
||||||
|
body: notification.body ?? '',
|
||||||
|
payload: message.data.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setupOpenedAppHandler() {
|
||||||
|
// App dibuka dari notifikasi saat terminated
|
||||||
|
_messaging.getInitialMessage().then((RemoteMessage? message) {
|
||||||
|
if (message != null) {
|
||||||
|
log('[FCM] App opened from terminated via: ${message.messageId}');
|
||||||
|
_handleMessageNavigation(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// App dibuka dari notifikasi saat background
|
||||||
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
||||||
|
log('[FCM] App opened from background via: ${message.messageId}');
|
||||||
|
_handleMessageNavigation(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleMessageNavigation(RemoteMessage message) {
|
||||||
|
// TODO: tambahkan logika navigasi berdasarkan message.data
|
||||||
|
log('[FCM] Handle navigation for: ${message.data}');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showLocalNotification({
|
||||||
|
required int id,
|
||||||
|
required String title,
|
||||||
|
required String body,
|
||||||
|
String? payload,
|
||||||
|
}) async {
|
||||||
|
const androidDetails = AndroidNotificationDetails(
|
||||||
|
'high_importance_channel',
|
||||||
|
'High Importance Notifications',
|
||||||
|
channelDescription: 'This channel is used for important notifications.',
|
||||||
|
importance: Importance.max,
|
||||||
|
priority: Priority.max,
|
||||||
|
showWhen: true,
|
||||||
|
playSound: true,
|
||||||
|
enableVibration: true,
|
||||||
|
enableLights: true,
|
||||||
|
fullScreenIntent: true,
|
||||||
|
icon: 'ic_notification',
|
||||||
|
);
|
||||||
|
|
||||||
|
const iosDetails = DarwinNotificationDetails(
|
||||||
|
presentAlert: true,
|
||||||
|
presentBadge: true,
|
||||||
|
presentSound: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const details = NotificationDetails(
|
||||||
|
android: androidDetails,
|
||||||
|
iOS: iosDetails,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _localNotifications.show(id, title, body, details, payload: payload);
|
||||||
|
log('[FCM] Local notification shown: $title');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ambil FCM token perangkat
|
||||||
|
Future<String?> getToken() async {
|
||||||
|
try {
|
||||||
|
return await _messaging.getToken();
|
||||||
|
} catch (e) {
|
||||||
|
log('[FCM] Failed to get token: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subscribe ke topic tertentu
|
||||||
|
Future<void> subscribeToTopic(String topic) async {
|
||||||
|
await _messaging.subscribeToTopic(topic);
|
||||||
|
log('[FCM] Subscribed to topic: $topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsubscribe dari topic tertentu
|
||||||
|
Future<void> unsubscribeFromTopic(String topic) async {
|
||||||
|
await _messaging.unsubscribeFromTopic(topic);
|
||||||
|
log('[FCM] Unsubscribed from topic: $topic');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../domain/unit/unit.dart';
|
||||||
|
|
||||||
|
/// Kamus satuan yang dipakai untuk menerjemahkan `unit_id` sebuah produk
|
||||||
|
/// menjadi singkatan yang dicetak di keranjang dan struk (`4,2 ons`).
|
||||||
|
///
|
||||||
|
/// Response produk hanya dijamin membawa `unit_id`; nama dan singkatannya
|
||||||
|
/// datang dari `GET /api/v1/units`. Registry ini menjembatani keduanya tanpa
|
||||||
|
/// memaksa setiap pemanggil menunggu request.
|
||||||
|
@lazySingleton
|
||||||
|
class UnitRegistry {
|
||||||
|
final IUnitRepository _repository;
|
||||||
|
final _logName = 'UnitRegistry';
|
||||||
|
|
||||||
|
Map<String, ProductUnit> _unitsById = {};
|
||||||
|
|
||||||
|
UnitRegistry(this._repository);
|
||||||
|
|
||||||
|
bool get isEmpty => _unitsById.isEmpty;
|
||||||
|
|
||||||
|
ProductUnit? byId(String? id) {
|
||||||
|
if (id == null || id.isEmpty) return null;
|
||||||
|
return _unitsById[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
String abbreviationOf(String? id) => byId(id)?.abbreviation ?? '';
|
||||||
|
|
||||||
|
String nameOf(String? id) => byId(id)?.name ?? '';
|
||||||
|
|
||||||
|
/// Muat satuan ke memori. Aman dipanggil berulang; [forceRefresh] menembak
|
||||||
|
/// server ulang saat pengguna melakukan sinkronisasi manual.
|
||||||
|
Future<void> load({bool forceRefresh = false}) async {
|
||||||
|
final result = await _repository.getUnits(forceRefresh: forceRefresh);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) => log('⚠️ Gagal memuat satuan: $failure', name: _logName),
|
||||||
|
(units) {
|
||||||
|
_unitsById = {for (final unit in units) unit.id: unit};
|
||||||
|
log('📏 ${units.length} satuan siap dipakai', name: _logName);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() => _unitsById = {};
|
||||||
|
}
|
||||||
@@ -1,27 +1,27 @@
|
|||||||
part of 'theme.dart';
|
part of 'theme.dart';
|
||||||
|
|
||||||
class AppStyle {
|
class AppStyle {
|
||||||
static TextStyle xs = TextStyle(color: AppColor.black, fontSize: 11);
|
static TextStyle xs = const TextStyle(color: AppColor.black, fontSize: 11);
|
||||||
|
|
||||||
static TextStyle sm = TextStyle(color: AppColor.black, fontSize: 12);
|
static TextStyle sm = const TextStyle(color: AppColor.black, fontSize: 12);
|
||||||
|
|
||||||
static TextStyle md = TextStyle(color: AppColor.black, fontSize: 14);
|
static TextStyle md = const TextStyle(color: AppColor.black, fontSize: 14);
|
||||||
|
|
||||||
static TextStyle lg = TextStyle(color: AppColor.black, fontSize: 16);
|
static TextStyle lg = const TextStyle(color: AppColor.black, fontSize: 16);
|
||||||
|
|
||||||
static TextStyle xl = TextStyle(color: AppColor.black, fontSize: 18);
|
static TextStyle xl = const TextStyle(color: AppColor.black, fontSize: 18);
|
||||||
|
|
||||||
static TextStyle xxl = TextStyle(color: AppColor.black, fontSize: 20);
|
static TextStyle xxl = const TextStyle(color: AppColor.black, fontSize: 20);
|
||||||
|
|
||||||
static TextStyle h6 = TextStyle(color: AppColor.black, fontSize: 22);
|
static TextStyle h6 = const TextStyle(color: AppColor.black, fontSize: 22);
|
||||||
|
|
||||||
static TextStyle h5 = TextStyle(color: AppColor.black, fontSize: 24);
|
static TextStyle h5 = const TextStyle(color: AppColor.black, fontSize: 24);
|
||||||
|
|
||||||
static TextStyle h4 = TextStyle(color: AppColor.black, fontSize: 26);
|
static TextStyle h4 = const TextStyle(color: AppColor.black, fontSize: 26);
|
||||||
|
|
||||||
static TextStyle h3 = TextStyle(color: AppColor.black, fontSize: 28);
|
static TextStyle h3 = const TextStyle(color: AppColor.black, fontSize: 28);
|
||||||
|
|
||||||
static TextStyle h2 = TextStyle(color: AppColor.black, fontSize: 30);
|
static TextStyle h2 = const TextStyle(color: AppColor.black, fontSize: 30);
|
||||||
|
|
||||||
static TextStyle h1 = TextStyle(color: AppColor.black, fontSize: 32);
|
static TextStyle h1 = const TextStyle(color: AppColor.black, fontSize: 32);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
part of 'theme.dart';
|
part of 'theme.dart';
|
||||||
|
|
||||||
class AppValue {
|
class AppValue {}
|
||||||
// TODO: define value
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class ThemeApp {
|
|||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
primaryColor: AppColor.primary,
|
primaryColor: AppColor.primary,
|
||||||
scaffoldBackgroundColor: AppColor.white,
|
scaffoldBackgroundColor: AppColor.white,
|
||||||
appBarTheme: AppBarTheme(
|
appBarTheme: const AppBarTheme(
|
||||||
color: AppColor.white,
|
color: AppColor.white,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
titleTextStyle: TextStyle(
|
titleTextStyle: TextStyle(
|
||||||
@@ -19,7 +19,7 @@ class ThemeApp {
|
|||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
iconTheme: const IconThemeData(color: AppColor.primary),
|
iconTheme: IconThemeData(color: AppColor.primary),
|
||||||
),
|
),
|
||||||
fontFamily: FontFamily.quicksand,
|
fontFamily: FontFamily.quicksand,
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: AppColor.primary),
|
colorScheme: ColorScheme.fromSeed(seedColor: AppColor.primary),
|
||||||
@@ -28,19 +28,19 @@ class ThemeApp {
|
|||||||
hintStyle: const TextStyle(color: AppColor.textSecondary),
|
hintStyle: const TextStyle(color: AppColor.textSecondary),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(color: AppColor.borderDark),
|
borderSide: const BorderSide(color: AppColor.borderDark),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(color: AppColor.primary),
|
borderSide: const BorderSide(color: AppColor.primary),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(color: AppColor.borderDark),
|
borderSide: const BorderSide(color: AppColor.borderDark),
|
||||||
),
|
),
|
||||||
errorBorder: OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(color: AppColor.error),
|
borderSide: const BorderSide(color: AppColor.error),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/// Cara sebuah produk dijual.
|
||||||
|
///
|
||||||
|
/// Lihat `docs/integration-weight-based-products.md` — backend hanya menerima
|
||||||
|
/// dua nilai ini, dan produk lama otomatis dianggap [SellBy.unit].
|
||||||
|
class SellBy {
|
||||||
|
const SellBy._();
|
||||||
|
|
||||||
|
/// Dijual per cacah. `quantity` integer, `weight` tidak dikirim.
|
||||||
|
static const String unit = 'unit';
|
||||||
|
|
||||||
|
/// Dijual per timbangan. `quantity` selalu 1, `weight` wajib.
|
||||||
|
static const String weight = 'weight';
|
||||||
|
|
||||||
|
static bool isWeight(String? value) => value == weight;
|
||||||
|
|
||||||
|
/// Nilai apa pun di luar dua yang dikenal diperlakukan sebagai [unit],
|
||||||
|
/// sama seperti perilaku backend untuk produk lama.
|
||||||
|
static String normalize(String? value) => value == weight ? weight : unit;
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
class ApiPath {
|
class ApiPath {
|
||||||
static const String login = '/api/v1/auth/login';
|
static const String login = '/api/v1/auth/login';
|
||||||
|
static const String logout = '/api/v1/auth/logout';
|
||||||
static const String outlets = '/api/v1/outlets';
|
static const String outlets = '/api/v1/outlets';
|
||||||
static const String categories = '/api/v1/categories';
|
static const String categories = '/api/v1/categories';
|
||||||
static const String products = '/api/v1/products';
|
static const String products = '/api/v1/products';
|
||||||
|
static const String units = '/api/v1/units';
|
||||||
static const String tables = '/api/v1/tables';
|
static const String tables = '/api/v1/tables';
|
||||||
static const String customers = '/api/v1/customers';
|
static const String customers = '/api/v1/customers';
|
||||||
static const String paymentMethods = '/api/v1/payment-methods';
|
static const String paymentMethods = '/api/v1/payment-methods';
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ class ProductAnalyticCategoryItem with _$ProductAnalyticCategoryItem {
|
|||||||
required int totalRevenue,
|
required int totalRevenue,
|
||||||
}) = _ProductAnalyticCategoryItem;
|
}) = _ProductAnalyticCategoryItem;
|
||||||
|
|
||||||
|
// ignore: prefer_const_constructors
|
||||||
factory ProductAnalyticCategoryItem.empty() => ProductAnalyticCategoryItem(
|
factory ProductAnalyticCategoryItem.empty() => ProductAnalyticCategoryItem(
|
||||||
categoryName: '',
|
categoryName: '',
|
||||||
productCount: 0,
|
productCount: 0,
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ abstract class IAuthRepository {
|
|||||||
Future<Either<AuthFailure, Login>> login({
|
Future<Either<AuthFailure, Login>> login({
|
||||||
required String email,
|
required String email,
|
||||||
required String password,
|
required String password,
|
||||||
|
required Map<String, String> devicePayload,
|
||||||
|
required String fcmToken,
|
||||||
});
|
});
|
||||||
|
Future<Either<AuthFailure, Unit>> logout();
|
||||||
Future<bool> hasToken();
|
Future<bool> hasToken();
|
||||||
Future<Either<AuthFailure, User>> currentUser();
|
Future<Either<AuthFailure, User>> currentUser();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ class ListCustomer with _$ListCustomer {
|
|||||||
required int totalPages,
|
required int totalPages,
|
||||||
}) = _ListCustomer;
|
}) = _ListCustomer;
|
||||||
|
|
||||||
factory ListCustomer.empty() => ListCustomer(
|
factory ListCustomer.empty() => const ListCustomer(
|
||||||
customers: const [],
|
customers: [],
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
page: 0,
|
page: 0,
|
||||||
limit: 0,
|
limit: 0,
|
||||||
|
|||||||
@@ -8,13 +8,26 @@ class AddItemOrderRequest with _$AddItemOrderRequest {
|
|||||||
required int quantity,
|
required int quantity,
|
||||||
required int unitPrice,
|
required int unitPrice,
|
||||||
required String notes,
|
required String notes,
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`.
|
||||||
|
double? weight,
|
||||||
}) = _AddItemOrderRequest;
|
}) = _AddItemOrderRequest;
|
||||||
|
|
||||||
factory AddItemOrderRequest.empty() => AddItemOrderRequest(
|
factory AddItemOrderRequest.empty() => const AddItemOrderRequest(
|
||||||
productId: '',
|
productId: '',
|
||||||
productVariantId: '',
|
productVariantId: '',
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
unitPrice: 0,
|
unitPrice: 0,
|
||||||
notes: '',
|
notes: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory AddItemOrderRequest.fromProductQuantity(ProductQuantity item) =>
|
||||||
|
AddItemOrderRequest(
|
||||||
|
productId: item.product.id,
|
||||||
|
productVariantId: item.variant?.id ?? '',
|
||||||
|
quantity: item.isWeighted ? 1 : item.quantity,
|
||||||
|
unitPrice: item.product.price.toInt(),
|
||||||
|
notes: item.notes,
|
||||||
|
weight: item.weight,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,19 @@ class ListOrder with _$ListOrder {
|
|||||||
required int totalPages,
|
required int totalPages,
|
||||||
}) = _ListOrder;
|
}) = _ListOrder;
|
||||||
|
|
||||||
factory ListOrder.empty() =>
|
factory ListOrder.empty() => const ListOrder(
|
||||||
ListOrder(orders: [], totalCount: 0, page: 0, limit: 0, totalPages: 0);
|
orders: [],
|
||||||
|
totalCount: 0,
|
||||||
|
page: 0,
|
||||||
|
limit: 0,
|
||||||
|
totalPages: 0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class Order with _$Order {
|
class Order with _$Order {
|
||||||
|
const Order._();
|
||||||
|
|
||||||
const factory Order({
|
const factory Order({
|
||||||
required String id,
|
required String id,
|
||||||
required String orderNumber,
|
required String orderNumber,
|
||||||
@@ -75,10 +82,143 @@ class Order with _$Order {
|
|||||||
splitType: '',
|
splitType: '',
|
||||||
refundReason: '',
|
refundReason: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory Order.mockOrder() => Order(
|
||||||
|
id: 'ORD-001',
|
||||||
|
orderNumber: 'A-1001',
|
||||||
|
outletId: 'OUTLET-001',
|
||||||
|
userId: 'USER-001',
|
||||||
|
tableNumber: 'T01',
|
||||||
|
orderType: 'Dine In',
|
||||||
|
status: 'Completed',
|
||||||
|
subtotal: 150000,
|
||||||
|
taxAmount: 15000,
|
||||||
|
discountAmount: 5000,
|
||||||
|
totalAmount: 160000,
|
||||||
|
totalCost: 100000,
|
||||||
|
remainingAmount: 0,
|
||||||
|
paymentStatus: 'Paid',
|
||||||
|
refundAmount: 0,
|
||||||
|
refundReason: '',
|
||||||
|
isVoid: false,
|
||||||
|
isRefund: false,
|
||||||
|
notes: 'Customer requested less sugar',
|
||||||
|
metadata: {'source': 'POS', 'device': 'iPad', 'customer_name': "John Doe"},
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(hours: 1)),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
orderItems: [
|
||||||
|
OrderItem(
|
||||||
|
id: 'ITEM-001',
|
||||||
|
orderId: 'ORD-001',
|
||||||
|
productId: 'PROD-001',
|
||||||
|
productName: 'Cappuccino',
|
||||||
|
productVariantId: 'VAR-001',
|
||||||
|
productVariantName: 'Hot',
|
||||||
|
quantity: 2,
|
||||||
|
unitPrice: 25000,
|
||||||
|
totalPrice: 50000,
|
||||||
|
modifiers: [
|
||||||
|
{'name': 'Extra Shot', 'price': 5000},
|
||||||
|
],
|
||||||
|
notes: 'No sugar',
|
||||||
|
status: 'Served',
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(hours: 1)),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
printerType: 'Barista',
|
||||||
|
printToChecker: false,
|
||||||
|
paidQuantity: 2,
|
||||||
|
categoryId: 'CAT-001',
|
||||||
|
categoryName: 'Minuman',
|
||||||
|
),
|
||||||
|
OrderItem(
|
||||||
|
id: 'ITEM-002',
|
||||||
|
orderId: 'ORD-001',
|
||||||
|
productId: 'PROD-002',
|
||||||
|
productName: 'Spaghetti Carbonara',
|
||||||
|
productVariantId: 'VAR-002',
|
||||||
|
productVariantName: '',
|
||||||
|
quantity: 1,
|
||||||
|
unitPrice: 55000,
|
||||||
|
totalPrice: 55000,
|
||||||
|
modifiers: [],
|
||||||
|
notes: '',
|
||||||
|
status: 'Served',
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(hours: 1)),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
printerType: 'Kitchen',
|
||||||
|
printToChecker: false,
|
||||||
|
paidQuantity: 1,
|
||||||
|
categoryId: 'CAT-002',
|
||||||
|
categoryName: 'Makanan',
|
||||||
|
),
|
||||||
|
OrderItem(
|
||||||
|
id: 'ITEM-003',
|
||||||
|
orderId: 'ORD-001',
|
||||||
|
productId: 'PROD-003',
|
||||||
|
productName: 'Pasta',
|
||||||
|
productVariantId: 'VAR-002',
|
||||||
|
productVariantName: '',
|
||||||
|
quantity: 1,
|
||||||
|
unitPrice: 50000,
|
||||||
|
totalPrice: 50000,
|
||||||
|
modifiers: [],
|
||||||
|
notes: '',
|
||||||
|
status: 'Served',
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(hours: 1)),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
printerType: 'Kitchen',
|
||||||
|
printToChecker: false,
|
||||||
|
paidQuantity: 1,
|
||||||
|
categoryId: 'CAT-002',
|
||||||
|
categoryName: 'Makanan',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
payments: [
|
||||||
|
Payment(
|
||||||
|
id: 'PAY-001',
|
||||||
|
orderId: 'ORD-001',
|
||||||
|
paymentMethodId: 'PM-CASH',
|
||||||
|
paymentMethodName: 'Cash',
|
||||||
|
paymentMethodType: 'Cash',
|
||||||
|
amount: 160000,
|
||||||
|
status: 'Success',
|
||||||
|
splitNumber: 1,
|
||||||
|
splitTotal: 1,
|
||||||
|
splitDescription: 'Full payment by cash',
|
||||||
|
refundAmount: 0,
|
||||||
|
metadata: {'cashier': 'Efril', 'device': 'POS iPad'},
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(minutes: 45)),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
totalPaid: 160000,
|
||||||
|
paymentCount: 1,
|
||||||
|
splitType: 'Single',
|
||||||
|
);
|
||||||
|
|
||||||
|
// For Add Item Order
|
||||||
|
factory Order.fromLastOrder(Order order, List<ProductQuantity> products) {
|
||||||
|
final subtotal = products.fold<int>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + item.lineTotal.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return order.copyWith(
|
||||||
|
orderItems: products.map(OrderItem.fromProductQuantity).toList(),
|
||||||
|
totalAmount: subtotal,
|
||||||
|
subtotal: subtotal,
|
||||||
|
metadata: order.metadata,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OrderItem> get orderItemNotCancelled =>
|
||||||
|
orderItems.where((item) => item.status != 'cancelled').toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class OrderItem with _$OrderItem {
|
class OrderItem with _$OrderItem {
|
||||||
|
const OrderItem._();
|
||||||
|
|
||||||
const factory OrderItem({
|
const factory OrderItem({
|
||||||
required String id,
|
required String id,
|
||||||
required String orderId,
|
required String orderId,
|
||||||
@@ -95,7 +235,16 @@ class OrderItem with _$OrderItem {
|
|||||||
required DateTime createdAt,
|
required DateTime createdAt,
|
||||||
required DateTime updatedAt,
|
required DateTime updatedAt,
|
||||||
required String printerType,
|
required String printerType,
|
||||||
|
required bool printToChecker,
|
||||||
required int paidQuantity,
|
required int paidQuantity,
|
||||||
|
required String categoryId,
|
||||||
|
required String categoryName,
|
||||||
|
|
||||||
|
/// Angka timbangan baris ini. `null` untuk produk cacah.
|
||||||
|
double? weight,
|
||||||
|
@Default('') String unitId,
|
||||||
|
@Default('') String unitName,
|
||||||
|
@Default('') String unitAbbreviation,
|
||||||
}) = _OrderItem;
|
}) = _OrderItem;
|
||||||
|
|
||||||
factory OrderItem.empty() => OrderItem(
|
factory OrderItem.empty() => OrderItem(
|
||||||
@@ -114,6 +263,57 @@ class OrderItem with _$OrderItem {
|
|||||||
createdAt: DateTime(1970),
|
createdAt: DateTime(1970),
|
||||||
updatedAt: DateTime(1970),
|
updatedAt: DateTime(1970),
|
||||||
printerType: '',
|
printerType: '',
|
||||||
|
printToChecker: false,
|
||||||
paidQuantity: 0,
|
paidQuantity: 0,
|
||||||
|
categoryId: '',
|
||||||
|
categoryName: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory OrderItem.fromProductQuantity(ProductQuantity productQuantity) =>
|
||||||
|
OrderItem(
|
||||||
|
id: '',
|
||||||
|
orderId: '',
|
||||||
|
productId: productQuantity.product.id,
|
||||||
|
productName: productQuantity.product.name,
|
||||||
|
productVariantId: productQuantity.variant?.id ?? '',
|
||||||
|
productVariantName: productQuantity.variant?.name ?? '',
|
||||||
|
quantity: productQuantity.isWeighted ? 1 : productQuantity.quantity,
|
||||||
|
unitPrice: productQuantity.product.price.toInt(),
|
||||||
|
totalPrice: productQuantity.lineTotal.toInt(),
|
||||||
|
modifiers: [],
|
||||||
|
notes: productQuantity.notes,
|
||||||
|
status: 'pending',
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
printerType: productQuantity.product.printerType,
|
||||||
|
printToChecker: productQuantity.product.printToChecker,
|
||||||
|
paidQuantity: 0,
|
||||||
|
categoryId: '',
|
||||||
|
categoryName: '',
|
||||||
|
weight: productQuantity.weight,
|
||||||
|
unitId: productQuantity.product.unitId,
|
||||||
|
unitName: productQuantity.product.unitName,
|
||||||
|
unitAbbreviation: productQuantity.product.unitAbbreviation,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Baris hasil penimbangan — utuh, tidak bisa dipecah sebagian.
|
||||||
|
bool get isWeighted => weight != null && weight! > 0;
|
||||||
|
|
||||||
|
/// Singkatan satuan untuk ditampilkan; jatuh ke nama panjang bila kosong.
|
||||||
|
String get unitLabel =>
|
||||||
|
unitAbbreviation.isNotEmpty ? unitAbbreviation : unitName;
|
||||||
|
|
||||||
|
/// `4,2 ons` untuk baris berbobot, `2` untuk baris biasa.
|
||||||
|
///
|
||||||
|
/// Jangan menampilkan [quantity] mentah pada baris berbobot — nilainya selalu
|
||||||
|
/// 1 dan terbaca seperti "satu ons".
|
||||||
|
String get quantityLabel {
|
||||||
|
if (!isWeighted) return quantity.toString();
|
||||||
|
final value = weight!.weightFormat;
|
||||||
|
return unitLabel.isEmpty ? value : '$value $unitLabel';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `4,2 ons x Rp 4.500` atau `2 x Rp 25.000`.
|
||||||
|
String get quantityPriceLabel =>
|
||||||
|
'$quantityLabel x ${unitPrice.currencyFormatRpV2}';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class OrderItemRequest with _$OrderItemRequest {
|
|||||||
required int quantity,
|
required int quantity,
|
||||||
required int unitPrice,
|
required int unitPrice,
|
||||||
required String notes,
|
required String notes,
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`; produk cacah tidak boleh
|
||||||
|
/// mengirim berat sama sekali.
|
||||||
|
double? weight,
|
||||||
}) = _OrderItemRequest;
|
}) = _OrderItemRequest;
|
||||||
|
|
||||||
factory OrderItemRequest.empty() => const OrderItemRequest(
|
factory OrderItemRequest.empty() => const OrderItemRequest(
|
||||||
@@ -42,4 +46,16 @@ class OrderItemRequest with _$OrderItemRequest {
|
|||||||
unitPrice: 0,
|
unitPrice: 0,
|
||||||
notes: '',
|
notes: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Bentuk baris dari isi keranjang: produk timbangan selalu `quantity: 1`
|
||||||
|
/// ditambah `weight`, produk cacah tetap seperti sebelumnya.
|
||||||
|
factory OrderItemRequest.fromProductQuantity(ProductQuantity item) =>
|
||||||
|
OrderItemRequest(
|
||||||
|
productId: item.product.id,
|
||||||
|
productVariantId: item.variant?.id ?? '',
|
||||||
|
quantity: item.isWeighted ? 1 : item.quantity,
|
||||||
|
unitPrice: item.product.price.toInt(),
|
||||||
|
notes: item.notes,
|
||||||
|
weight: item.weight,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
|||||||
|
|
||||||
import '../../common/api/api_failure.dart';
|
import '../../common/api/api_failure.dart';
|
||||||
import '../../common/types/split_type.dart';
|
import '../../common/types/split_type.dart';
|
||||||
|
import '../../common/extension/extension.dart';
|
||||||
|
import '../product/product.dart';
|
||||||
|
|
||||||
part 'order.freezed.dart';
|
part 'order.freezed.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -681,7 +681,7 @@ class __$$OrderImplCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$OrderImpl implements _Order {
|
class _$OrderImpl extends _Order {
|
||||||
const _$OrderImpl({
|
const _$OrderImpl({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.orderNumber,
|
required this.orderNumber,
|
||||||
@@ -712,7 +712,8 @@ class _$OrderImpl implements _Order {
|
|||||||
required this.splitType,
|
required this.splitType,
|
||||||
}) : _metadata = metadata,
|
}) : _metadata = metadata,
|
||||||
_orderItems = orderItems,
|
_orderItems = orderItems,
|
||||||
_payments = payments;
|
_payments = payments,
|
||||||
|
super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
@@ -889,7 +890,7 @@ class _$OrderImpl implements _Order {
|
|||||||
__$$OrderImplCopyWithImpl<_$OrderImpl>(this, _$identity);
|
__$$OrderImplCopyWithImpl<_$OrderImpl>(this, _$identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Order implements Order {
|
abstract class _Order extends Order {
|
||||||
const factory _Order({
|
const factory _Order({
|
||||||
required final String id,
|
required final String id,
|
||||||
required final String orderNumber,
|
required final String orderNumber,
|
||||||
@@ -919,6 +920,7 @@ abstract class _Order implements Order {
|
|||||||
required final int paymentCount,
|
required final int paymentCount,
|
||||||
required final String splitType,
|
required final String splitType,
|
||||||
}) = _$OrderImpl;
|
}) = _$OrderImpl;
|
||||||
|
const _Order._() : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id;
|
String get id;
|
||||||
@@ -1000,7 +1002,16 @@ mixin _$OrderItem {
|
|||||||
DateTime get createdAt => throw _privateConstructorUsedError;
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
DateTime get updatedAt => throw _privateConstructorUsedError;
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
String get printerType => throw _privateConstructorUsedError;
|
String get printerType => throw _privateConstructorUsedError;
|
||||||
|
bool get printToChecker => throw _privateConstructorUsedError;
|
||||||
int get paidQuantity => throw _privateConstructorUsedError;
|
int get paidQuantity => throw _privateConstructorUsedError;
|
||||||
|
String get categoryId => throw _privateConstructorUsedError;
|
||||||
|
String get categoryName => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Angka timbangan baris ini. `null` untuk produk cacah.
|
||||||
|
double? get weight => throw _privateConstructorUsedError;
|
||||||
|
String get unitId => throw _privateConstructorUsedError;
|
||||||
|
String get unitName => throw _privateConstructorUsedError;
|
||||||
|
String get unitAbbreviation => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of OrderItem
|
/// Create a copy of OrderItem
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1030,7 +1041,14 @@ abstract class $OrderItemCopyWith<$Res> {
|
|||||||
DateTime createdAt,
|
DateTime createdAt,
|
||||||
DateTime updatedAt,
|
DateTime updatedAt,
|
||||||
String printerType,
|
String printerType,
|
||||||
|
bool printToChecker,
|
||||||
int paidQuantity,
|
int paidQuantity,
|
||||||
|
String categoryId,
|
||||||
|
String categoryName,
|
||||||
|
double? weight,
|
||||||
|
String unitId,
|
||||||
|
String unitName,
|
||||||
|
String unitAbbreviation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,7 +1082,14 @@ class _$OrderItemCopyWithImpl<$Res, $Val extends OrderItem>
|
|||||||
Object? createdAt = null,
|
Object? createdAt = null,
|
||||||
Object? updatedAt = null,
|
Object? updatedAt = null,
|
||||||
Object? printerType = null,
|
Object? printerType = null,
|
||||||
|
Object? printToChecker = null,
|
||||||
Object? paidQuantity = null,
|
Object? paidQuantity = null,
|
||||||
|
Object? categoryId = null,
|
||||||
|
Object? categoryName = null,
|
||||||
|
Object? weight = freezed,
|
||||||
|
Object? unitId = null,
|
||||||
|
Object? unitName = null,
|
||||||
|
Object? unitAbbreviation = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -1128,10 +1153,38 @@ class _$OrderItemCopyWithImpl<$Res, $Val extends OrderItem>
|
|||||||
? _value.printerType
|
? _value.printerType
|
||||||
: printerType // ignore: cast_nullable_to_non_nullable
|
: printerType // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
printToChecker: null == printToChecker
|
||||||
|
? _value.printToChecker
|
||||||
|
: printToChecker // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
paidQuantity: null == paidQuantity
|
paidQuantity: null == paidQuantity
|
||||||
? _value.paidQuantity
|
? _value.paidQuantity
|
||||||
: paidQuantity // ignore: cast_nullable_to_non_nullable
|
: paidQuantity // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
categoryId: null == categoryId
|
||||||
|
? _value.categoryId
|
||||||
|
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
categoryName: null == categoryName
|
||||||
|
? _value.categoryName
|
||||||
|
: categoryName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
|
unitId: null == unitId
|
||||||
|
? _value.unitId
|
||||||
|
: unitId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitName: null == unitName
|
||||||
|
? _value.unitName
|
||||||
|
: unitName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitAbbreviation: null == unitAbbreviation
|
||||||
|
? _value.unitAbbreviation
|
||||||
|
: unitAbbreviation // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -1163,7 +1216,14 @@ abstract class _$$OrderItemImplCopyWith<$Res>
|
|||||||
DateTime createdAt,
|
DateTime createdAt,
|
||||||
DateTime updatedAt,
|
DateTime updatedAt,
|
||||||
String printerType,
|
String printerType,
|
||||||
|
bool printToChecker,
|
||||||
int paidQuantity,
|
int paidQuantity,
|
||||||
|
String categoryId,
|
||||||
|
String categoryName,
|
||||||
|
double? weight,
|
||||||
|
String unitId,
|
||||||
|
String unitName,
|
||||||
|
String unitAbbreviation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1196,7 +1256,14 @@ class __$$OrderItemImplCopyWithImpl<$Res>
|
|||||||
Object? createdAt = null,
|
Object? createdAt = null,
|
||||||
Object? updatedAt = null,
|
Object? updatedAt = null,
|
||||||
Object? printerType = null,
|
Object? printerType = null,
|
||||||
|
Object? printToChecker = null,
|
||||||
Object? paidQuantity = null,
|
Object? paidQuantity = null,
|
||||||
|
Object? categoryId = null,
|
||||||
|
Object? categoryName = null,
|
||||||
|
Object? weight = freezed,
|
||||||
|
Object? unitId = null,
|
||||||
|
Object? unitName = null,
|
||||||
|
Object? unitAbbreviation = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$OrderItemImpl(
|
_$OrderItemImpl(
|
||||||
@@ -1260,10 +1327,38 @@ class __$$OrderItemImplCopyWithImpl<$Res>
|
|||||||
? _value.printerType
|
? _value.printerType
|
||||||
: printerType // ignore: cast_nullable_to_non_nullable
|
: printerType // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
printToChecker: null == printToChecker
|
||||||
|
? _value.printToChecker
|
||||||
|
: printToChecker // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
paidQuantity: null == paidQuantity
|
paidQuantity: null == paidQuantity
|
||||||
? _value.paidQuantity
|
? _value.paidQuantity
|
||||||
: paidQuantity // ignore: cast_nullable_to_non_nullable
|
: paidQuantity // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
categoryId: null == categoryId
|
||||||
|
? _value.categoryId
|
||||||
|
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
categoryName: null == categoryName
|
||||||
|
? _value.categoryName
|
||||||
|
: categoryName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
|
unitId: null == unitId
|
||||||
|
? _value.unitId
|
||||||
|
: unitId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitName: null == unitName
|
||||||
|
? _value.unitName
|
||||||
|
: unitName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitAbbreviation: null == unitAbbreviation
|
||||||
|
? _value.unitAbbreviation
|
||||||
|
: unitAbbreviation // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1271,7 +1366,7 @@ class __$$OrderItemImplCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$OrderItemImpl implements _OrderItem {
|
class _$OrderItemImpl extends _OrderItem {
|
||||||
const _$OrderItemImpl({
|
const _$OrderItemImpl({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.orderId,
|
required this.orderId,
|
||||||
@@ -1288,8 +1383,16 @@ class _$OrderItemImpl implements _OrderItem {
|
|||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.printerType,
|
required this.printerType,
|
||||||
|
required this.printToChecker,
|
||||||
required this.paidQuantity,
|
required this.paidQuantity,
|
||||||
}) : _modifiers = modifiers;
|
required this.categoryId,
|
||||||
|
required this.categoryName,
|
||||||
|
this.weight,
|
||||||
|
this.unitId = '',
|
||||||
|
this.unitName = '',
|
||||||
|
this.unitAbbreviation = '',
|
||||||
|
}) : _modifiers = modifiers,
|
||||||
|
super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
@@ -1328,11 +1431,30 @@ class _$OrderItemImpl implements _OrderItem {
|
|||||||
@override
|
@override
|
||||||
final String printerType;
|
final String printerType;
|
||||||
@override
|
@override
|
||||||
|
final bool printToChecker;
|
||||||
|
@override
|
||||||
final int paidQuantity;
|
final int paidQuantity;
|
||||||
|
@override
|
||||||
|
final String categoryId;
|
||||||
|
@override
|
||||||
|
final String categoryName;
|
||||||
|
|
||||||
|
/// Angka timbangan baris ini. `null` untuk produk cacah.
|
||||||
|
@override
|
||||||
|
final double? weight;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String unitId;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String unitName;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String unitAbbreviation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'OrderItem(id: $id, orderId: $orderId, productId: $productId, productName: $productName, productVariantId: $productVariantId, productVariantName: $productVariantName, quantity: $quantity, unitPrice: $unitPrice, totalPrice: $totalPrice, modifiers: $modifiers, notes: $notes, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, printerType: $printerType, paidQuantity: $paidQuantity)';
|
return 'OrderItem(id: $id, orderId: $orderId, productId: $productId, productName: $productName, productVariantId: $productVariantId, productVariantName: $productVariantName, quantity: $quantity, unitPrice: $unitPrice, totalPrice: $totalPrice, modifiers: $modifiers, notes: $notes, status: $status, createdAt: $createdAt, updatedAt: $updatedAt, printerType: $printerType, printToChecker: $printToChecker, paidQuantity: $paidQuantity, categoryId: $categoryId, categoryName: $categoryName, weight: $weight, unitId: $unitId, unitName: $unitName, unitAbbreviation: $unitAbbreviation)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1368,12 +1490,24 @@ class _$OrderItemImpl implements _OrderItem {
|
|||||||
other.updatedAt == updatedAt) &&
|
other.updatedAt == updatedAt) &&
|
||||||
(identical(other.printerType, printerType) ||
|
(identical(other.printerType, printerType) ||
|
||||||
other.printerType == printerType) &&
|
other.printerType == printerType) &&
|
||||||
|
(identical(other.printToChecker, printToChecker) ||
|
||||||
|
other.printToChecker == printToChecker) &&
|
||||||
(identical(other.paidQuantity, paidQuantity) ||
|
(identical(other.paidQuantity, paidQuantity) ||
|
||||||
other.paidQuantity == paidQuantity));
|
other.paidQuantity == paidQuantity) &&
|
||||||
|
(identical(other.categoryId, categoryId) ||
|
||||||
|
other.categoryId == categoryId) &&
|
||||||
|
(identical(other.categoryName, categoryName) ||
|
||||||
|
other.categoryName == categoryName) &&
|
||||||
|
(identical(other.weight, weight) || other.weight == weight) &&
|
||||||
|
(identical(other.unitId, unitId) || other.unitId == unitId) &&
|
||||||
|
(identical(other.unitName, unitName) ||
|
||||||
|
other.unitName == unitName) &&
|
||||||
|
(identical(other.unitAbbreviation, unitAbbreviation) ||
|
||||||
|
other.unitAbbreviation == unitAbbreviation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hashAll([
|
||||||
runtimeType,
|
runtimeType,
|
||||||
id,
|
id,
|
||||||
orderId,
|
orderId,
|
||||||
@@ -1390,8 +1524,15 @@ class _$OrderItemImpl implements _OrderItem {
|
|||||||
createdAt,
|
createdAt,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
printerType,
|
printerType,
|
||||||
|
printToChecker,
|
||||||
paidQuantity,
|
paidQuantity,
|
||||||
);
|
categoryId,
|
||||||
|
categoryName,
|
||||||
|
weight,
|
||||||
|
unitId,
|
||||||
|
unitName,
|
||||||
|
unitAbbreviation,
|
||||||
|
]);
|
||||||
|
|
||||||
/// Create a copy of OrderItem
|
/// Create a copy of OrderItem
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1402,7 +1543,7 @@ class _$OrderItemImpl implements _OrderItem {
|
|||||||
__$$OrderItemImplCopyWithImpl<_$OrderItemImpl>(this, _$identity);
|
__$$OrderItemImplCopyWithImpl<_$OrderItemImpl>(this, _$identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _OrderItem implements OrderItem {
|
abstract class _OrderItem extends OrderItem {
|
||||||
const factory _OrderItem({
|
const factory _OrderItem({
|
||||||
required final String id,
|
required final String id,
|
||||||
required final String orderId,
|
required final String orderId,
|
||||||
@@ -1419,8 +1560,16 @@ abstract class _OrderItem implements OrderItem {
|
|||||||
required final DateTime createdAt,
|
required final DateTime createdAt,
|
||||||
required final DateTime updatedAt,
|
required final DateTime updatedAt,
|
||||||
required final String printerType,
|
required final String printerType,
|
||||||
|
required final bool printToChecker,
|
||||||
required final int paidQuantity,
|
required final int paidQuantity,
|
||||||
|
required final String categoryId,
|
||||||
|
required final String categoryName,
|
||||||
|
final double? weight,
|
||||||
|
final String unitId,
|
||||||
|
final String unitName,
|
||||||
|
final String unitAbbreviation,
|
||||||
}) = _$OrderItemImpl;
|
}) = _$OrderItemImpl;
|
||||||
|
const _OrderItem._() : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id;
|
String get id;
|
||||||
@@ -1453,7 +1602,23 @@ abstract class _OrderItem implements OrderItem {
|
|||||||
@override
|
@override
|
||||||
String get printerType;
|
String get printerType;
|
||||||
@override
|
@override
|
||||||
|
bool get printToChecker;
|
||||||
|
@override
|
||||||
int get paidQuantity;
|
int get paidQuantity;
|
||||||
|
@override
|
||||||
|
String get categoryId;
|
||||||
|
@override
|
||||||
|
String get categoryName;
|
||||||
|
|
||||||
|
/// Angka timbangan baris ini. `null` untuk produk cacah.
|
||||||
|
@override
|
||||||
|
double? get weight;
|
||||||
|
@override
|
||||||
|
String get unitId;
|
||||||
|
@override
|
||||||
|
String get unitName;
|
||||||
|
@override
|
||||||
|
String get unitAbbreviation;
|
||||||
|
|
||||||
/// Create a copy of OrderItem
|
/// Create a copy of OrderItem
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1779,6 +1944,10 @@ mixin _$OrderItemRequest {
|
|||||||
int get unitPrice => throw _privateConstructorUsedError;
|
int get unitPrice => throw _privateConstructorUsedError;
|
||||||
String get notes => throw _privateConstructorUsedError;
|
String get notes => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`; produk cacah tidak boleh
|
||||||
|
/// mengirim berat sama sekali.
|
||||||
|
double? get weight => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of OrderItemRequest
|
/// Create a copy of OrderItemRequest
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -1799,6 +1968,7 @@ abstract class $OrderItemRequestCopyWith<$Res> {
|
|||||||
int quantity,
|
int quantity,
|
||||||
int unitPrice,
|
int unitPrice,
|
||||||
String notes,
|
String notes,
|
||||||
|
double? weight,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1822,6 +1992,7 @@ class _$OrderItemRequestCopyWithImpl<$Res, $Val extends OrderItemRequest>
|
|||||||
Object? quantity = null,
|
Object? quantity = null,
|
||||||
Object? unitPrice = null,
|
Object? unitPrice = null,
|
||||||
Object? notes = null,
|
Object? notes = null,
|
||||||
|
Object? weight = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -1845,6 +2016,10 @@ class _$OrderItemRequestCopyWithImpl<$Res, $Val extends OrderItemRequest>
|
|||||||
? _value.notes
|
? _value.notes
|
||||||
: notes // ignore: cast_nullable_to_non_nullable
|
: notes // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -1866,6 +2041,7 @@ abstract class _$$OrderItemRequestImplCopyWith<$Res>
|
|||||||
int quantity,
|
int quantity,
|
||||||
int unitPrice,
|
int unitPrice,
|
||||||
String notes,
|
String notes,
|
||||||
|
double? weight,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1888,6 +2064,7 @@ class __$$OrderItemRequestImplCopyWithImpl<$Res>
|
|||||||
Object? quantity = null,
|
Object? quantity = null,
|
||||||
Object? unitPrice = null,
|
Object? unitPrice = null,
|
||||||
Object? notes = null,
|
Object? notes = null,
|
||||||
|
Object? weight = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$OrderItemRequestImpl(
|
_$OrderItemRequestImpl(
|
||||||
@@ -1911,6 +2088,10 @@ class __$$OrderItemRequestImplCopyWithImpl<$Res>
|
|||||||
? _value.notes
|
? _value.notes
|
||||||
: notes // ignore: cast_nullable_to_non_nullable
|
: notes // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1925,6 +2106,7 @@ class _$OrderItemRequestImpl implements _OrderItemRequest {
|
|||||||
required this.quantity,
|
required this.quantity,
|
||||||
required this.unitPrice,
|
required this.unitPrice,
|
||||||
required this.notes,
|
required this.notes,
|
||||||
|
this.weight,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1938,9 +2120,14 @@ class _$OrderItemRequestImpl implements _OrderItemRequest {
|
|||||||
@override
|
@override
|
||||||
final String notes;
|
final String notes;
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`; produk cacah tidak boleh
|
||||||
|
/// mengirim berat sama sekali.
|
||||||
|
@override
|
||||||
|
final double? weight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'OrderItemRequest(productId: $productId, productVariantId: $productVariantId, quantity: $quantity, unitPrice: $unitPrice, notes: $notes)';
|
return 'OrderItemRequest(productId: $productId, productVariantId: $productVariantId, quantity: $quantity, unitPrice: $unitPrice, notes: $notes, weight: $weight)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1956,7 +2143,8 @@ class _$OrderItemRequestImpl implements _OrderItemRequest {
|
|||||||
other.quantity == quantity) &&
|
other.quantity == quantity) &&
|
||||||
(identical(other.unitPrice, unitPrice) ||
|
(identical(other.unitPrice, unitPrice) ||
|
||||||
other.unitPrice == unitPrice) &&
|
other.unitPrice == unitPrice) &&
|
||||||
(identical(other.notes, notes) || other.notes == notes));
|
(identical(other.notes, notes) || other.notes == notes) &&
|
||||||
|
(identical(other.weight, weight) || other.weight == weight));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1967,6 +2155,7 @@ class _$OrderItemRequestImpl implements _OrderItemRequest {
|
|||||||
quantity,
|
quantity,
|
||||||
unitPrice,
|
unitPrice,
|
||||||
notes,
|
notes,
|
||||||
|
weight,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of OrderItemRequest
|
/// Create a copy of OrderItemRequest
|
||||||
@@ -1988,6 +2177,7 @@ abstract class _OrderItemRequest implements OrderItemRequest {
|
|||||||
required final int quantity,
|
required final int quantity,
|
||||||
required final int unitPrice,
|
required final int unitPrice,
|
||||||
required final String notes,
|
required final String notes,
|
||||||
|
final double? weight,
|
||||||
}) = _$OrderItemRequestImpl;
|
}) = _$OrderItemRequestImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -2001,6 +2191,11 @@ abstract class _OrderItemRequest implements OrderItemRequest {
|
|||||||
@override
|
@override
|
||||||
String get notes;
|
String get notes;
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`; produk cacah tidak boleh
|
||||||
|
/// mengirim berat sama sekali.
|
||||||
|
@override
|
||||||
|
double? get weight;
|
||||||
|
|
||||||
/// Create a copy of OrderItemRequest
|
/// Create a copy of OrderItemRequest
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -3380,6 +3575,9 @@ mixin _$AddItemOrderRequest {
|
|||||||
int get unitPrice => throw _privateConstructorUsedError;
|
int get unitPrice => throw _privateConstructorUsedError;
|
||||||
String get notes => throw _privateConstructorUsedError;
|
String get notes => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`.
|
||||||
|
double? get weight => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of AddItemOrderRequest
|
/// Create a copy of AddItemOrderRequest
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -3400,6 +3598,7 @@ abstract class $AddItemOrderRequestCopyWith<$Res> {
|
|||||||
int quantity,
|
int quantity,
|
||||||
int unitPrice,
|
int unitPrice,
|
||||||
String notes,
|
String notes,
|
||||||
|
double? weight,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3423,6 +3622,7 @@ class _$AddItemOrderRequestCopyWithImpl<$Res, $Val extends AddItemOrderRequest>
|
|||||||
Object? quantity = null,
|
Object? quantity = null,
|
||||||
Object? unitPrice = null,
|
Object? unitPrice = null,
|
||||||
Object? notes = null,
|
Object? notes = null,
|
||||||
|
Object? weight = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -3446,6 +3646,10 @@ class _$AddItemOrderRequestCopyWithImpl<$Res, $Val extends AddItemOrderRequest>
|
|||||||
? _value.notes
|
? _value.notes
|
||||||
: notes // ignore: cast_nullable_to_non_nullable
|
: notes // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -3467,6 +3671,7 @@ abstract class _$$AddItemOrderRequestImplCopyWith<$Res>
|
|||||||
int quantity,
|
int quantity,
|
||||||
int unitPrice,
|
int unitPrice,
|
||||||
String notes,
|
String notes,
|
||||||
|
double? weight,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3489,6 +3694,7 @@ class __$$AddItemOrderRequestImplCopyWithImpl<$Res>
|
|||||||
Object? quantity = null,
|
Object? quantity = null,
|
||||||
Object? unitPrice = null,
|
Object? unitPrice = null,
|
||||||
Object? notes = null,
|
Object? notes = null,
|
||||||
|
Object? weight = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$AddItemOrderRequestImpl(
|
_$AddItemOrderRequestImpl(
|
||||||
@@ -3512,6 +3718,10 @@ class __$$AddItemOrderRequestImplCopyWithImpl<$Res>
|
|||||||
? _value.notes
|
? _value.notes
|
||||||
: notes // ignore: cast_nullable_to_non_nullable
|
: notes // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3526,6 +3736,7 @@ class _$AddItemOrderRequestImpl implements _AddItemOrderRequest {
|
|||||||
required this.quantity,
|
required this.quantity,
|
||||||
required this.unitPrice,
|
required this.unitPrice,
|
||||||
required this.notes,
|
required this.notes,
|
||||||
|
this.weight,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -3539,9 +3750,13 @@ class _$AddItemOrderRequestImpl implements _AddItemOrderRequest {
|
|||||||
@override
|
@override
|
||||||
final String notes;
|
final String notes;
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`.
|
||||||
|
@override
|
||||||
|
final double? weight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AddItemOrderRequest(productId: $productId, productVariantId: $productVariantId, quantity: $quantity, unitPrice: $unitPrice, notes: $notes)';
|
return 'AddItemOrderRequest(productId: $productId, productVariantId: $productVariantId, quantity: $quantity, unitPrice: $unitPrice, notes: $notes, weight: $weight)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -3557,7 +3772,8 @@ class _$AddItemOrderRequestImpl implements _AddItemOrderRequest {
|
|||||||
other.quantity == quantity) &&
|
other.quantity == quantity) &&
|
||||||
(identical(other.unitPrice, unitPrice) ||
|
(identical(other.unitPrice, unitPrice) ||
|
||||||
other.unitPrice == unitPrice) &&
|
other.unitPrice == unitPrice) &&
|
||||||
(identical(other.notes, notes) || other.notes == notes));
|
(identical(other.notes, notes) || other.notes == notes) &&
|
||||||
|
(identical(other.weight, weight) || other.weight == weight));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -3568,6 +3784,7 @@ class _$AddItemOrderRequestImpl implements _AddItemOrderRequest {
|
|||||||
quantity,
|
quantity,
|
||||||
unitPrice,
|
unitPrice,
|
||||||
notes,
|
notes,
|
||||||
|
weight,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a copy of AddItemOrderRequest
|
/// Create a copy of AddItemOrderRequest
|
||||||
@@ -3589,6 +3806,7 @@ abstract class _AddItemOrderRequest implements AddItemOrderRequest {
|
|||||||
required final int quantity,
|
required final int quantity,
|
||||||
required final int unitPrice,
|
required final int unitPrice,
|
||||||
required final String notes,
|
required final String notes,
|
||||||
|
final double? weight,
|
||||||
}) = _$AddItemOrderRequestImpl;
|
}) = _$AddItemOrderRequestImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -3602,6 +3820,10 @@ abstract class _AddItemOrderRequest implements AddItemOrderRequest {
|
|||||||
@override
|
@override
|
||||||
String get notes;
|
String get notes;
|
||||||
|
|
||||||
|
/// Hanya diisi untuk produk `sell_by: "weight"`.
|
||||||
|
@override
|
||||||
|
double? get weight;
|
||||||
|
|
||||||
/// Create a copy of AddItemOrderRequest
|
/// Create a copy of AddItemOrderRequest
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ abstract class IOrderRepository {
|
|||||||
Future<Either<OrderFailure, Order>> addItemOrder({
|
Future<Either<OrderFailure, Order>> addItemOrder({
|
||||||
required String id,
|
required String id,
|
||||||
required List<AddItemOrderRequest> request,
|
required List<AddItemOrderRequest> request,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<Either<OrderFailure, Payment>> createPayment({
|
Future<Either<OrderFailure, Payment>> createPayment({
|
||||||
required PaymentRequest request,
|
required PaymentRequest request,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<Either<OrderFailure, Unit>> voidOrder({
|
Future<Either<OrderFailure, Unit>> voidOrder({
|
||||||
@@ -35,15 +37,18 @@ abstract class IOrderRepository {
|
|||||||
required String reason,
|
required String reason,
|
||||||
String type = "ITEM", // TYPE: ALL, ITEM
|
String type = "ITEM", // TYPE: ALL, ITEM
|
||||||
required List<OrderItem> orderItems,
|
required List<OrderItem> orderItems,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<Either<OrderFailure, Payment>> createSplitBill(
|
Future<Either<OrderFailure, Payment>> createSplitBill(
|
||||||
PaymentSplitBillRequest request,
|
PaymentSplitBillRequest request, {
|
||||||
);
|
String? idempotencyKey,
|
||||||
|
});
|
||||||
|
|
||||||
Future<Either<OrderFailure, Unit>> refundOrder({
|
Future<Either<OrderFailure, Unit>> refundOrder({
|
||||||
required String id,
|
required String id,
|
||||||
required String reason,
|
required String reason,
|
||||||
required int refundAmount,
|
required int refundAmount,
|
||||||
|
String? idempotencyKey,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class ListPaymentMethod with _$ListPaymentMethod {
|
|||||||
required int totalPages,
|
required int totalPages,
|
||||||
}) = _ListPaymentMethod;
|
}) = _ListPaymentMethod;
|
||||||
|
|
||||||
factory ListPaymentMethod.empty() => ListPaymentMethod(
|
factory ListPaymentMethod.empty() => const ListPaymentMethod(
|
||||||
paymentMethods: [],
|
paymentMethods: [],
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
page: 0,
|
page: 0,
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
part of '../printer.dart';
|
||||||
|
|
||||||
|
/// Satu tujuan cetak dalam sekali proses print.
|
||||||
|
///
|
||||||
|
/// Perhatikan kasir, void, dan split bill sengaja menumpang printer `receipt`
|
||||||
|
/// — ketiganya tidak punya printer sendiri di halaman pengaturan.
|
||||||
|
enum PrintStation {
|
||||||
|
receipt,
|
||||||
|
cashier,
|
||||||
|
checker,
|
||||||
|
kitchen,
|
||||||
|
bar,
|
||||||
|
voided,
|
||||||
|
splitBill;
|
||||||
|
|
||||||
|
String get label => switch (this) {
|
||||||
|
PrintStation.receipt => 'Struk Customer',
|
||||||
|
PrintStation.cashier => 'Struk Kasir',
|
||||||
|
PrintStation.checker => 'Checker',
|
||||||
|
PrintStation.kitchen => 'Kitchen',
|
||||||
|
PrintStation.bar => 'Bar',
|
||||||
|
PrintStation.voided => 'Struk Void',
|
||||||
|
PrintStation.splitBill => 'Struk Split Bill',
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Kode printer di tabel `printers`.
|
||||||
|
String get printerCode => switch (this) {
|
||||||
|
PrintStation.receipt ||
|
||||||
|
PrintStation.cashier ||
|
||||||
|
PrintStation.voided ||
|
||||||
|
PrintStation.splitBill => 'receipt',
|
||||||
|
PrintStation.checker => 'checker',
|
||||||
|
PrintStation.kitchen => 'kitchen',
|
||||||
|
PrintStation.bar => 'bar',
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Station yang hanya mencetak item dengan `printerType` tertentu.
|
||||||
|
/// Station lain mencetak seluruh item order.
|
||||||
|
String? get itemPrinterType => switch (this) {
|
||||||
|
PrintStation.kitchen => 'kitchen',
|
||||||
|
PrintStation.bar => 'bar',
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PrintStationStatus {
|
||||||
|
/// Belum dijalankan.
|
||||||
|
pending,
|
||||||
|
|
||||||
|
/// Sedang dikirim ke printer.
|
||||||
|
printing,
|
||||||
|
|
||||||
|
/// Berhasil dicetak.
|
||||||
|
success,
|
||||||
|
|
||||||
|
/// Gagal — lihat `message`.
|
||||||
|
failed,
|
||||||
|
|
||||||
|
/// Sengaja dilewati, misal tidak ada item untuk station tersebut.
|
||||||
|
/// Ini bukan kegagalan dan tidak perlu dicetak ulang.
|
||||||
|
skipped,
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class PrintStationResult with _$PrintStationResult {
|
||||||
|
const PrintStationResult._();
|
||||||
|
|
||||||
|
const factory PrintStationResult({
|
||||||
|
required PrintStation station,
|
||||||
|
required PrintStationStatus status,
|
||||||
|
String? message,
|
||||||
|
@Default(Duration.zero) Duration elapsed,
|
||||||
|
}) = _PrintStationResult;
|
||||||
|
|
||||||
|
factory PrintStationResult.pending(PrintStation station) =>
|
||||||
|
PrintStationResult(station: station, status: PrintStationStatus.pending);
|
||||||
|
|
||||||
|
bool get isSuccess => status == PrintStationStatus.success;
|
||||||
|
bool get isFailed => status == PrintStationStatus.failed;
|
||||||
|
bool get isSkipped => status == PrintStationStatus.skipped;
|
||||||
|
bool get isFinished => isSuccess || isFailed || isSkipped;
|
||||||
|
}
|
||||||
@@ -23,4 +23,21 @@ class Printer with _$Printer {
|
|||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory Printer.fromTest({
|
||||||
|
required String code,
|
||||||
|
required String name,
|
||||||
|
required String address,
|
||||||
|
required String paper,
|
||||||
|
required String type,
|
||||||
|
}) => Printer(
|
||||||
|
id: generateRandomNumber(),
|
||||||
|
code: code,
|
||||||
|
name: name,
|
||||||
|
address: address,
|
||||||
|
paper: paper,
|
||||||
|
type: type,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart' hide Order;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||||
|
|
||||||
import '../../common/api/api_failure.dart';
|
import '../../common/api/api_failure.dart';
|
||||||
|
import '../../common/function/app_function.dart';
|
||||||
|
import '../order/order.dart';
|
||||||
|
|
||||||
part 'printer.freezed.dart';
|
part 'printer.freezed.dart';
|
||||||
|
|
||||||
part 'entities/printer_entity.dart';
|
part 'entities/printer_entity.dart';
|
||||||
|
part 'entities/print_station_result.dart';
|
||||||
part 'failures/printer_failure.dart';
|
part 'failures/printer_failure.dart';
|
||||||
part 'repositories/i_printer_repository.dart';
|
part 'repositories/i_printer_repository.dart';
|
||||||
|
|||||||
@@ -307,6 +307,216 @@ abstract class _Printer implements Printer {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$PrintStationResult {
|
||||||
|
PrintStation get station => throw _privateConstructorUsedError;
|
||||||
|
PrintStationStatus get status => throw _privateConstructorUsedError;
|
||||||
|
String? get message => throw _privateConstructorUsedError;
|
||||||
|
Duration get elapsed => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of PrintStationResult
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$PrintStationResultCopyWith<PrintStationResult> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $PrintStationResultCopyWith<$Res> {
|
||||||
|
factory $PrintStationResultCopyWith(
|
||||||
|
PrintStationResult value,
|
||||||
|
$Res Function(PrintStationResult) then,
|
||||||
|
) = _$PrintStationResultCopyWithImpl<$Res, PrintStationResult>;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
PrintStation station,
|
||||||
|
PrintStationStatus status,
|
||||||
|
String? message,
|
||||||
|
Duration elapsed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$PrintStationResultCopyWithImpl<$Res, $Val extends PrintStationResult>
|
||||||
|
implements $PrintStationResultCopyWith<$Res> {
|
||||||
|
_$PrintStationResultCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of PrintStationResult
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? station = null,
|
||||||
|
Object? status = null,
|
||||||
|
Object? message = freezed,
|
||||||
|
Object? elapsed = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_value.copyWith(
|
||||||
|
station: null == station
|
||||||
|
? _value.station
|
||||||
|
: station // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PrintStation,
|
||||||
|
status: null == status
|
||||||
|
? _value.status
|
||||||
|
: status // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PrintStationStatus,
|
||||||
|
message: freezed == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
elapsed: null == elapsed
|
||||||
|
? _value.elapsed
|
||||||
|
: elapsed // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Duration,
|
||||||
|
)
|
||||||
|
as $Val,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$PrintStationResultImplCopyWith<$Res>
|
||||||
|
implements $PrintStationResultCopyWith<$Res> {
|
||||||
|
factory _$$PrintStationResultImplCopyWith(
|
||||||
|
_$PrintStationResultImpl value,
|
||||||
|
$Res Function(_$PrintStationResultImpl) then,
|
||||||
|
) = __$$PrintStationResultImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
PrintStation station,
|
||||||
|
PrintStationStatus status,
|
||||||
|
String? message,
|
||||||
|
Duration elapsed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$PrintStationResultImplCopyWithImpl<$Res>
|
||||||
|
extends _$PrintStationResultCopyWithImpl<$Res, _$PrintStationResultImpl>
|
||||||
|
implements _$$PrintStationResultImplCopyWith<$Res> {
|
||||||
|
__$$PrintStationResultImplCopyWithImpl(
|
||||||
|
_$PrintStationResultImpl _value,
|
||||||
|
$Res Function(_$PrintStationResultImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of PrintStationResult
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? station = null,
|
||||||
|
Object? status = null,
|
||||||
|
Object? message = freezed,
|
||||||
|
Object? elapsed = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_$PrintStationResultImpl(
|
||||||
|
station: null == station
|
||||||
|
? _value.station
|
||||||
|
: station // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PrintStation,
|
||||||
|
status: null == status
|
||||||
|
? _value.status
|
||||||
|
: status // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PrintStationStatus,
|
||||||
|
message: freezed == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
elapsed: null == elapsed
|
||||||
|
? _value.elapsed
|
||||||
|
: elapsed // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Duration,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$PrintStationResultImpl extends _PrintStationResult {
|
||||||
|
const _$PrintStationResultImpl({
|
||||||
|
required this.station,
|
||||||
|
required this.status,
|
||||||
|
this.message,
|
||||||
|
this.elapsed = Duration.zero,
|
||||||
|
}) : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final PrintStation station;
|
||||||
|
@override
|
||||||
|
final PrintStationStatus status;
|
||||||
|
@override
|
||||||
|
final String? message;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final Duration elapsed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'PrintStationResult(station: $station, status: $status, message: $message, elapsed: $elapsed)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$PrintStationResultImpl &&
|
||||||
|
(identical(other.station, station) || other.station == station) &&
|
||||||
|
(identical(other.status, status) || other.status == status) &&
|
||||||
|
(identical(other.message, message) || other.message == message) &&
|
||||||
|
(identical(other.elapsed, elapsed) || other.elapsed == elapsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, station, status, message, elapsed);
|
||||||
|
|
||||||
|
/// Create a copy of PrintStationResult
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$PrintStationResultImplCopyWith<_$PrintStationResultImpl> get copyWith =>
|
||||||
|
__$$PrintStationResultImplCopyWithImpl<_$PrintStationResultImpl>(
|
||||||
|
this,
|
||||||
|
_$identity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _PrintStationResult extends PrintStationResult {
|
||||||
|
const factory _PrintStationResult({
|
||||||
|
required final PrintStation station,
|
||||||
|
required final PrintStationStatus status,
|
||||||
|
final String? message,
|
||||||
|
final Duration elapsed,
|
||||||
|
}) = _$PrintStationResultImpl;
|
||||||
|
const _PrintStationResult._() : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
PrintStation get station;
|
||||||
|
@override
|
||||||
|
PrintStationStatus get status;
|
||||||
|
@override
|
||||||
|
String? get message;
|
||||||
|
@override
|
||||||
|
Duration get elapsed;
|
||||||
|
|
||||||
|
/// Create a copy of PrintStationResult
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$PrintStationResultImplCopyWith<_$PrintStationResultImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$PrinterFailure {
|
mixin _$PrinterFailure {
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
|
|||||||
@@ -10,4 +10,22 @@ abstract class IPrinterRepository {
|
|||||||
Future<Either<PrinterFailure, Unit>> updatePrinter(Printer printer, int id);
|
Future<Either<PrinterFailure, Unit>> updatePrinter(Printer printer, int id);
|
||||||
Future<Either<PrinterFailure, Unit>> deletePrinter(int id);
|
Future<Either<PrinterFailure, Unit>> deletePrinter(int id);
|
||||||
Future<Either<PrinterFailure, Printer>> getPrinterByCode(String code);
|
Future<Either<PrinterFailure, Printer>> getPrinterByCode(String code);
|
||||||
|
Future<Either<PrinterFailure, bool>> printStruct(
|
||||||
|
Printer printer,
|
||||||
|
List<int> printValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Mencetak [stations] untuk [order], melaporkan status tiap station.
|
||||||
|
///
|
||||||
|
/// Setiap emit berisi daftar lengkap semua station, jadi UI cukup
|
||||||
|
/// menggambar ulang apa adanya.
|
||||||
|
///
|
||||||
|
/// Satu station yang gagal **tidak** membatalkan station lainnya — semuanya
|
||||||
|
/// tetap dicoba. Station Bluetooth dijalankan berurutan karena radionya
|
||||||
|
/// hanya satu, station Network dijalankan paralel karena tidak berebut
|
||||||
|
/// sumber daya dengan Bluetooth.
|
||||||
|
Stream<List<PrintStationResult>> printStations({
|
||||||
|
required Order order,
|
||||||
|
required List<PrintStation> stations,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ class ListProduct with _$ListProduct {
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class Product with _$Product {
|
class Product with _$Product {
|
||||||
|
const Product._();
|
||||||
|
|
||||||
const factory Product({
|
const factory Product({
|
||||||
required String id,
|
required String id,
|
||||||
required String organizationId,
|
required String organizationId,
|
||||||
@@ -33,11 +35,17 @@ class Product with _$Product {
|
|||||||
required String businessType,
|
required String businessType,
|
||||||
required String imageUrl,
|
required String imageUrl,
|
||||||
required String printerType,
|
required String printerType,
|
||||||
|
required bool printToChecker,
|
||||||
required Map<String, dynamic> metadata,
|
required Map<String, dynamic> metadata,
|
||||||
required bool isActive,
|
required bool isActive,
|
||||||
required String createdAt,
|
required String createdAt,
|
||||||
required String updatedAt,
|
required String updatedAt,
|
||||||
required List<ProductVariant> variants,
|
required List<ProductVariant> variants,
|
||||||
|
/// `unit` (default) atau `weight`. Lihat [SellBy].
|
||||||
|
@Default(SellBy.unit) String sellBy,
|
||||||
|
@Default('') String unitId,
|
||||||
|
@Default('') String unitName,
|
||||||
|
@Default('') String unitAbbreviation,
|
||||||
}) = _Product;
|
}) = _Product;
|
||||||
|
|
||||||
factory Product.empty() => const Product(
|
factory Product.empty() => const Product(
|
||||||
@@ -52,12 +60,25 @@ class Product with _$Product {
|
|||||||
businessType: '',
|
businessType: '',
|
||||||
imageUrl: '',
|
imageUrl: '',
|
||||||
printerType: '',
|
printerType: '',
|
||||||
|
printToChecker: false,
|
||||||
metadata: {},
|
metadata: {},
|
||||||
isActive: false,
|
isActive: false,
|
||||||
createdAt: '',
|
createdAt: '',
|
||||||
updatedAt: '',
|
updatedAt: '',
|
||||||
variants: [],
|
variants: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Produk yang dijual per timbangan: `quantity` selalu 1, `weight` wajib.
|
||||||
|
bool get isSoldByWeight => SellBy.isWeight(sellBy);
|
||||||
|
|
||||||
|
/// Singkatan satuan untuk ditampilkan — `ons`. Jatuh ke nama panjang bila
|
||||||
|
/// backend hanya mengirim itu.
|
||||||
|
String get unitLabel =>
|
||||||
|
unitAbbreviation.isNotEmpty ? unitAbbreviation : unitName;
|
||||||
|
|
||||||
|
/// Label harga mengikuti satuan: "Harga per ons".
|
||||||
|
String get priceUnitLabel =>
|
||||||
|
isSoldByWeight && unitLabel.isNotEmpty ? 'per $unitLabel' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|||||||
@@ -1,14 +1,53 @@
|
|||||||
part of '../product.dart';
|
part of '../product.dart';
|
||||||
|
|
||||||
|
/// Satu baris keranjang.
|
||||||
|
///
|
||||||
|
/// Untuk produk timbangan, **satu penimbangan = satu baris**: dua kali menimbang
|
||||||
|
/// Ikan Tude menghasilkan dua baris terpisah dengan [quantity] 1 dan [weight]
|
||||||
|
/// masing-masing. Karena itu baris dibedakan lewat [lineId], bukan lewat
|
||||||
|
/// kombinasi produk + varian.
|
||||||
@freezed
|
@freezed
|
||||||
class ProductQuantity with _$ProductQuantity {
|
class ProductQuantity with _$ProductQuantity {
|
||||||
|
const ProductQuantity._();
|
||||||
|
|
||||||
const factory ProductQuantity({
|
const factory ProductQuantity({
|
||||||
required Product product,
|
required Product product,
|
||||||
ProductVariant? variant,
|
ProductVariant? variant,
|
||||||
required int quantity,
|
required int quantity,
|
||||||
required String notes,
|
required String notes,
|
||||||
|
@Default('') String lineId,
|
||||||
|
|
||||||
|
/// Angka timbangan dalam satuan produk. `null` untuk produk cacah.
|
||||||
|
double? weight,
|
||||||
}) = _ProductQuantity;
|
}) = _ProductQuantity;
|
||||||
|
|
||||||
factory ProductQuantity.empty() =>
|
factory ProductQuantity.empty() =>
|
||||||
ProductQuantity(product: Product.empty(), quantity: 0, notes: '');
|
ProductQuantity(product: Product.empty(), quantity: 0, notes: '');
|
||||||
|
|
||||||
|
bool get isWeighted => weight != null;
|
||||||
|
|
||||||
|
double get unitPrice => product.price + (variant?.priceModifier ?? 0);
|
||||||
|
|
||||||
|
/// Harga baris: `weight × unit_price` untuk timbangan, `quantity × unit_price`
|
||||||
|
/// untuk produk cacah.
|
||||||
|
double get lineTotal =>
|
||||||
|
isWeighted ? unitPrice * weight! : unitPrice * quantity;
|
||||||
|
|
||||||
|
/// `4,2 ons` atau `2` — dipakai di kolom Qty keranjang.
|
||||||
|
String get quantityLabel {
|
||||||
|
if (!isWeighted) return quantity.toString();
|
||||||
|
final label = product.unitLabel;
|
||||||
|
final value = weight!.weightFormat;
|
||||||
|
return label.isEmpty ? value : '$value $label';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProductQuantityListExt on List<ProductQuantity> {
|
||||||
|
/// Total keranjang: baris berbobot pakai `weight × harga`, baris cacah pakai
|
||||||
|
/// `quantity × harga`.
|
||||||
|
double get cartTotal => fold<double>(0, (sum, item) => sum + item.lineTotal);
|
||||||
|
|
||||||
|
/// Baris berbobot dihitung satu — satu penimbangan, satu barang.
|
||||||
|
int get cartItemCount =>
|
||||||
|
fold<int>(0, (sum, item) => sum + (item.isWeighted ? 1 : item.quantity));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
import '../../common/api/api_failure.dart';
|
import '../../common/api/api_failure.dart';
|
||||||
|
import '../../common/extension/extension.dart';
|
||||||
|
import '../../common/types/sell_by.dart';
|
||||||
|
|
||||||
part 'product.freezed.dart';
|
part 'product.freezed.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -279,12 +279,19 @@ mixin _$Product {
|
|||||||
String get businessType => throw _privateConstructorUsedError;
|
String get businessType => throw _privateConstructorUsedError;
|
||||||
String get imageUrl => throw _privateConstructorUsedError;
|
String get imageUrl => throw _privateConstructorUsedError;
|
||||||
String get printerType => throw _privateConstructorUsedError;
|
String get printerType => throw _privateConstructorUsedError;
|
||||||
|
bool get printToChecker => throw _privateConstructorUsedError;
|
||||||
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
|
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
|
||||||
bool get isActive => throw _privateConstructorUsedError;
|
bool get isActive => throw _privateConstructorUsedError;
|
||||||
String get createdAt => throw _privateConstructorUsedError;
|
String get createdAt => throw _privateConstructorUsedError;
|
||||||
String get updatedAt => throw _privateConstructorUsedError;
|
String get updatedAt => throw _privateConstructorUsedError;
|
||||||
List<ProductVariant> get variants => throw _privateConstructorUsedError;
|
List<ProductVariant> get variants => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// `unit` (default) atau `weight`. Lihat [SellBy].
|
||||||
|
String get sellBy => throw _privateConstructorUsedError;
|
||||||
|
String get unitId => throw _privateConstructorUsedError;
|
||||||
|
String get unitName => throw _privateConstructorUsedError;
|
||||||
|
String get unitAbbreviation => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of Product
|
/// Create a copy of Product
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -308,11 +315,16 @@ abstract class $ProductCopyWith<$Res> {
|
|||||||
String businessType,
|
String businessType,
|
||||||
String imageUrl,
|
String imageUrl,
|
||||||
String printerType,
|
String printerType,
|
||||||
|
bool printToChecker,
|
||||||
Map<String, dynamic> metadata,
|
Map<String, dynamic> metadata,
|
||||||
bool isActive,
|
bool isActive,
|
||||||
String createdAt,
|
String createdAt,
|
||||||
String updatedAt,
|
String updatedAt,
|
||||||
List<ProductVariant> variants,
|
List<ProductVariant> variants,
|
||||||
|
String sellBy,
|
||||||
|
String unitId,
|
||||||
|
String unitName,
|
||||||
|
String unitAbbreviation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,11 +354,16 @@ class _$ProductCopyWithImpl<$Res, $Val extends Product>
|
|||||||
Object? businessType = null,
|
Object? businessType = null,
|
||||||
Object? imageUrl = null,
|
Object? imageUrl = null,
|
||||||
Object? printerType = null,
|
Object? printerType = null,
|
||||||
|
Object? printToChecker = null,
|
||||||
Object? metadata = null,
|
Object? metadata = null,
|
||||||
Object? isActive = null,
|
Object? isActive = null,
|
||||||
Object? createdAt = null,
|
Object? createdAt = null,
|
||||||
Object? updatedAt = null,
|
Object? updatedAt = null,
|
||||||
Object? variants = null,
|
Object? variants = null,
|
||||||
|
Object? sellBy = null,
|
||||||
|
Object? unitId = null,
|
||||||
|
Object? unitName = null,
|
||||||
|
Object? unitAbbreviation = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -394,6 +411,10 @@ class _$ProductCopyWithImpl<$Res, $Val extends Product>
|
|||||||
? _value.printerType
|
? _value.printerType
|
||||||
: printerType // ignore: cast_nullable_to_non_nullable
|
: printerType // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
printToChecker: null == printToChecker
|
||||||
|
? _value.printToChecker
|
||||||
|
: printToChecker // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
metadata: null == metadata
|
metadata: null == metadata
|
||||||
? _value.metadata
|
? _value.metadata
|
||||||
: metadata // ignore: cast_nullable_to_non_nullable
|
: metadata // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -414,6 +435,22 @@ class _$ProductCopyWithImpl<$Res, $Val extends Product>
|
|||||||
? _value.variants
|
? _value.variants
|
||||||
: variants // ignore: cast_nullable_to_non_nullable
|
: variants // ignore: cast_nullable_to_non_nullable
|
||||||
as List<ProductVariant>,
|
as List<ProductVariant>,
|
||||||
|
sellBy: null == sellBy
|
||||||
|
? _value.sellBy
|
||||||
|
: sellBy // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitId: null == unitId
|
||||||
|
? _value.unitId
|
||||||
|
: unitId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitName: null == unitName
|
||||||
|
? _value.unitName
|
||||||
|
: unitName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitAbbreviation: null == unitAbbreviation
|
||||||
|
? _value.unitAbbreviation
|
||||||
|
: unitAbbreviation // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -440,11 +477,16 @@ abstract class _$$ProductImplCopyWith<$Res> implements $ProductCopyWith<$Res> {
|
|||||||
String businessType,
|
String businessType,
|
||||||
String imageUrl,
|
String imageUrl,
|
||||||
String printerType,
|
String printerType,
|
||||||
|
bool printToChecker,
|
||||||
Map<String, dynamic> metadata,
|
Map<String, dynamic> metadata,
|
||||||
bool isActive,
|
bool isActive,
|
||||||
String createdAt,
|
String createdAt,
|
||||||
String updatedAt,
|
String updatedAt,
|
||||||
List<ProductVariant> variants,
|
List<ProductVariant> variants,
|
||||||
|
String sellBy,
|
||||||
|
String unitId,
|
||||||
|
String unitName,
|
||||||
|
String unitAbbreviation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,11 +515,16 @@ class __$$ProductImplCopyWithImpl<$Res>
|
|||||||
Object? businessType = null,
|
Object? businessType = null,
|
||||||
Object? imageUrl = null,
|
Object? imageUrl = null,
|
||||||
Object? printerType = null,
|
Object? printerType = null,
|
||||||
|
Object? printToChecker = null,
|
||||||
Object? metadata = null,
|
Object? metadata = null,
|
||||||
Object? isActive = null,
|
Object? isActive = null,
|
||||||
Object? createdAt = null,
|
Object? createdAt = null,
|
||||||
Object? updatedAt = null,
|
Object? updatedAt = null,
|
||||||
Object? variants = null,
|
Object? variants = null,
|
||||||
|
Object? sellBy = null,
|
||||||
|
Object? unitId = null,
|
||||||
|
Object? unitName = null,
|
||||||
|
Object? unitAbbreviation = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$ProductImpl(
|
_$ProductImpl(
|
||||||
@@ -525,6 +572,10 @@ class __$$ProductImplCopyWithImpl<$Res>
|
|||||||
? _value.printerType
|
? _value.printerType
|
||||||
: printerType // ignore: cast_nullable_to_non_nullable
|
: printerType // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
printToChecker: null == printToChecker
|
||||||
|
? _value.printToChecker
|
||||||
|
: printToChecker // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
metadata: null == metadata
|
metadata: null == metadata
|
||||||
? _value._metadata
|
? _value._metadata
|
||||||
: metadata // ignore: cast_nullable_to_non_nullable
|
: metadata // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -545,6 +596,22 @@ class __$$ProductImplCopyWithImpl<$Res>
|
|||||||
? _value._variants
|
? _value._variants
|
||||||
: variants // ignore: cast_nullable_to_non_nullable
|
: variants // ignore: cast_nullable_to_non_nullable
|
||||||
as List<ProductVariant>,
|
as List<ProductVariant>,
|
||||||
|
sellBy: null == sellBy
|
||||||
|
? _value.sellBy
|
||||||
|
: sellBy // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitId: null == unitId
|
||||||
|
? _value.unitId
|
||||||
|
: unitId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitName: null == unitName
|
||||||
|
? _value.unitName
|
||||||
|
: unitName // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
unitAbbreviation: null == unitAbbreviation
|
||||||
|
? _value.unitAbbreviation
|
||||||
|
: unitAbbreviation // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -552,7 +619,7 @@ class __$$ProductImplCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
class _$ProductImpl extends _Product with DiagnosticableTreeMixin {
|
||||||
const _$ProductImpl({
|
const _$ProductImpl({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.organizationId,
|
required this.organizationId,
|
||||||
@@ -565,13 +632,19 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
required this.businessType,
|
required this.businessType,
|
||||||
required this.imageUrl,
|
required this.imageUrl,
|
||||||
required this.printerType,
|
required this.printerType,
|
||||||
|
required this.printToChecker,
|
||||||
required final Map<String, dynamic> metadata,
|
required final Map<String, dynamic> metadata,
|
||||||
required this.isActive,
|
required this.isActive,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required final List<ProductVariant> variants,
|
required final List<ProductVariant> variants,
|
||||||
|
this.sellBy = SellBy.unit,
|
||||||
|
this.unitId = '',
|
||||||
|
this.unitName = '',
|
||||||
|
this.unitAbbreviation = '',
|
||||||
}) : _metadata = metadata,
|
}) : _metadata = metadata,
|
||||||
_variants = variants;
|
_variants = variants,
|
||||||
|
super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String id;
|
final String id;
|
||||||
@@ -595,6 +668,8 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
final String imageUrl;
|
final String imageUrl;
|
||||||
@override
|
@override
|
||||||
final String printerType;
|
final String printerType;
|
||||||
|
@override
|
||||||
|
final bool printToChecker;
|
||||||
final Map<String, dynamic> _metadata;
|
final Map<String, dynamic> _metadata;
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> get metadata {
|
Map<String, dynamic> get metadata {
|
||||||
@@ -617,9 +692,23 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
return EqualUnmodifiableListView(_variants);
|
return EqualUnmodifiableListView(_variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `unit` (default) atau `weight`. Lihat [SellBy].
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String sellBy;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String unitId;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String unitName;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String unitAbbreviation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
return 'Product(id: $id, organizationId: $organizationId, categoryId: $categoryId, sku: $sku, name: $name, description: $description, price: $price, cost: $cost, businessType: $businessType, imageUrl: $imageUrl, printerType: $printerType, metadata: $metadata, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, variants: $variants)';
|
return 'Product(id: $id, organizationId: $organizationId, categoryId: $categoryId, sku: $sku, name: $name, description: $description, price: $price, cost: $cost, businessType: $businessType, imageUrl: $imageUrl, printerType: $printerType, printToChecker: $printToChecker, metadata: $metadata, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, variants: $variants, sellBy: $sellBy, unitId: $unitId, unitName: $unitName, unitAbbreviation: $unitAbbreviation)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -638,11 +727,16 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
..add(DiagnosticsProperty('businessType', businessType))
|
..add(DiagnosticsProperty('businessType', businessType))
|
||||||
..add(DiagnosticsProperty('imageUrl', imageUrl))
|
..add(DiagnosticsProperty('imageUrl', imageUrl))
|
||||||
..add(DiagnosticsProperty('printerType', printerType))
|
..add(DiagnosticsProperty('printerType', printerType))
|
||||||
|
..add(DiagnosticsProperty('printToChecker', printToChecker))
|
||||||
..add(DiagnosticsProperty('metadata', metadata))
|
..add(DiagnosticsProperty('metadata', metadata))
|
||||||
..add(DiagnosticsProperty('isActive', isActive))
|
..add(DiagnosticsProperty('isActive', isActive))
|
||||||
..add(DiagnosticsProperty('createdAt', createdAt))
|
..add(DiagnosticsProperty('createdAt', createdAt))
|
||||||
..add(DiagnosticsProperty('updatedAt', updatedAt))
|
..add(DiagnosticsProperty('updatedAt', updatedAt))
|
||||||
..add(DiagnosticsProperty('variants', variants));
|
..add(DiagnosticsProperty('variants', variants))
|
||||||
|
..add(DiagnosticsProperty('sellBy', sellBy))
|
||||||
|
..add(DiagnosticsProperty('unitId', unitId))
|
||||||
|
..add(DiagnosticsProperty('unitName', unitName))
|
||||||
|
..add(DiagnosticsProperty('unitAbbreviation', unitAbbreviation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -667,6 +761,8 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
other.imageUrl == imageUrl) &&
|
other.imageUrl == imageUrl) &&
|
||||||
(identical(other.printerType, printerType) ||
|
(identical(other.printerType, printerType) ||
|
||||||
other.printerType == printerType) &&
|
other.printerType == printerType) &&
|
||||||
|
(identical(other.printToChecker, printToChecker) ||
|
||||||
|
other.printToChecker == printToChecker) &&
|
||||||
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
||||||
(identical(other.isActive, isActive) ||
|
(identical(other.isActive, isActive) ||
|
||||||
other.isActive == isActive) &&
|
other.isActive == isActive) &&
|
||||||
@@ -674,11 +770,17 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
other.createdAt == createdAt) &&
|
other.createdAt == createdAt) &&
|
||||||
(identical(other.updatedAt, updatedAt) ||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
other.updatedAt == updatedAt) &&
|
other.updatedAt == updatedAt) &&
|
||||||
const DeepCollectionEquality().equals(other._variants, _variants));
|
const DeepCollectionEquality().equals(other._variants, _variants) &&
|
||||||
|
(identical(other.sellBy, sellBy) || other.sellBy == sellBy) &&
|
||||||
|
(identical(other.unitId, unitId) || other.unitId == unitId) &&
|
||||||
|
(identical(other.unitName, unitName) ||
|
||||||
|
other.unitName == unitName) &&
|
||||||
|
(identical(other.unitAbbreviation, unitAbbreviation) ||
|
||||||
|
other.unitAbbreviation == unitAbbreviation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hashAll([
|
||||||
runtimeType,
|
runtimeType,
|
||||||
id,
|
id,
|
||||||
organizationId,
|
organizationId,
|
||||||
@@ -691,12 +793,17 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
businessType,
|
businessType,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
printerType,
|
printerType,
|
||||||
|
printToChecker,
|
||||||
const DeepCollectionEquality().hash(_metadata),
|
const DeepCollectionEquality().hash(_metadata),
|
||||||
isActive,
|
isActive,
|
||||||
createdAt,
|
createdAt,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
const DeepCollectionEquality().hash(_variants),
|
const DeepCollectionEquality().hash(_variants),
|
||||||
);
|
sellBy,
|
||||||
|
unitId,
|
||||||
|
unitName,
|
||||||
|
unitAbbreviation,
|
||||||
|
]);
|
||||||
|
|
||||||
/// Create a copy of Product
|
/// Create a copy of Product
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -707,7 +814,7 @@ class _$ProductImpl with DiagnosticableTreeMixin implements _Product {
|
|||||||
__$$ProductImplCopyWithImpl<_$ProductImpl>(this, _$identity);
|
__$$ProductImplCopyWithImpl<_$ProductImpl>(this, _$identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Product implements Product {
|
abstract class _Product extends Product {
|
||||||
const factory _Product({
|
const factory _Product({
|
||||||
required final String id,
|
required final String id,
|
||||||
required final String organizationId,
|
required final String organizationId,
|
||||||
@@ -720,12 +827,18 @@ abstract class _Product implements Product {
|
|||||||
required final String businessType,
|
required final String businessType,
|
||||||
required final String imageUrl,
|
required final String imageUrl,
|
||||||
required final String printerType,
|
required final String printerType,
|
||||||
|
required final bool printToChecker,
|
||||||
required final Map<String, dynamic> metadata,
|
required final Map<String, dynamic> metadata,
|
||||||
required final bool isActive,
|
required final bool isActive,
|
||||||
required final String createdAt,
|
required final String createdAt,
|
||||||
required final String updatedAt,
|
required final String updatedAt,
|
||||||
required final List<ProductVariant> variants,
|
required final List<ProductVariant> variants,
|
||||||
|
final String sellBy,
|
||||||
|
final String unitId,
|
||||||
|
final String unitName,
|
||||||
|
final String unitAbbreviation,
|
||||||
}) = _$ProductImpl;
|
}) = _$ProductImpl;
|
||||||
|
const _Product._() : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id;
|
String get id;
|
||||||
@@ -750,6 +863,8 @@ abstract class _Product implements Product {
|
|||||||
@override
|
@override
|
||||||
String get printerType;
|
String get printerType;
|
||||||
@override
|
@override
|
||||||
|
bool get printToChecker;
|
||||||
|
@override
|
||||||
Map<String, dynamic> get metadata;
|
Map<String, dynamic> get metadata;
|
||||||
@override
|
@override
|
||||||
bool get isActive;
|
bool get isActive;
|
||||||
@@ -760,6 +875,16 @@ abstract class _Product implements Product {
|
|||||||
@override
|
@override
|
||||||
List<ProductVariant> get variants;
|
List<ProductVariant> get variants;
|
||||||
|
|
||||||
|
/// `unit` (default) atau `weight`. Lihat [SellBy].
|
||||||
|
@override
|
||||||
|
String get sellBy;
|
||||||
|
@override
|
||||||
|
String get unitId;
|
||||||
|
@override
|
||||||
|
String get unitName;
|
||||||
|
@override
|
||||||
|
String get unitAbbreviation;
|
||||||
|
|
||||||
/// Create a copy of Product
|
/// Create a copy of Product
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -1098,6 +1223,10 @@ mixin _$ProductQuantity {
|
|||||||
ProductVariant? get variant => throw _privateConstructorUsedError;
|
ProductVariant? get variant => throw _privateConstructorUsedError;
|
||||||
int get quantity => throw _privateConstructorUsedError;
|
int get quantity => throw _privateConstructorUsedError;
|
||||||
String get notes => throw _privateConstructorUsedError;
|
String get notes => throw _privateConstructorUsedError;
|
||||||
|
String get lineId => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Angka timbangan dalam satuan produk. `null` untuk produk cacah.
|
||||||
|
double? get weight => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of ProductQuantity
|
/// Create a copy of ProductQuantity
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1118,6 +1247,8 @@ abstract class $ProductQuantityCopyWith<$Res> {
|
|||||||
ProductVariant? variant,
|
ProductVariant? variant,
|
||||||
int quantity,
|
int quantity,
|
||||||
String notes,
|
String notes,
|
||||||
|
String lineId,
|
||||||
|
double? weight,
|
||||||
});
|
});
|
||||||
|
|
||||||
$ProductCopyWith<$Res> get product;
|
$ProductCopyWith<$Res> get product;
|
||||||
@@ -1143,6 +1274,8 @@ class _$ProductQuantityCopyWithImpl<$Res, $Val extends ProductQuantity>
|
|||||||
Object? variant = freezed,
|
Object? variant = freezed,
|
||||||
Object? quantity = null,
|
Object? quantity = null,
|
||||||
Object? notes = null,
|
Object? notes = null,
|
||||||
|
Object? lineId = null,
|
||||||
|
Object? weight = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_value.copyWith(
|
_value.copyWith(
|
||||||
@@ -1162,6 +1295,14 @@ class _$ProductQuantityCopyWithImpl<$Res, $Val extends ProductQuantity>
|
|||||||
? _value.notes
|
? _value.notes
|
||||||
: notes // ignore: cast_nullable_to_non_nullable
|
: notes // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
lineId: null == lineId
|
||||||
|
? _value.lineId
|
||||||
|
: lineId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
)
|
)
|
||||||
as $Val,
|
as $Val,
|
||||||
);
|
);
|
||||||
@@ -1206,6 +1347,8 @@ abstract class _$$ProductQuantityImplCopyWith<$Res>
|
|||||||
ProductVariant? variant,
|
ProductVariant? variant,
|
||||||
int quantity,
|
int quantity,
|
||||||
String notes,
|
String notes,
|
||||||
|
String lineId,
|
||||||
|
double? weight,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1232,6 +1375,8 @@ class __$$ProductQuantityImplCopyWithImpl<$Res>
|
|||||||
Object? variant = freezed,
|
Object? variant = freezed,
|
||||||
Object? quantity = null,
|
Object? quantity = null,
|
||||||
Object? notes = null,
|
Object? notes = null,
|
||||||
|
Object? lineId = null,
|
||||||
|
Object? weight = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(
|
return _then(
|
||||||
_$ProductQuantityImpl(
|
_$ProductQuantityImpl(
|
||||||
@@ -1251,6 +1396,14 @@ class __$$ProductQuantityImplCopyWithImpl<$Res>
|
|||||||
? _value.notes
|
? _value.notes
|
||||||
: notes // ignore: cast_nullable_to_non_nullable
|
: notes // ignore: cast_nullable_to_non_nullable
|
||||||
as String,
|
as String,
|
||||||
|
lineId: null == lineId
|
||||||
|
? _value.lineId
|
||||||
|
: lineId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
weight: freezed == weight
|
||||||
|
? _value.weight
|
||||||
|
: weight // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1258,15 +1411,16 @@ class __$$ProductQuantityImplCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$ProductQuantityImpl
|
class _$ProductQuantityImpl extends _ProductQuantity
|
||||||
with DiagnosticableTreeMixin
|
with DiagnosticableTreeMixin {
|
||||||
implements _ProductQuantity {
|
|
||||||
const _$ProductQuantityImpl({
|
const _$ProductQuantityImpl({
|
||||||
required this.product,
|
required this.product,
|
||||||
this.variant,
|
this.variant,
|
||||||
required this.quantity,
|
required this.quantity,
|
||||||
required this.notes,
|
required this.notes,
|
||||||
});
|
this.lineId = '',
|
||||||
|
this.weight,
|
||||||
|
}) : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Product product;
|
final Product product;
|
||||||
@@ -1276,10 +1430,17 @@ class _$ProductQuantityImpl
|
|||||||
final int quantity;
|
final int quantity;
|
||||||
@override
|
@override
|
||||||
final String notes;
|
final String notes;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String lineId;
|
||||||
|
|
||||||
|
/// Angka timbangan dalam satuan produk. `null` untuk produk cacah.
|
||||||
|
@override
|
||||||
|
final double? weight;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
return 'ProductQuantity(product: $product, variant: $variant, quantity: $quantity, notes: $notes)';
|
return 'ProductQuantity(product: $product, variant: $variant, quantity: $quantity, notes: $notes, lineId: $lineId, weight: $weight)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1290,7 +1451,9 @@ class _$ProductQuantityImpl
|
|||||||
..add(DiagnosticsProperty('product', product))
|
..add(DiagnosticsProperty('product', product))
|
||||||
..add(DiagnosticsProperty('variant', variant))
|
..add(DiagnosticsProperty('variant', variant))
|
||||||
..add(DiagnosticsProperty('quantity', quantity))
|
..add(DiagnosticsProperty('quantity', quantity))
|
||||||
..add(DiagnosticsProperty('notes', notes));
|
..add(DiagnosticsProperty('notes', notes))
|
||||||
|
..add(DiagnosticsProperty('lineId', lineId))
|
||||||
|
..add(DiagnosticsProperty('weight', weight));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1302,12 +1465,21 @@ class _$ProductQuantityImpl
|
|||||||
(identical(other.variant, variant) || other.variant == variant) &&
|
(identical(other.variant, variant) || other.variant == variant) &&
|
||||||
(identical(other.quantity, quantity) ||
|
(identical(other.quantity, quantity) ||
|
||||||
other.quantity == quantity) &&
|
other.quantity == quantity) &&
|
||||||
(identical(other.notes, notes) || other.notes == notes));
|
(identical(other.notes, notes) || other.notes == notes) &&
|
||||||
|
(identical(other.lineId, lineId) || other.lineId == lineId) &&
|
||||||
|
(identical(other.weight, weight) || other.weight == weight));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(
|
||||||
Object.hash(runtimeType, product, variant, quantity, notes);
|
runtimeType,
|
||||||
|
product,
|
||||||
|
variant,
|
||||||
|
quantity,
|
||||||
|
notes,
|
||||||
|
lineId,
|
||||||
|
weight,
|
||||||
|
);
|
||||||
|
|
||||||
/// Create a copy of ProductQuantity
|
/// Create a copy of ProductQuantity
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1321,13 +1493,16 @@ class _$ProductQuantityImpl
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _ProductQuantity implements ProductQuantity {
|
abstract class _ProductQuantity extends ProductQuantity {
|
||||||
const factory _ProductQuantity({
|
const factory _ProductQuantity({
|
||||||
required final Product product,
|
required final Product product,
|
||||||
final ProductVariant? variant,
|
final ProductVariant? variant,
|
||||||
required final int quantity,
|
required final int quantity,
|
||||||
required final String notes,
|
required final String notes,
|
||||||
|
final String lineId,
|
||||||
|
final double? weight,
|
||||||
}) = _$ProductQuantityImpl;
|
}) = _$ProductQuantityImpl;
|
||||||
|
const _ProductQuantity._() : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Product get product;
|
Product get product;
|
||||||
@@ -1337,6 +1512,12 @@ abstract class _ProductQuantity implements ProductQuantity {
|
|||||||
int get quantity;
|
int get quantity;
|
||||||
@override
|
@override
|
||||||
String get notes;
|
String get notes;
|
||||||
|
@override
|
||||||
|
String get lineId;
|
||||||
|
|
||||||
|
/// Angka timbangan dalam satuan produk. `null` untuk produk cacah.
|
||||||
|
@override
|
||||||
|
double? get weight;
|
||||||
|
|
||||||
/// Create a copy of ProductQuantity
|
/// Create a copy of ProductQuantity
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|||||||
@@ -10,8 +10,13 @@ class ListTable with _$ListTable {
|
|||||||
required int totalPages,
|
required int totalPages,
|
||||||
}) = _ListTable;
|
}) = _ListTable;
|
||||||
|
|
||||||
factory ListTable.empty() =>
|
factory ListTable.empty() => const ListTable(
|
||||||
ListTable(tables: [], totalCount: 0, page: 0, limit: 0, totalPages: 0);
|
tables: [],
|
||||||
|
totalCount: 0,
|
||||||
|
page: 0,
|
||||||
|
limit: 0,
|
||||||
|
totalPages: 0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
part of '../unit.dart';
|
||||||
|
|
||||||
|
/// Satuan timbangan milik organisasi — ons, kg, gram.
|
||||||
|
///
|
||||||
|
/// [abbreviation] yang dipakai POS untuk mencetak `4,2 ons` di struk.
|
||||||
|
@freezed
|
||||||
|
class ProductUnit with _$ProductUnit {
|
||||||
|
const factory ProductUnit({
|
||||||
|
required String id,
|
||||||
|
required String organizationId,
|
||||||
|
required String name,
|
||||||
|
required String abbreviation,
|
||||||
|
required bool isActive,
|
||||||
|
}) = _ProductUnit;
|
||||||
|
|
||||||
|
factory ProductUnit.empty() => const ProductUnit(
|
||||||
|
id: '',
|
||||||
|
organizationId: '',
|
||||||
|
name: '',
|
||||||
|
abbreviation: '',
|
||||||
|
isActive: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
part of '../unit.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class UnitFailure with _$UnitFailure {
|
||||||
|
const factory UnitFailure.serverError(ApiFailure failure) = _ServerError;
|
||||||
|
const factory UnitFailure.unexpectedError() = _UnexpectedError;
|
||||||
|
const factory UnitFailure.empty() = _Empty;
|
||||||
|
const factory UnitFailure.localStorageError(String erroMessage) =
|
||||||
|
_LocalStorageError;
|
||||||
|
const factory UnitFailure.dynamicErrorMessage(String erroMessage) =
|
||||||
|
_DynamicErrorMessage;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
part of '../unit.dart';
|
||||||
|
|
||||||
|
abstract class IUnitRepository {
|
||||||
|
/// Ambil satuan dari server lalu simpan ke cache lokal.
|
||||||
|
/// Bila server tidak terjangkau, kembalikan isi cache supaya POS tetap bisa
|
||||||
|
/// mencetak `4,2 ons` saat offline.
|
||||||
|
Future<Either<UnitFailure, List<ProductUnit>>> getUnits({
|
||||||
|
bool forceRefresh = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<List<ProductUnit>> cachedUnits();
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
import '../../common/api/api_failure.dart';
|
||||||
|
|
||||||
|
part 'unit.freezed.dart';
|
||||||
|
|
||||||
|
part 'entities/unit_entity.dart';
|
||||||
|
part 'failures/unit_failure.dart';
|
||||||
|
part 'repositories/i_unit_repository.dart';
|
||||||
File diff suppressed because it is too large
Load Diff
+9
-2
@@ -2,6 +2,7 @@ import 'package:injectable/injectable.dart';
|
|||||||
|
|
||||||
abstract class Env {
|
abstract class Env {
|
||||||
String get baseUrl;
|
String get baseUrl;
|
||||||
|
String get dbName;
|
||||||
// add getter here...
|
// add getter here...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,12 +10,18 @@ abstract class Env {
|
|||||||
@dev
|
@dev
|
||||||
class DevEnv implements Env {
|
class DevEnv implements Env {
|
||||||
@override
|
@override
|
||||||
String get baseUrl => 'https://api-pos.apskel.id'; // example value
|
String get baseUrl => 'https://enaklo-pos-api.altru.id';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get dbName => "apskel_pos_staging.db";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable(as: Env)
|
@Injectable(as: Env)
|
||||||
@prod
|
@prod
|
||||||
class ProdEnv implements Env {
|
class ProdEnv implements Env {
|
||||||
@override
|
@override
|
||||||
String get baseUrl => 'https://api-pos.apskel.id';
|
String get baseUrl => 'https://enaklo-pos-api.altru.id';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get dbName => "apskel_pos_prod.db";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final dashboard = DashboardAnalyticDto.fromJson(
|
final dashboard = DashboardAnalyticDto.fromJson(
|
||||||
@@ -65,7 +65,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final sales = SalesAnalyticDto.fromJson(
|
final sales = SalesAnalyticDto.fromJson(
|
||||||
@@ -94,7 +94,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final products = ProductAnalyticDto.fromJson(
|
final products = ProductAnalyticDto.fromJson(
|
||||||
@@ -123,7 +123,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final paymentMethods = PaymentMethodAnalyticDto.fromJson(
|
final paymentMethods = PaymentMethodAnalyticDto.fromJson(
|
||||||
@@ -152,7 +152,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final profitLoss = ProfitLossAnalyticDto.fromJson(
|
final profitLoss = ProfitLossAnalyticDto.fromJson(
|
||||||
@@ -181,7 +181,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final categories = CategoryAnalyticDto.fromJson(
|
final categories = CategoryAnalyticDto.fromJson(
|
||||||
@@ -211,7 +211,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['success'] == false) {
|
if (response.data['success'] == false) {
|
||||||
return DC.error(AnalyticFailure.unexpectedError());
|
return DC.error(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
final inventory = InventoryAnalyticDto.fromJson(
|
final inventory = InventoryAnalyticDto.fromJson(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user