Compare commits
56
Commits
670a0b6414
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
457ed38827 | ||
|
|
d34487d883 | ||
|
|
f4cdfcb96f | ||
|
|
0bccec8a1e | ||
|
|
86d2196a04 | ||
|
|
aa25de8da7 | ||
|
|
96387c08f4 | ||
|
|
e585cf4292 | ||
|
|
290360674f | ||
|
|
1fbacae1f4 | ||
|
|
613b216c04 | ||
|
|
455a6afd70 | ||
|
|
2813011fac | ||
|
|
83af323a2f | ||
|
|
cef1f79032 | ||
|
|
59a8d7f661 | ||
|
|
a58d1040af | ||
|
|
72a464b4c0 | ||
|
|
5b980d237f | ||
|
|
c12d6525fa | ||
|
|
44402140fb | ||
|
|
04811015b6 | ||
|
|
f104390141 | ||
|
|
3022d8de9f | ||
|
|
6acfbdc4ca | ||
|
|
7b12793618 | ||
|
|
a6ac12c36a | ||
|
|
e1e702bd76 | ||
|
|
fabb77d2b9 | ||
|
|
f18a70b312 | ||
|
|
78914d7140 | ||
|
|
937b98e45b | ||
|
|
c1be16845e | ||
|
|
dcb929cde5 | ||
|
|
8aa326c513 | ||
|
|
bd02f389e5 | ||
|
|
5534aad0d4 | ||
|
|
6599e6fe7c | ||
|
|
3624f75bea | ||
|
|
0d91d43866 | ||
|
|
dcd739e4a4 | ||
|
|
631ac4bc46 | ||
|
|
7007f39766 | ||
|
|
79585b253d | ||
|
|
cc8012354f | ||
|
|
2c75fcf582 | ||
|
|
34c0ad5411 | ||
|
|
f37814fec8 | ||
|
|
0c375de6e4 | ||
|
|
f87309af3f | ||
|
|
96d0b91984 | ||
|
|
e20dc53fb8 | ||
|
|
c09822b470 | ||
|
|
6a9a26f71c | ||
|
|
b0006ee6bc | ||
|
|
b34428965e |
@@ -2,6 +2,8 @@ plugins {
|
|||||||
id "com.android.application"
|
id "com.android.application"
|
||||||
id "kotlin-android"
|
id "kotlin-android"
|
||||||
id "dev.flutter.flutter-gradle-plugin"
|
id "dev.flutter.flutter-gradle-plugin"
|
||||||
|
id "com.google.gms.google-services"
|
||||||
|
id "com.google.firebase.crashlytics"
|
||||||
}
|
}
|
||||||
|
|
||||||
def localProperties = new Properties()
|
def localProperties = new Properties()
|
||||||
@@ -45,7 +47,7 @@ android {
|
|||||||
applicationId "com.appscale.pos"
|
applicationId "com.appscale.pos"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion 21
|
minSdkVersion flutter.minSdkVersion
|
||||||
targetSdkVersion 35
|
targetSdkVersion 35
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
@@ -69,4 +71,6 @@ dependencies {
|
|||||||
//multiDex
|
//multiDex
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
|
implementation platform('com.google.firebase:firebase-bom:34.2.0')
|
||||||
|
implementation 'com.google.firebase:firebase-crashlytics'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "1094037575589",
|
||||||
|
"project_id": "apskel-pos",
|
||||||
|
"storage_bucket": "apskel-pos.firebasestorage.app"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:1094037575589:android:5157684038a971a3d3fa4c",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.appscale.pos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyAle1ZcO4Z7wtNJUHkHEVkuBHr6mHcvPvI"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ buildscript {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
classpath 'com.google.gms:google-services:4.4.3'
|
||||||
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ class _CustomDatePickerState extends State<CustomDatePicker> {
|
|||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
suffixIcon: Padding(
|
suffixIcon: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8, horizontal: 16),
|
||||||
child: Assets.icons.calendar.svg(),
|
child: Assets.icons.calendar.svg(),
|
||||||
),
|
),
|
||||||
prefix: widget.prefix,
|
prefix: widget.prefix,
|
||||||
|
|||||||
@@ -0,0 +1,409 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
|
||||||
|
|
||||||
|
class DateRangePickerModal {
|
||||||
|
static Future<DateRangePickerSelectionChangedArgs?> show({
|
||||||
|
required BuildContext context,
|
||||||
|
String title = 'Pilih Rentang Tanggal',
|
||||||
|
DateTime? initialStartDate,
|
||||||
|
DateTime? initialEndDate,
|
||||||
|
DateTime? minDate,
|
||||||
|
DateTime? maxDate,
|
||||||
|
String confirmText = 'Pilih',
|
||||||
|
String cancelText = 'Batal',
|
||||||
|
Color primaryColor = Colors.blue,
|
||||||
|
Function(DateTime? startDate, DateTime? endDate)? onChanged,
|
||||||
|
}) async {
|
||||||
|
return await showDialog<DateRangePickerSelectionChangedArgs?>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) => _DateRangePickerDialog(
|
||||||
|
title: title,
|
||||||
|
initialStartDate: initialStartDate,
|
||||||
|
initialEndDate: initialEndDate,
|
||||||
|
minDate: minDate,
|
||||||
|
maxDate: maxDate,
|
||||||
|
confirmText: confirmText,
|
||||||
|
cancelText: cancelText,
|
||||||
|
primaryColor: primaryColor,
|
||||||
|
onChanged: onChanged,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DateRangePickerDialog extends StatefulWidget {
|
||||||
|
final String title;
|
||||||
|
final DateTime? initialStartDate;
|
||||||
|
final DateTime? initialEndDate;
|
||||||
|
final DateTime? minDate;
|
||||||
|
final DateTime? maxDate;
|
||||||
|
final String confirmText;
|
||||||
|
final String cancelText;
|
||||||
|
final Color primaryColor;
|
||||||
|
final Function(DateTime? startDate, DateTime? endDate)? onChanged;
|
||||||
|
|
||||||
|
const _DateRangePickerDialog({
|
||||||
|
required this.title,
|
||||||
|
this.initialStartDate,
|
||||||
|
this.initialEndDate,
|
||||||
|
this.minDate,
|
||||||
|
this.maxDate,
|
||||||
|
required this.confirmText,
|
||||||
|
required this.cancelText,
|
||||||
|
required this.primaryColor,
|
||||||
|
this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_DateRangePickerDialog> createState() => _DateRangePickerDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DateRangePickerDialogState extends State<_DateRangePickerDialog>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
DateRangePickerSelectionChangedArgs? _selectionChangedArgs;
|
||||||
|
late AnimationController _animationController;
|
||||||
|
late Animation<double> _scaleAnimation;
|
||||||
|
late Animation<double> _fadeAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
_scaleAnimation = Tween<double>(
|
||||||
|
begin: 0.8,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _animationController,
|
||||||
|
curve: Curves.elasticOut,
|
||||||
|
));
|
||||||
|
_fadeAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _animationController,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
));
|
||||||
|
_animationController.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSelectionChanged(DateRangePickerSelectionChangedArgs args) {
|
||||||
|
setState(() {
|
||||||
|
_selectionChangedArgs = args;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: onChanged callback is now called only when confirm button is pressed
|
||||||
|
// This allows users to see real-time selection without triggering callbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getSelectionText() {
|
||||||
|
if (_selectionChangedArgs?.value is PickerDateRange) {
|
||||||
|
final PickerDateRange range = _selectionChangedArgs!.value;
|
||||||
|
if (range.startDate != null && range.endDate != null) {
|
||||||
|
return '${_formatDate(range.startDate!)} - ${_formatDate(range.endDate!)}';
|
||||||
|
} else if (range.startDate != null) {
|
||||||
|
return _formatDate(range.startDate!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'Belum ada tanggal dipilih';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDate(DateTime date) {
|
||||||
|
final months = [
|
||||||
|
'Jan',
|
||||||
|
'Feb',
|
||||||
|
'Mar',
|
||||||
|
'Apr',
|
||||||
|
'Mei',
|
||||||
|
'Jun',
|
||||||
|
'Jul',
|
||||||
|
'Agu',
|
||||||
|
'Sep',
|
||||||
|
'Okt',
|
||||||
|
'Nov',
|
||||||
|
'Des'
|
||||||
|
];
|
||||||
|
return '${date.day} ${months[date.month - 1]} ${date.year}';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _isValidSelection {
|
||||||
|
if (_selectionChangedArgs?.value is PickerDateRange) {
|
||||||
|
final PickerDateRange range = _selectionChangedArgs!.value;
|
||||||
|
return range.startDate != null && range.endDate != null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: _animationController,
|
||||||
|
builder: (context, child) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: _fadeAnimation,
|
||||||
|
child: ScaleTransition(
|
||||||
|
scale: _scaleAnimation,
|
||||||
|
child: Dialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
insetPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: 400,
|
||||||
|
maxHeight: MediaQuery.of(context).size.height * 0.85,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
blurRadius: 20,
|
||||||
|
offset: const Offset(0, 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Header
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
widget.primaryColor,
|
||||||
|
widget.primaryColor.withOpacity(0.8),
|
||||||
|
],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(20),
|
||||||
|
topRight: Radius.circular(20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.calendar_today_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
widget.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Scrollable Content
|
||||||
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Selection Info
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
margin: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: widget.primaryColor.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: widget.primaryColor.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Tanggal Terpilih:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: widget.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
_getSelectionText(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Date Picker
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Container(
|
||||||
|
height: 320,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.grey.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SfDateRangePicker(
|
||||||
|
onSelectionChanged: _onSelectionChanged,
|
||||||
|
selectionMode:
|
||||||
|
DateRangePickerSelectionMode.range,
|
||||||
|
initialSelectedRange:
|
||||||
|
(widget.initialStartDate != null &&
|
||||||
|
widget.initialEndDate != null)
|
||||||
|
? PickerDateRange(
|
||||||
|
widget.initialStartDate,
|
||||||
|
widget.initialEndDate,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
minDate: widget.minDate,
|
||||||
|
maxDate: widget.maxDate,
|
||||||
|
startRangeSelectionColor: widget.primaryColor,
|
||||||
|
endRangeSelectionColor: widget.primaryColor,
|
||||||
|
rangeSelectionColor:
|
||||||
|
widget.primaryColor.withOpacity(0.2),
|
||||||
|
todayHighlightColor: widget.primaryColor,
|
||||||
|
headerStyle: DateRangePickerHeaderStyle(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
monthViewSettings:
|
||||||
|
DateRangePickerMonthViewSettings(
|
||||||
|
viewHeaderStyle:
|
||||||
|
DateRangePickerViewHeaderStyle(
|
||||||
|
backgroundColor:
|
||||||
|
Colors.grey.withOpacity(0.1),
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: widget.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
selectionTextStyle: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
rangeTextStyle: TextStyle(
|
||||||
|
color: widget.primaryColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Action Buttons
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 14),
|
||||||
|
side: BorderSide(color: Colors.grey.shade400),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.cancelText,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: _isValidSelection
|
||||||
|
? () {
|
||||||
|
// Call onChanged when confirm button is pressed
|
||||||
|
if (widget.onChanged != null &&
|
||||||
|
_selectionChangedArgs?.value
|
||||||
|
is PickerDateRange) {
|
||||||
|
final PickerDateRange range =
|
||||||
|
_selectionChangedArgs!.value;
|
||||||
|
widget.onChanged!(
|
||||||
|
range.startDate, range.endDate);
|
||||||
|
}
|
||||||
|
Navigator.of(context)
|
||||||
|
.pop(_selectionChangedArgs);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: widget.primaryColor,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 14),
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
disabledBackgroundColor: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.confirmText,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: _isValidSelection
|
||||||
|
? Colors.white
|
||||||
|
: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,6 @@ class Variables {
|
|||||||
static const String appName = 'POS Kasir Resto App';
|
static const String appName = 'POS Kasir Resto App';
|
||||||
static const String apiVersion = 'v1';
|
static const String apiVersion = 'v1';
|
||||||
// static const String baseUrl = 'http://192.168.1.202:8000';
|
// static const String baseUrl = 'http://192.168.1.202:8000';
|
||||||
static const String baseUrl = 'https://enaklo-pos-be.altru.id';
|
static const String baseUrl = 'https://api-pos.apskel.id';
|
||||||
static const int defaultLimit = 10;
|
static const int defaultLimit = 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
|
class DatabaseHelper {
|
||||||
|
static DatabaseHelper? _instance;
|
||||||
|
static Database? _database;
|
||||||
|
|
||||||
|
DatabaseHelper._internal();
|
||||||
|
|
||||||
|
static DatabaseHelper get instance {
|
||||||
|
_instance ??= DatabaseHelper._internal();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Database> get database async {
|
||||||
|
_database ??= await _initDatabase();
|
||||||
|
return _database!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Database> _initDatabase() async {
|
||||||
|
String path = join(await getDatabasesPath(), 'pos_database.db');
|
||||||
|
|
||||||
|
return await openDatabase(
|
||||||
|
path,
|
||||||
|
version: 3, // Updated version for categories table
|
||||||
|
onCreate: _onCreate,
|
||||||
|
onUpgrade: _onUpgrade,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onCreate(Database db, int version) async {
|
||||||
|
// Products table
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE products (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
organization_id TEXT,
|
||||||
|
category_id TEXT,
|
||||||
|
sku TEXT,
|
||||||
|
name TEXT,
|
||||||
|
description TEXT,
|
||||||
|
price INTEGER,
|
||||||
|
cost INTEGER,
|
||||||
|
business_type TEXT,
|
||||||
|
image_url TEXT,
|
||||||
|
printer_type TEXT,
|
||||||
|
metadata TEXT,
|
||||||
|
is_active INTEGER,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
|
||||||
|
// Product Variants table
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE product_variants (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
product_id TEXT,
|
||||||
|
name TEXT,
|
||||||
|
price_modifier INTEGER,
|
||||||
|
cost INTEGER,
|
||||||
|
metadata TEXT,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT,
|
||||||
|
FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
|
||||||
|
// Categories table - NEW
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE categories (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
organization_id TEXT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
business_type TEXT,
|
||||||
|
metadata TEXT,
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
|
||||||
|
// Printer table
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE printers (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
address TEXT,
|
||||||
|
paper TEXT,
|
||||||
|
type TEXT,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
|
||||||
|
// Create indexes for better performance
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX idx_products_category_id ON products(category_id)');
|
||||||
|
await db.execute('CREATE INDEX idx_products_name ON products(name)');
|
||||||
|
await db.execute('CREATE INDEX idx_products_sku ON products(sku)');
|
||||||
|
await db.execute('CREATE INDEX idx_categories_name ON categories(name)');
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX idx_categories_organization_id ON categories(organization_id)');
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX idx_categories_is_active ON categories(is_active)');
|
||||||
|
await db.execute('CREATE INDEX idx_printers_code ON printers(code)');
|
||||||
|
await db.execute('CREATE INDEX idx_printers_type ON printers(type)');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
||||||
|
if (oldVersion < 2) {
|
||||||
|
// Add printer table in version 2
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE printers (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
address TEXT,
|
||||||
|
paper TEXT,
|
||||||
|
type TEXT,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
|
||||||
|
await db.execute('CREATE INDEX idx_printers_code ON printers(code)');
|
||||||
|
await db.execute('CREATE INDEX idx_printers_type ON printers(type)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
// Add categories table in version 3
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE categories (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
organization_id TEXT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
business_type TEXT,
|
||||||
|
metadata TEXT,
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
created_at TEXT,
|
||||||
|
updated_at TEXT
|
||||||
|
)
|
||||||
|
''');
|
||||||
|
|
||||||
|
await db.execute('CREATE INDEX idx_categories_name ON categories(name)');
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX idx_categories_organization_id ON categories(organization_id)');
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX idx_categories_is_active ON categories(is_active)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close() async {
|
||||||
|
final db = await database;
|
||||||
|
await db.close();
|
||||||
|
_database = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
class DatabaseMigrationHandler {
|
||||||
|
static Future<void> migrate(
|
||||||
|
Database db, int oldVersion, int newVersion) async {
|
||||||
|
if (oldVersion < 2) {
|
||||||
|
// Add indexes for better performance
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_products_name_search ON products(name)');
|
||||||
|
await db.execute(
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_products_sku_search ON products(sku)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
// Add full text search support
|
||||||
|
await db.execute(
|
||||||
|
'CREATE VIRTUAL TABLE products_fts USING fts5(name, sku, description, content=products, content_rowid=rowid)');
|
||||||
|
await db.execute(
|
||||||
|
'INSERT INTO products_fts SELECT name, sku, description FROM products');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 4) {
|
||||||
|
// Add sync tracking
|
||||||
|
await db.execute('ALTER TABLE products ADD COLUMN last_sync_at TEXT');
|
||||||
|
await db.execute(
|
||||||
|
'ALTER TABLE products ADD COLUMN sync_version INTEGER DEFAULT 1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
class DatabaseErrorHandler {
|
||||||
|
static Future<T> executeWithRetry<T>(
|
||||||
|
Future<T> Function() operation, {
|
||||||
|
int maxRetries = 3,
|
||||||
|
Duration delay = const Duration(milliseconds: 500),
|
||||||
|
}) async {
|
||||||
|
int attempts = 0;
|
||||||
|
|
||||||
|
while (attempts < maxRetries) {
|
||||||
|
try {
|
||||||
|
return await operation();
|
||||||
|
} catch (e) {
|
||||||
|
attempts++;
|
||||||
|
|
||||||
|
if (attempts >= maxRetries) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Database operation failed (attempt $attempts/$maxRetries): $e');
|
||||||
|
await Future.delayed(delay * attempts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception('Max retries exceeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isDatabaseCorrupted(dynamic error) {
|
||||||
|
final errorString = error.toString().toLowerCase();
|
||||||
|
return errorString.contains('corrupt') ||
|
||||||
|
errorString.contains('malformed') ||
|
||||||
|
errorString.contains('no such table');
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> handleDatabaseCorruption() async {
|
||||||
|
try {
|
||||||
|
// Delete corrupted database
|
||||||
|
final dbPath = await getDatabasesPath();
|
||||||
|
final file = File('$dbPath/pos_database.db');
|
||||||
|
if (await file.exists()) {
|
||||||
|
await file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Corrupted database deleted, will be recreated');
|
||||||
|
} catch (e) {
|
||||||
|
log('Error handling database corruption: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,13 @@ import 'package:enaklo_pos/core/extensions/string_ext.dart';
|
|||||||
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
||||||
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/type/bussines_type.dart';
|
import 'package:enaklo_pos/data/type/bussines_type.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:barcode/barcode.dart';
|
import 'package:barcode/barcode.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
@@ -22,15 +23,63 @@ Future<void> onPrint(
|
|||||||
required Order order,
|
required Order order,
|
||||||
}) async {
|
}) async {
|
||||||
final outlet = await OutletLocalDatasource().get();
|
final outlet = await OutletLocalDatasource().get();
|
||||||
|
final settings = await SettingsLocalDatasource().getTax();
|
||||||
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
|
||||||
if (outlet.businessType == BusinessType.restaurant) {
|
if (outlet.businessType == BusinessType.restaurant) {
|
||||||
final checkerPrinter =
|
final checkerPrinter =
|
||||||
await ProductLocalDatasource.instance.getPrinterByCode('checker');
|
await PrinterLocalDatasource.instance.getPrinterByCode('checker');
|
||||||
final kitchenPrinter =
|
final kitchenPrinter =
|
||||||
await ProductLocalDatasource.instance.getPrinterByCode('kitchen');
|
await PrinterLocalDatasource.instance.getPrinterByCode('kitchen');
|
||||||
|
final receiptPrinter =
|
||||||
|
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
|
||||||
final barPrinter =
|
final barPrinter =
|
||||||
await ProductLocalDatasource.instance.getPrinterByCode('bar');
|
await PrinterLocalDatasource.instance.getPrinterByCode('bar');
|
||||||
|
|
||||||
final authData = await AuthLocalDataSource().getAuthData();
|
if (receiptPrinter != null) {
|
||||||
|
try {
|
||||||
|
final printValue = await PrintDataoutputs.instance.printOrderV4(
|
||||||
|
order,
|
||||||
|
authData.user?.name ?? "",
|
||||||
|
'',
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
settings.value,
|
||||||
|
receiptPrinter.paper.toIntegerFromText,
|
||||||
|
order.orderType ?? "",
|
||||||
|
outlet,
|
||||||
|
productQuantity);
|
||||||
|
await PrinterService()
|
||||||
|
.printWithPrinter(receiptPrinter, printValue, context);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Print receipt failed',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
'Printer: ${receiptPrinter.name}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
log("Error printing receipt order: $e");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error printing receipt order: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Receipt printer not found',
|
||||||
|
null,
|
||||||
|
reason:
|
||||||
|
'Receipt printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer receipt')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Checker printer
|
// Checker printer
|
||||||
if (checkerPrinter != null) {
|
if (checkerPrinter != null) {
|
||||||
@@ -40,69 +89,151 @@ Future<void> onPrint(
|
|||||||
order.tableNumber ?? "",
|
order.tableNumber ?? "",
|
||||||
order.orderNumber ?? "",
|
order.orderNumber ?? "",
|
||||||
authData.user?.name ?? "",
|
authData.user?.name ?? "",
|
||||||
|
order.metadata?['customer_name'] ?? "",
|
||||||
checkerPrinter.paper.toIntegerFromText,
|
checkerPrinter.paper.toIntegerFromText,
|
||||||
order.orderType ?? "",
|
order.orderType ?? "",
|
||||||
);
|
);
|
||||||
|
|
||||||
await PrinterService()
|
if (productQuantity.isNotEmpty) {
|
||||||
// ignore: use_build_context_synchronously
|
await PrinterService()
|
||||||
.printWithPrinter(checkerPrinter, printValue, context);
|
// ignore: use_build_context_synchronously
|
||||||
} catch (e) {
|
.printWithPrinter(checkerPrinter, printValue, context);
|
||||||
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
log("Error printing checker: $e");
|
log("Error printing checker: $e");
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error printing checker ${checkerPrinter.name}',
|
||||||
|
information: [
|
||||||
|
'Printer: checker',
|
||||||
|
'data: ${checkerPrinter.toMap()}',
|
||||||
|
],
|
||||||
|
);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Error printing checker: $e')),
|
SnackBar(content: Text('Error printing checker: $e')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Checker printer not found',
|
||||||
|
null,
|
||||||
|
reason:
|
||||||
|
'Checker printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer checker')),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kitchen printer
|
// Kitchen printer
|
||||||
if (kitchenPrinter != null) {
|
if (kitchenPrinter != null) {
|
||||||
try {
|
try {
|
||||||
final printValue = await PrintDataoutputs.instance.printKitchen(
|
final productByPrinter = productQuantity
|
||||||
productQuantity,
|
.where((item) => item.product.printerType == 'kitchen')
|
||||||
order.tableNumber!,
|
.toList();
|
||||||
|
final printValue =
|
||||||
|
await PrintDataoutputs.instance.printKitchenPerProduct(
|
||||||
|
productByPrinter,
|
||||||
|
order.tableNumber ?? "",
|
||||||
order.orderNumber ?? "",
|
order.orderNumber ?? "",
|
||||||
authData.user?.name ?? "",
|
authData.user?.name ?? "",
|
||||||
|
order.metadata?['customer_name'] ?? "",
|
||||||
kitchenPrinter.paper.toIntegerFromText,
|
kitchenPrinter.paper.toIntegerFromText,
|
||||||
order.orderType ?? "",
|
order.orderType ?? "",
|
||||||
);
|
);
|
||||||
|
|
||||||
await PrinterService()
|
if (productByPrinter.isNotEmpty) {
|
||||||
.printWithPrinter(kitchenPrinter, printValue, context);
|
await PrinterService()
|
||||||
} catch (e) {
|
.printWithPrinter(kitchenPrinter, printValue, context);
|
||||||
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
log("Error printing kitchen order: $e");
|
log("Error printing kitchen order: $e");
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error printing kitchen ${kitchenPrinter.name}',
|
||||||
|
information: [
|
||||||
|
'Printer: kitchen',
|
||||||
|
'data: ${kitchenPrinter.toMap()}',
|
||||||
|
],
|
||||||
|
);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Error printing kitchen order: $e')),
|
SnackBar(content: Text('Error printing kitchen order: $e')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Kitchen printer not found',
|
||||||
|
null,
|
||||||
|
reason:
|
||||||
|
'Kitchen printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer kitchen')),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bar printer
|
// Bar printer
|
||||||
if (barPrinter != null) {
|
if (barPrinter != null) {
|
||||||
try {
|
try {
|
||||||
|
final productByPrinter = productQuantity
|
||||||
|
.where((item) => item.product.printerType == 'bar')
|
||||||
|
.toList();
|
||||||
|
|
||||||
final printValue = await PrintDataoutputs.instance.printBar(
|
final printValue = await PrintDataoutputs.instance.printBar(
|
||||||
productQuantity,
|
productByPrinter,
|
||||||
order.tableNumber ?? "",
|
order.tableNumber ?? "",
|
||||||
order.orderNumber ?? "",
|
order.orderNumber ?? "",
|
||||||
authData.user?.name ?? "",
|
authData.user?.name ?? "",
|
||||||
|
order.metadata?['customer_name'] ?? "",
|
||||||
barPrinter.paper.toIntegerFromText,
|
barPrinter.paper.toIntegerFromText,
|
||||||
order.orderType ?? "",
|
order.orderType ?? "",
|
||||||
);
|
);
|
||||||
|
|
||||||
await PrinterService()
|
if (productByPrinter.isNotEmpty) {
|
||||||
.printWithPrinter(barPrinter, printValue, context);
|
await PrinterService()
|
||||||
} catch (e) {
|
.printWithPrinter(barPrinter, printValue, context);
|
||||||
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
log("Error printing bar order: $e");
|
log("Error printing bar order: $e");
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error printing bar ${barPrinter.name}',
|
||||||
|
information: [
|
||||||
|
'Printer: bar',
|
||||||
|
'data: ${barPrinter.toMap()}',
|
||||||
|
],
|
||||||
|
);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Error printing bar order: $e')),
|
SnackBar(content: Text('Error printing bar order: $e')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Bar printer not found',
|
||||||
|
null,
|
||||||
|
reason: 'Bar printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer bar')),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outlet.businessType == BusinessType.ticketing) {
|
if (outlet.businessType == BusinessType.ticketing) {
|
||||||
final ticketPrinter =
|
final ticketPrinter =
|
||||||
await ProductLocalDatasource.instance.getPrinterByCode('ticket');
|
await PrinterLocalDatasource.instance.getPrinterByCode('ticket');
|
||||||
|
|
||||||
final barcode = await generateBarcodeAsUint8List(order.orderNumber ?? "");
|
final barcode = await generateBarcodeAsUint8List(order.orderNumber ?? "");
|
||||||
|
|
||||||
@@ -117,12 +248,142 @@ Future<void> onPrint(
|
|||||||
await PrinterService()
|
await PrinterService()
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
.printWithPrinter(ticketPrinter, printValue, context);
|
.printWithPrinter(ticketPrinter, printValue, context);
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error printing ticket ${ticketPrinter.name}',
|
||||||
|
information: [
|
||||||
|
'Printer: ticket',
|
||||||
|
'data: ${ticketPrinter.toMap()}',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Error printing ticket: $e");
|
log("Error printing ticket: $e");
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Error printing ticket: $e')),
|
SnackBar(content: Text('Error printing ticket: $e')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Ticket printer not found',
|
||||||
|
null,
|
||||||
|
reason:
|
||||||
|
'Ticket printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer ticket')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onPrintBill(
|
||||||
|
BuildContext context, {
|
||||||
|
required List<ProductQuantity> productQuantity,
|
||||||
|
required Order order,
|
||||||
|
}) async {
|
||||||
|
final outlet = await OutletLocalDatasource().get();
|
||||||
|
final settings = await SettingsLocalDatasource().getTax();
|
||||||
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
|
||||||
|
if (outlet.businessType == BusinessType.restaurant) {
|
||||||
|
final receiptPrinter =
|
||||||
|
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
|
||||||
|
|
||||||
|
if (receiptPrinter != null) {
|
||||||
|
try {
|
||||||
|
final printValue = await PrintDataoutputs.instance.printOrderV4(
|
||||||
|
order,
|
||||||
|
authData.user?.name ?? "",
|
||||||
|
'',
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
settings.value,
|
||||||
|
receiptPrinter.paper.toIntegerFromText,
|
||||||
|
order.orderType ?? "",
|
||||||
|
outlet,
|
||||||
|
productQuantity);
|
||||||
|
await PrinterService()
|
||||||
|
.printWithPrinter(receiptPrinter, printValue, context);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Print receipt failed',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
'Printer: ${receiptPrinter.name}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
log("Error printing receipt order: $e");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error printing receipt order: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Receipt printer not found',
|
||||||
|
null,
|
||||||
|
reason:
|
||||||
|
'Receipt printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer receipt')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outlet.businessType == BusinessType.ticketing) {
|
||||||
|
final ticketPrinter =
|
||||||
|
await PrinterLocalDatasource.instance.getPrinterByCode('ticket');
|
||||||
|
|
||||||
|
final barcode = await generateBarcodeAsUint8List(order.orderNumber ?? "");
|
||||||
|
|
||||||
|
if (ticketPrinter != null) {
|
||||||
|
try {
|
||||||
|
final printValue = await PrintDataoutputs.instance.printTicket(
|
||||||
|
order.totalAmount ?? 0,
|
||||||
|
barcode,
|
||||||
|
ticketPrinter.paper.toIntegerFromText,
|
||||||
|
);
|
||||||
|
|
||||||
|
await PrinterService()
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
.printWithPrinter(ticketPrinter, printValue, context);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error printing ticket ${ticketPrinter.name}',
|
||||||
|
information: [
|
||||||
|
'Printer: ticket',
|
||||||
|
'data: ${ticketPrinter.toMap()}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
log("Error printing ticket: $e");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error printing ticket: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Ticket printer not found',
|
||||||
|
null,
|
||||||
|
reason:
|
||||||
|
'Ticket printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer ticket')),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,9 +394,10 @@ Future<void> onPrintRecipt(
|
|||||||
required String paymentMethod,
|
required String paymentMethod,
|
||||||
required int nominalBayar,
|
required int nominalBayar,
|
||||||
required int kembalian,
|
required int kembalian,
|
||||||
|
required List<ProductQuantity> productQuantity,
|
||||||
}) async {
|
}) async {
|
||||||
final receiptPrinter =
|
final receiptPrinter =
|
||||||
await ProductLocalDatasource.instance.getPrinterByCode('receipt');
|
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
|
||||||
final authData = await AuthLocalDataSource().getAuthData();
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
final settings = await SettingsLocalDatasource().getTax();
|
final settings = await SettingsLocalDatasource().getTax();
|
||||||
final outlet = await OutletLocalDatasource().get();
|
final outlet = await OutletLocalDatasource().get();
|
||||||
@@ -143,16 +405,71 @@ Future<void> onPrintRecipt(
|
|||||||
if (receiptPrinter != null) {
|
if (receiptPrinter != null) {
|
||||||
try {
|
try {
|
||||||
final printValue = await PrintDataoutputs.instance.printOrderV4(
|
final printValue = await PrintDataoutputs.instance.printOrderV4(
|
||||||
order,
|
order,
|
||||||
authData.user?.name ?? "",
|
authData.user?.name ?? "",
|
||||||
paymentMethod,
|
paymentMethod,
|
||||||
nominalBayar,
|
nominalBayar,
|
||||||
kembalian,
|
kembalian,
|
||||||
settings.value,
|
settings.value,
|
||||||
receiptPrinter.paper.toIntegerFromText,
|
receiptPrinter.paper.toIntegerFromText,
|
||||||
order.orderType ?? "",
|
order.orderType ?? "",
|
||||||
outlet,
|
outlet,
|
||||||
|
productQuantity);
|
||||||
|
await PrinterService()
|
||||||
|
.printWithPrinter(receiptPrinter, printValue, context);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Print receipt failed',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
'Payment Method: $paymentMethod',
|
||||||
|
'Printer: ${receiptPrinter.name}',
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
log("Error printing receipt order: $e");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error printing receipt order: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Kitchen printer not found',
|
||||||
|
null,
|
||||||
|
reason: 'Kitchen printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer kitchen')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onPrinVoidRecipt(
|
||||||
|
context, {
|
||||||
|
required Order order,
|
||||||
|
required List<OrderItem> productItemVoid,
|
||||||
|
required int totalVoid,
|
||||||
|
}) async {
|
||||||
|
final receiptPrinter =
|
||||||
|
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
|
||||||
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
final settings = await SettingsLocalDatasource().getTax();
|
||||||
|
final outlet = await OutletLocalDatasource().get();
|
||||||
|
|
||||||
|
if (receiptPrinter != null) {
|
||||||
|
try {
|
||||||
|
final printValue = await PrintDataoutputs.instance.printVoidOrder(
|
||||||
|
order,
|
||||||
|
authData.user?.name ?? "",
|
||||||
|
settings.value,
|
||||||
|
receiptPrinter.paper.toIntegerFromText,
|
||||||
|
totalVoid,
|
||||||
|
outlet,
|
||||||
|
productItemVoid);
|
||||||
await PrinterService()
|
await PrinterService()
|
||||||
.printWithPrinter(receiptPrinter, printValue, context);
|
.printWithPrinter(receiptPrinter, printValue, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -164,6 +481,56 @@ Future<void> onPrintRecipt(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> onPrintSplit(
|
||||||
|
context, {
|
||||||
|
required Order order,
|
||||||
|
}) async {
|
||||||
|
final receiptPrinter =
|
||||||
|
await PrinterLocalDatasource.instance.getPrinterByCode('receipt');
|
||||||
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
final settings = await SettingsLocalDatasource().getTax();
|
||||||
|
final outlet = await OutletLocalDatasource().get();
|
||||||
|
|
||||||
|
if (receiptPrinter != null) {
|
||||||
|
try {
|
||||||
|
final printValue = await PrintDataoutputs.instance.printSplitBill(
|
||||||
|
order,
|
||||||
|
authData.user?.name ?? "",
|
||||||
|
receiptPrinter.paper.toIntegerFromText,
|
||||||
|
outlet,
|
||||||
|
);
|
||||||
|
await PrinterService()
|
||||||
|
.printWithPrinter(receiptPrinter, printValue, context);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Print receipt failed',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
'Printer: ${receiptPrinter.name}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
log("Error printing receipt order: $e");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error printing receipt order: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Kitchen printer not found',
|
||||||
|
null,
|
||||||
|
reason: 'Kitchen printer not found / Printer not setting in printer page',
|
||||||
|
information: [
|
||||||
|
'Order ID: ${order.id}',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Anda belum menghubungkan printer kitchen')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Uint8List> generateBarcodeAsUint8List(String data) async {
|
Future<Uint8List> generateBarcodeAsUint8List(String data) async {
|
||||||
// 1. Buat barcode instance (code128, qrCode, dll)
|
// 1. Buat barcode instance (code128, qrCode, dll)
|
||||||
final barcode = Barcode.code128();
|
final barcode = Barcode.code128();
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
|
import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:enaklo_pos/core/constants/variables.dart';
|
||||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/auth_response_model.dart';
|
||||||
import 'package:enaklo_pos/presentation/auth/login_page.dart';
|
import 'package:enaklo_pos/presentation/auth/login_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class DioClient {
|
class DioClient {
|
||||||
static final Dio _dio = Dio(BaseOptions(
|
static final Dio _dio = Dio(BaseOptions(
|
||||||
connectTimeout: const Duration(seconds: 10),
|
connectTimeout: const Duration(minutes: 1),
|
||||||
receiveTimeout: const Duration(seconds: 10),
|
receiveTimeout: const Duration(minutes: 1),
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
},
|
},
|
||||||
@@ -124,13 +127,17 @@ class AuthInterceptorWithRefresh extends Interceptor {
|
|||||||
Future<bool> _tryRefreshToken() async {
|
Future<bool> _tryRefreshToken() async {
|
||||||
try {
|
try {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final refreshToken = prefs.getString('refresh_token');
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
|
||||||
if (refreshToken == null) return false;
|
final url = '${Variables.baseUrl}/api/v1/auth/refresh';
|
||||||
|
|
||||||
final response = await Dio().post(
|
final response = await Dio().post(
|
||||||
'YOUR_REFRESH_TOKEN_ENDPOINT',
|
url,
|
||||||
data: {'refresh_token': refreshToken},
|
options: Options(
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ${authData.refreshToken}',
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
@@ -140,6 +147,11 @@ class AuthInterceptorWithRefresh extends Interceptor {
|
|||||||
await prefs.setString('auth_token', newToken);
|
await prefs.setString('auth_token', newToken);
|
||||||
await prefs.setString('refresh_token', newRefreshToken);
|
await prefs.setString('refresh_token', newRefreshToken);
|
||||||
|
|
||||||
|
AuthResponseModel authResponseModel =
|
||||||
|
AuthResponseModel.fromMap(response.data['data']);
|
||||||
|
|
||||||
|
AuthLocalDataSource().saveAuthData(authResponseModel);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
class DatabasePerformanceMonitor {
|
||||||
|
static final Map<String, List<int>> _queryTimes = {};
|
||||||
|
|
||||||
|
static Future<T> monitorQuery<T>(
|
||||||
|
String queryName,
|
||||||
|
Future<T> Function() query,
|
||||||
|
) async {
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = await query();
|
||||||
|
stopwatch.stop();
|
||||||
|
|
||||||
|
_recordQueryTime(queryName, stopwatch.elapsedMilliseconds);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
stopwatch.stop();
|
||||||
|
log('Query "$queryName" failed after ${stopwatch.elapsedMilliseconds}ms: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _recordQueryTime(String queryName, int milliseconds) {
|
||||||
|
if (!_queryTimes.containsKey(queryName)) {
|
||||||
|
_queryTimes[queryName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_queryTimes[queryName]!.add(milliseconds);
|
||||||
|
|
||||||
|
// Keep only last 100 entries
|
||||||
|
if (_queryTimes[queryName]!.length > 100) {
|
||||||
|
_queryTimes[queryName]!.removeAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log slow queries
|
||||||
|
if (milliseconds > 1000) {
|
||||||
|
log('Slow query detected: "$queryName" took ${milliseconds}ms');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic> getPerformanceStats() {
|
||||||
|
final stats = <String, dynamic>{};
|
||||||
|
|
||||||
|
_queryTimes.forEach((queryName, times) {
|
||||||
|
if (times.isNotEmpty) {
|
||||||
|
final avgTime = times.reduce((a, b) => a + b) / times.length;
|
||||||
|
final maxTime = times.reduce((a, b) => a > b ? a : b);
|
||||||
|
final minTime = times.reduce((a, b) => a < b ? a : b);
|
||||||
|
|
||||||
|
stats[queryName] = {
|
||||||
|
'average_ms': avgTime.round(),
|
||||||
|
'max_ms': maxTime,
|
||||||
|
'min_ms': minTime,
|
||||||
|
'total_queries': times.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,568 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/inventory_analytic_response_model.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:pdf/pdf.dart';
|
||||||
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
|
|
||||||
|
class InventoryReport {
|
||||||
|
static final primaryColor = PdfColor.fromHex("36175e");
|
||||||
|
|
||||||
|
static Future<File> previewPdf({
|
||||||
|
required String searchDateFormatted,
|
||||||
|
required InventoryAnalyticData? inventory,
|
||||||
|
}) async {
|
||||||
|
final pdf = pw.Document();
|
||||||
|
final ByteData dataImage = await rootBundle.load('assets/logo/logo.png');
|
||||||
|
final Uint8List bytes = dataImage.buffer.asUint8List();
|
||||||
|
final outlet = await OutletLocalDatasource().get();
|
||||||
|
|
||||||
|
final image = pw.MemoryImage(bytes);
|
||||||
|
pdf.addPage(
|
||||||
|
pw.MultiPage(
|
||||||
|
pageFormat: PdfPageFormat.a4,
|
||||||
|
margin: pw.EdgeInsets.zero,
|
||||||
|
build: (pw.Context context) {
|
||||||
|
return [
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Row(
|
||||||
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Bagian kiri - Logo dan Info Perusahaan
|
||||||
|
pw.Row(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Icon/Logo placeholder (bisa diganti dengan gambar logo)
|
||||||
|
pw.Container(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
child: pw.Image(image),
|
||||||
|
),
|
||||||
|
pw.SizedBox(width: 15),
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
pw.Text(
|
||||||
|
'Apskel',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 4),
|
||||||
|
pw.Text(
|
||||||
|
outlet.name ?? "",
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: PdfColors.grey700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 2),
|
||||||
|
pw.Text(
|
||||||
|
outlet.address ?? "",
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: PdfColors.grey600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// Bagian kanan - Info Laporan
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
pw.Text(
|
||||||
|
'Laporan Transaksi',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: PdfColors.grey800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 8),
|
||||||
|
pw.Text(
|
||||||
|
searchDateFormatted,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: PdfColors.grey600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 4),
|
||||||
|
pw.Text(
|
||||||
|
'Laporan',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: PdfColors.grey500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 3,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('1. Ringkasan'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Row(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
pw.Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Item',
|
||||||
|
(inventory?.summary.totalProducts ?? 0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Item Masuk',
|
||||||
|
(inventory?.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalIn)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Item Keluar',
|
||||||
|
(inventory?.products.fold<num>(0,
|
||||||
|
(sum, item) => sum + (item.totalOut)))
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(width: 20),
|
||||||
|
pw.Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Ingredient',
|
||||||
|
(inventory?.summary.totalIngredients ?? 0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Ingredient Masuk',
|
||||||
|
(inventory?.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalIn)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Ingredient Keluar',
|
||||||
|
(inventory?.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalOut)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary Item
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
children: [
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('2. Item'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
topLeft: pw.Radius.circular(8),
|
||||||
|
topRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(2), // Kategori
|
||||||
|
2: pw.FlexColumnWidth(1), // Stock
|
||||||
|
3: pw.FlexColumnWidth(2), // Masuk
|
||||||
|
4: pw.FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Nama'),
|
||||||
|
_buildHeaderCell('Kategori'),
|
||||||
|
_buildHeaderCell('Stock'),
|
||||||
|
_buildHeaderCell('Masuk'),
|
||||||
|
_buildHeaderCell('Keluar'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: PdfColors.white,
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(2), // Kategori
|
||||||
|
2: pw.FlexColumnWidth(1), // Stock
|
||||||
|
3: pw.FlexColumnWidth(2), // Masuk
|
||||||
|
4: pw.FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: inventory?.products
|
||||||
|
.map((item) => _buildProductDataRow(
|
||||||
|
item,
|
||||||
|
inventory.products.indexOf(item) %
|
||||||
|
2 ==
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
bottomLeft: pw.Radius.circular(8),
|
||||||
|
bottomRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(2), // Kategori
|
||||||
|
2: pw.FlexColumnWidth(1), // Stock
|
||||||
|
3: pw.FlexColumnWidth(2), // Masuk
|
||||||
|
4: pw.FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
_buildTotalCell(
|
||||||
|
(inventory?.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.quantity)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(inventory?.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalIn)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(inventory?.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalOut)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary Ingredient
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
children: [
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('3. Ingredient'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
topLeft: pw.Radius.circular(8),
|
||||||
|
topRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Name
|
||||||
|
1: pw.FlexColumnWidth(1), // Stock
|
||||||
|
2: pw.FlexColumnWidth(2), // Masuk
|
||||||
|
3: pw.FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Nama'),
|
||||||
|
_buildHeaderCell('Stock'),
|
||||||
|
_buildHeaderCell('Masuk'),
|
||||||
|
_buildHeaderCell('Keluar'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: PdfColors.white,
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Name
|
||||||
|
1: pw.FlexColumnWidth(1), // Stock
|
||||||
|
2: pw.FlexColumnWidth(2), // Masuk
|
||||||
|
3: pw.FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: inventory?.ingredients
|
||||||
|
.map((item) => _buildIngredientsDataRow(
|
||||||
|
item,
|
||||||
|
inventory.ingredients.indexOf(item) %
|
||||||
|
2 ==
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
bottomLeft: pw.Radius.circular(8),
|
||||||
|
bottomRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Name
|
||||||
|
1: pw.FlexColumnWidth(1), // Stock
|
||||||
|
2: pw.FlexColumnWidth(2), // Masuk
|
||||||
|
3: pw.FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(
|
||||||
|
(inventory?.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.quantity)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(inventory?.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalIn)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(inventory?.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + (item.totalOut)) ??
|
||||||
|
0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return HelperPdfService.saveDocument(
|
||||||
|
name:
|
||||||
|
'Apskel POS | Inventory Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
|
||||||
|
pdf: pdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildSectionWidget(String title) {
|
||||||
|
return pw.Text(
|
||||||
|
title,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildSummaryItem(
|
||||||
|
String label,
|
||||||
|
String value, {
|
||||||
|
pw.TextStyle? valueStyle,
|
||||||
|
pw.TextStyle? labelStyle,
|
||||||
|
}) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.only(bottom: 8),
|
||||||
|
margin: pw.EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
border: pw.Border(
|
||||||
|
bottom: pw.BorderSide(
|
||||||
|
color: PdfColors.grey300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Row(
|
||||||
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
pw.Text(label, style: labelStyle),
|
||||||
|
pw.Text(
|
||||||
|
value,
|
||||||
|
style: valueStyle ??
|
||||||
|
pw.TextStyle(
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildHeaderCell(String text) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
color: PdfColors.white,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: pw.TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildDataCell(String text,
|
||||||
|
{pw.Alignment alignment = pw.Alignment.center, PdfColor? textColor}) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
alignment: alignment,
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: textColor ?? PdfColors.black,
|
||||||
|
fontWeight: pw.FontWeight.normal,
|
||||||
|
),
|
||||||
|
textAlign: alignment == pw.Alignment.centerLeft
|
||||||
|
? pw.TextAlign.left
|
||||||
|
: pw.TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildTotalCell(String text) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
color: PdfColors.white,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: pw.TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.TableRow _buildProductDataRow(
|
||||||
|
InventoryProductItem product, bool isEven) {
|
||||||
|
return pw.TableRow(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: product.isZeroStock
|
||||||
|
? PdfColors.red100
|
||||||
|
: product.isLowStock
|
||||||
|
? PdfColors.yellow100
|
||||||
|
: isEven
|
||||||
|
? PdfColors.grey50
|
||||||
|
: PdfColors.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(product.productName, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(product.categoryName,
|
||||||
|
alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(product.quantity.toString()),
|
||||||
|
_buildDataCell(product.totalIn.toString()),
|
||||||
|
_buildDataCell(product.totalOut.toString()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.TableRow _buildIngredientsDataRow(
|
||||||
|
InventoryIngredientItem item, bool isEven) {
|
||||||
|
return pw.TableRow(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: item.isZeroStock
|
||||||
|
? PdfColors.red100
|
||||||
|
: item.isLowStock
|
||||||
|
? PdfColors.yellow100
|
||||||
|
: isEven
|
||||||
|
? PdfColors.grey50
|
||||||
|
: PdfColors.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(item.ingredientName, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(item.quantity.toString()),
|
||||||
|
_buildDataCell(item.totalIn.toString()),
|
||||||
|
_buildDataCell(item.totalOut.toString()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_esc_pos_network/flutter_esc_pos_network.dart';
|
import 'package:flutter_esc_pos_network/flutter_esc_pos_network.dart';
|
||||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/print_model.dart';
|
import 'package:enaklo_pos/data/models/response/print_model.dart';
|
||||||
|
import 'package:synchronized/synchronized.dart';
|
||||||
|
|
||||||
class PrinterService {
|
class PrinterService {
|
||||||
static final PrinterService _instance = PrinterService._internal();
|
static final PrinterService _instance = PrinterService._internal();
|
||||||
factory PrinterService() => _instance;
|
factory PrinterService() => _instance;
|
||||||
PrinterService._internal();
|
PrinterService._internal();
|
||||||
|
|
||||||
|
final Map<String, Lock> _locks = {};
|
||||||
|
String? _connectedMac;
|
||||||
|
|
||||||
/// Connect to Bluetooth printer
|
/// Connect to Bluetooth printer
|
||||||
Future<bool> connectBluetoothPrinter(String macAddress) async {
|
Future<bool> connectBluetoothPrinter(String macAddress) async {
|
||||||
try {
|
try {
|
||||||
@@ -20,22 +25,106 @@ class PrinterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect to the printer
|
// Connect to the printer
|
||||||
bool connected = await PrintBluetoothThermal.connect(
|
bool connected =
|
||||||
macPrinterAddress: macAddress);
|
await PrintBluetoothThermal.connect(macPrinterAddress: macAddress);
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
log("Successfully connected to Bluetooth printer: $macAddress");
|
log("Successfully connected to Bluetooth printer: $macAddress");
|
||||||
} else {
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Failed to connect to Bluetooth printer',
|
||||||
|
null,
|
||||||
|
reason: 'Failed to connect to Bluetooth printe',
|
||||||
|
information: [
|
||||||
|
'function: connectBluetoothPrinter(String macAddress)',
|
||||||
|
'macAddress: $macAddress',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Failed to connect to Bluetooth printer: $macAddress");
|
log("Failed to connect to Bluetooth printer: $macAddress");
|
||||||
}
|
}
|
||||||
|
|
||||||
return connected;
|
return connected;
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error connecting to Bluetooth printer',
|
||||||
|
information: [
|
||||||
|
'function: connectBluetoothPrinter(String macAddress)',
|
||||||
|
'Printer: Bluetooth printer',
|
||||||
|
'macAddress: $macAddress',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Error connecting to Bluetooth printer: $e");
|
log("Error connecting to Bluetooth printer: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Lock _getLock(String address) {
|
||||||
|
return _locks.putIfAbsent(address, () => Lock());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> printWithPrinter(
|
||||||
|
PrintModel printer, List<int> printData, BuildContext context) async {
|
||||||
|
if (printer.type == 'Bluetooth') {
|
||||||
|
return await _getLock(printer.address).synchronized(() async {
|
||||||
|
try {
|
||||||
|
bool isConnected = await PrintBluetoothThermal.connectionStatus;
|
||||||
|
if (!isConnected || _connectedMac != printer.address) {
|
||||||
|
if (isConnected) {
|
||||||
|
await PrintBluetoothThermal.disconnect;
|
||||||
|
await Future.delayed(const Duration(milliseconds: 1500));
|
||||||
|
}
|
||||||
|
bool connected = await PrintBluetoothThermal.connect(
|
||||||
|
macPrinterAddress: printer.address);
|
||||||
|
if (!connected) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Gagal connect ke ${printer.name}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_connectedMac = printer.address;
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(milliseconds: 1000)); // naikkan dari 500ms
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = await _writeBytesChunked(printData);
|
||||||
|
|
||||||
|
// Beri waktu printer flush sebelum job berikutnya
|
||||||
|
await Future.delayed(const Duration(milliseconds: 1000));
|
||||||
|
|
||||||
|
log("Print result ${printer.name}: $result");
|
||||||
|
return result;
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
_connectedMac = null;
|
||||||
|
FirebaseCrashlytics.instance.recordError(e, stackTrace,
|
||||||
|
reason: 'Error printing ${printer.name}',
|
||||||
|
information: [
|
||||||
|
'Printer: ${printer.name}',
|
||||||
|
'MAC: ${printer.address}'
|
||||||
|
]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return await printNetwork(printer, printData, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kirim data per chunk untuk hindari buffer overflow Bluetooth
|
||||||
|
Future<bool> _writeBytesChunked(List<int> data, {int chunkSize = 512}) async {
|
||||||
|
for (int i = 0; i < data.length; i += chunkSize) {
|
||||||
|
final end = (i + chunkSize > data.length) ? data.length : i + chunkSize;
|
||||||
|
final chunk = data.sublist(i, end);
|
||||||
|
bool result = await PrintBluetoothThermal.writeBytes(chunk);
|
||||||
|
if (!result) return false;
|
||||||
|
await Future.delayed(const Duration(milliseconds: 30));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Print using Bluetooth printer
|
/// Print using Bluetooth printer
|
||||||
Future<bool> printBluetooth(List<int> printData) async {
|
Future<bool> printBluetooth(List<int> printData) async {
|
||||||
try {
|
try {
|
||||||
@@ -49,76 +138,96 @@ class PrinterService {
|
|||||||
if (printResult) {
|
if (printResult) {
|
||||||
log("Successfully printed via Bluetooth");
|
log("Successfully printed via Bluetooth");
|
||||||
} else {
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Failed to print via Bluetooth',
|
||||||
|
null,
|
||||||
|
reason: 'Failed to print via Bluetooth',
|
||||||
|
information: [
|
||||||
|
'function: printBluetooth(List<int> printData)',
|
||||||
|
'printData: $printData',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Failed to print via Bluetooth");
|
log("Failed to print via Bluetooth");
|
||||||
}
|
}
|
||||||
|
|
||||||
return printResult;
|
return printResult;
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
e,
|
||||||
|
stackTrace,
|
||||||
|
reason: 'Error printing via Bluetooth',
|
||||||
|
information: [
|
||||||
|
'function: printBluetooth(List<int> printData)',
|
||||||
|
'Printer: Bluetooth printer',
|
||||||
|
'printData: $printData',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Error printing via Bluetooth: $e");
|
log("Error printing via Bluetooth: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print using Network printer
|
/// Print using Network printer
|
||||||
Future<bool> printNetwork(String ipAddress, List<int> printData) async {
|
Future<bool> printNetwork(
|
||||||
|
PrintModel printer, List<int> printData, BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
final printer = PrinterNetworkManager(ipAddress);
|
final networkPrinter = PrinterNetworkManager(printer.address);
|
||||||
PosPrintResult connect = await printer.connect();
|
PosPrintResult connect = await networkPrinter.connect();
|
||||||
|
|
||||||
if (connect == PosPrintResult.success) {
|
if (connect == PosPrintResult.success) {
|
||||||
PosPrintResult printing = await printer.printTicket(printData);
|
PosPrintResult printing = await networkPrinter.printTicket(printData);
|
||||||
printer.disconnect();
|
networkPrinter.disconnect();
|
||||||
|
|
||||||
if (printing == PosPrintResult.success) {
|
if (printing == PosPrintResult.success) {
|
||||||
log("Successfully printed via Network printer: $ipAddress");
|
log("Successfully printed via Network printer: ${printer.address}");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Failed to print via Network printer: ${printing.msg}',
|
||||||
|
null,
|
||||||
|
reason: 'Failed to print via Network printer',
|
||||||
|
information: [
|
||||||
|
'Printer: ${printer.name}',
|
||||||
|
'ipAddress: ${printer.address}',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Failed to print via Network printer: ${printing.msg}");
|
log("Failed to print via Network printer: ${printing.msg}");
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Gagal print ke ${printer.name}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
'Failed to connect to Network printer: ${connect.msg}',
|
||||||
|
null,
|
||||||
|
reason: 'Failed to connect Network printer',
|
||||||
|
information: [
|
||||||
|
'Printer: ${printer.name}',
|
||||||
|
'ipAddress: ${printer.address}',
|
||||||
|
],
|
||||||
|
);
|
||||||
log("Failed to connect to Network printer: ${connect.msg}");
|
log("Failed to connect to Network printer: ${connect.msg}");
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Gagal connect ke ${printer.name}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
log("Error printing via Network: $e");
|
FirebaseCrashlytics.instance.recordError(
|
||||||
return false;
|
e,
|
||||||
}
|
stackTrace,
|
||||||
}
|
reason: 'Error printing via Network',
|
||||||
|
information: [
|
||||||
/// Print with automatic printer type detection
|
'Printer: ${printer.name}',
|
||||||
Future<bool> printWithPrinter(PrintModel printer, List<int> printData, BuildContext context) async {
|
'ipAddress: ${printer.address}',
|
||||||
try {
|
],
|
||||||
if (printer.type == 'Bluetooth') {
|
|
||||||
bool connected = await connectBluetoothPrinter(printer.address);
|
|
||||||
if (!connected) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Failed to connect to ${printer.name}')),
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool printResult = await printBluetooth(printData);
|
|
||||||
if (!printResult) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Failed to print to ${printer.name}')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return printResult;
|
|
||||||
} else {
|
|
||||||
bool printResult = await printNetwork(printer.address, printData);
|
|
||||||
if (!printResult) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Failed to print to ${printer.name}')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return printResult;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing with printer ${printer.name}: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Error printing to ${printer.name}: $e')),
|
|
||||||
);
|
);
|
||||||
|
log("Error printing via Network: $e");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,4 +263,4 @@ class PrinterService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,912 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:enaklo_pos/core/extensions/string_ext.dart';
|
||||||
|
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:pdf/pdf.dart';
|
||||||
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
|
|
||||||
|
class TransactionReport {
|
||||||
|
static final primaryColor = PdfColor.fromHex("36175e");
|
||||||
|
|
||||||
|
static Future<File> previewPdf({
|
||||||
|
required Outlet outlet,
|
||||||
|
required String searchDateFormatted,
|
||||||
|
required CategoryAnalyticData? categoryAnalyticData,
|
||||||
|
required ProfitLossData? profitLossData,
|
||||||
|
required PaymentMethodAnalyticData? paymentMethodAnalyticData,
|
||||||
|
required ProductAnalyticData? productAnalyticData,
|
||||||
|
}) async {
|
||||||
|
final pdf = pw.Document();
|
||||||
|
final ByteData dataImage = await rootBundle.load('assets/logo/logo.png');
|
||||||
|
final Uint8List bytes = dataImage.buffer.asUint8List();
|
||||||
|
|
||||||
|
final profitLossProductSummary = {
|
||||||
|
'totalRevenue': profitLossData?.productData
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.revenue)) ??
|
||||||
|
0,
|
||||||
|
'totalCost': profitLossData?.productData
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.cost)) ??
|
||||||
|
0,
|
||||||
|
'totalGrossProfit': profitLossData?.productData
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.grossProfit)) ??
|
||||||
|
0,
|
||||||
|
'totalQuantity': profitLossData?.productData
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.quantitySold)) ??
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
final categorySummary = {
|
||||||
|
'totalRevenue': categoryAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.totalRevenue)) ??
|
||||||
|
0,
|
||||||
|
'orderCount': categoryAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.orderCount)) ??
|
||||||
|
0,
|
||||||
|
'productCount': categoryAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.productCount)) ??
|
||||||
|
0,
|
||||||
|
'totalQuantity': categoryAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.totalQuantity)) ??
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
final productItemSummary = {
|
||||||
|
'totalRevenue': productAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.revenue)) ??
|
||||||
|
0,
|
||||||
|
'orderCount': productAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.orderCount)) ??
|
||||||
|
0,
|
||||||
|
'totalQuantitySold': productAnalyticData?.data
|
||||||
|
.fold<num>(0, (sum, item) => sum + (item.quantitySold)) ??
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Membuat objek Image dari gambar
|
||||||
|
final image = pw.MemoryImage(bytes);
|
||||||
|
pdf.addPage(
|
||||||
|
pw.MultiPage(
|
||||||
|
pageFormat: PdfPageFormat.a4,
|
||||||
|
margin: pw.EdgeInsets.zero,
|
||||||
|
build: (pw.Context context) {
|
||||||
|
return [
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Row(
|
||||||
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Bagian kiri - Logo dan Info Perusahaan
|
||||||
|
pw.Row(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Icon/Logo placeholder (bisa diganti dengan gambar logo)
|
||||||
|
pw.Container(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
child: pw.Image(image),
|
||||||
|
),
|
||||||
|
pw.SizedBox(width: 15),
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
pw.Text(
|
||||||
|
'Apskel',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 4),
|
||||||
|
pw.Text(
|
||||||
|
outlet.name ?? "",
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: PdfColors.grey700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 2),
|
||||||
|
pw.Text(
|
||||||
|
outlet.address ?? "",
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: PdfColors.grey600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// Bagian kanan - Info Laporan
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
pw.Text(
|
||||||
|
'Laporan Transaksi',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: PdfColors.grey800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 8),
|
||||||
|
pw.Text(
|
||||||
|
searchDateFormatted,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: PdfColors.grey600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 4),
|
||||||
|
pw.Text(
|
||||||
|
'Laporan',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: PdfColors.grey500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 3,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('1. Ringkasan'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Row(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
pw.Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Penjualan (termasuk rasik)',
|
||||||
|
(profitLossData?.summary.totalRevenue ?? 0)
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Total Terjual',
|
||||||
|
(profitLossData?.summary.totalOrders ?? 0)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'HPP',
|
||||||
|
'${(profitLossData?.summary.totalCost ?? 0).toString().currencyFormatRpV2} | ${(((profitLossData?.summary.totalCost ?? 0) / (profitLossData?.summary.totalRevenue ?? 1)) * 100).round()}%',
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Laba Kotor',
|
||||||
|
'${(profitLossData?.summary.grossProfit ?? 0).toString().currencyFormatRpV2} | ${(profitLossData?.summary.grossProfitMargin ?? 0).round()}%',
|
||||||
|
valueStyle: pw.TextStyle(
|
||||||
|
color: PdfColors.green800,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
labelStyle: pw.TextStyle(
|
||||||
|
color: PdfColors.green800,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(width: 20),
|
||||||
|
pw.Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Biaya Lain lain',
|
||||||
|
'${(profitLossData?.summary.totalTax ?? 0).toString().currencyFormatRpV2} | ${(((profitLossData?.summary.totalTax ?? 0) / (profitLossData?.summary.totalRevenue ?? 1)) * 100).round()}%',
|
||||||
|
),
|
||||||
|
_buildSummaryItem(
|
||||||
|
'Laba/Rugi',
|
||||||
|
'${(profitLossData?.summary.netProfit ?? 0).toString().currencyFormatRpV2} | ${(profitLossData?.summary.netProfitMargin ?? 0).round()}%',
|
||||||
|
valueStyle: pw.TextStyle(
|
||||||
|
color: PdfColors.blue800,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
labelStyle: pw.TextStyle(
|
||||||
|
color: PdfColors.blue800,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 16),
|
||||||
|
pw.Text(
|
||||||
|
"Laba Rugi Perproduk",
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 20),
|
||||||
|
pw.Column(
|
||||||
|
children: [
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
topLeft: pw.Radius.circular(8),
|
||||||
|
topRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(1), // Qty
|
||||||
|
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||||
|
3: pw.FlexColumnWidth(2), // HPP
|
||||||
|
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||||
|
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Produk'),
|
||||||
|
_buildHeaderCell('Qty'),
|
||||||
|
_buildHeaderCell('Pendapatan'),
|
||||||
|
_buildHeaderCell('HPP'),
|
||||||
|
_buildHeaderCell('Laba Kotor'),
|
||||||
|
_buildHeaderCell('Margin (%)'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: PdfColors.white,
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(1), // Qty
|
||||||
|
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||||
|
3: pw.FlexColumnWidth(2), // HPP
|
||||||
|
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||||
|
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||||
|
},
|
||||||
|
children: profitLossData?.productData
|
||||||
|
.map(
|
||||||
|
(profitLoss) => _buildPerProductDataRow(
|
||||||
|
product: profitLoss.productName,
|
||||||
|
qty: profitLoss.quantitySold.toString(),
|
||||||
|
pendapatan: profitLoss.revenue
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
hpp: profitLoss.cost
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
labaKotor: profitLoss.grossProfit
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
margin:
|
||||||
|
'${profitLoss.grossProfitMargin.round()}%',
|
||||||
|
isEven: profitLossData.productData
|
||||||
|
.indexOf(profitLoss) %
|
||||||
|
2 ==
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
bottomLeft: pw.Radius.circular(8),
|
||||||
|
bottomRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(1), // Qty
|
||||||
|
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||||
|
3: pw.FlexColumnWidth(2), // HPP
|
||||||
|
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||||
|
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(
|
||||||
|
profitLossProductSummary['totalQuantity']
|
||||||
|
.toString()),
|
||||||
|
_buildTotalCell(
|
||||||
|
profitLossProductSummary['totalRevenue']
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2),
|
||||||
|
_buildTotalCell(
|
||||||
|
profitLossProductSummary['totalCost']
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2),
|
||||||
|
_buildTotalCell(
|
||||||
|
profitLossProductSummary['totalGrossProfit']
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary Payment Method
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
children: [
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('2. Ringkasan Metode Pembayaran'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
topLeft: pw.Radius.circular(8),
|
||||||
|
topRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Nama
|
||||||
|
1: pw.FlexColumnWidth(1), // Tipe
|
||||||
|
2: pw.FlexColumnWidth(2.5), // Jumlah Order
|
||||||
|
3: pw.FlexColumnWidth(2), // Total Amount
|
||||||
|
4: pw.FlexColumnWidth(2), // Presentase
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Nama'),
|
||||||
|
_buildHeaderCell('Tipe'),
|
||||||
|
_buildHeaderCell('Jumlah Order'),
|
||||||
|
_buildHeaderCell('Total Amount'),
|
||||||
|
_buildHeaderCell('Presentase'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: PdfColors.white,
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Nama
|
||||||
|
1: pw.FlexColumnWidth(1), // Tipe
|
||||||
|
2: pw.FlexColumnWidth(2.5), // Jumlah Order
|
||||||
|
3: pw.FlexColumnWidth(2), // Total Amount
|
||||||
|
4: pw.FlexColumnWidth(2), // Presentase
|
||||||
|
},
|
||||||
|
children: paymentMethodAnalyticData?.data
|
||||||
|
.map(
|
||||||
|
(payment) => _buildPaymentMethodDataRow(
|
||||||
|
name: payment.paymentMethodName,
|
||||||
|
tipe: payment.paymentMethodType
|
||||||
|
.toTitleCase(),
|
||||||
|
jumlahOrder:
|
||||||
|
payment.orderCount.toString(),
|
||||||
|
totalAmount: payment.totalAmount
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
presentase:
|
||||||
|
'${payment.percentage.round()}%',
|
||||||
|
isEven: paymentMethodAnalyticData.data
|
||||||
|
.indexOf(payment) %
|
||||||
|
2 ==
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
bottomLeft: pw.Radius.circular(8),
|
||||||
|
bottomRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(1), // Qty
|
||||||
|
2: pw.FlexColumnWidth(2.5), // Pendapatan
|
||||||
|
3: pw.FlexColumnWidth(2), // HPP
|
||||||
|
4: pw.FlexColumnWidth(2), // Laba Kotor
|
||||||
|
5: pw.FlexColumnWidth(2), // Margin (%)
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
_buildTotalCell((paymentMethodAnalyticData
|
||||||
|
?.summary.totalOrders ??
|
||||||
|
0)
|
||||||
|
.toString()),
|
||||||
|
_buildTotalCell((paymentMethodAnalyticData
|
||||||
|
?.summary.totalAmount ??
|
||||||
|
0)
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary Category
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('3. Ringkasan Kategori'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Column(
|
||||||
|
children: [
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
topLeft: pw.Radius.circular(8),
|
||||||
|
topRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Nama
|
||||||
|
1: pw.FlexColumnWidth(2), // Total Product
|
||||||
|
2: pw.FlexColumnWidth(1), // qty
|
||||||
|
3: pw.FlexColumnWidth(2), // Jumlah Order
|
||||||
|
4: pw.FlexColumnWidth(2.5), // Presentase
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Nama'),
|
||||||
|
_buildHeaderCell('Total Produk'),
|
||||||
|
_buildHeaderCell('Qty'),
|
||||||
|
_buildHeaderCell('Jumlah Order'),
|
||||||
|
_buildHeaderCell('Pendapatan'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: PdfColors.white,
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Nama
|
||||||
|
1: pw.FlexColumnWidth(2), // Total Product
|
||||||
|
2: pw.FlexColumnWidth(1), // qty
|
||||||
|
3: pw.FlexColumnWidth(2), // Jumlah Order
|
||||||
|
4: pw.FlexColumnWidth(2.5), // Presentase
|
||||||
|
},
|
||||||
|
children: categoryAnalyticData?.data
|
||||||
|
.map((category) => _buildCategoryDataRow(
|
||||||
|
name: category.categoryName,
|
||||||
|
totalProduct:
|
||||||
|
category.productCount.toString(),
|
||||||
|
qty: category.totalQuantity.toString(),
|
||||||
|
jumlahOrder:
|
||||||
|
category.orderCount.toString(),
|
||||||
|
pendapatan: category.totalRevenue
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
isEven: categoryAnalyticData.data
|
||||||
|
.indexOf(category) %
|
||||||
|
2 ==
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
bottomLeft: pw.Radius.circular(8),
|
||||||
|
bottomRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Nama
|
||||||
|
1: pw.FlexColumnWidth(2), // Total Product
|
||||||
|
2: pw.FlexColumnWidth(1), // qty
|
||||||
|
3: pw.FlexColumnWidth(2), // Jumlah Order
|
||||||
|
4: pw.FlexColumnWidth(2.5), // Presentase
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(
|
||||||
|
categorySummary['productCount'].toString()),
|
||||||
|
_buildTotalCell(categorySummary['totalQuantity']
|
||||||
|
.toString()),
|
||||||
|
_buildTotalCell(
|
||||||
|
categorySummary['orderCount'].toString()),
|
||||||
|
_buildTotalCell(categorySummary['totalRevenue']
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Summary Item
|
||||||
|
pw.Container(
|
||||||
|
padding: pw.EdgeInsets.all(20),
|
||||||
|
child: pw.Column(
|
||||||
|
children: [
|
||||||
|
pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionWidget('4. Ringkasan Item'),
|
||||||
|
pw.SizedBox(height: 30),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
topLeft: pw.Radius.circular(8),
|
||||||
|
topRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(2), // Kategori
|
||||||
|
2: pw.FlexColumnWidth(1), // qty
|
||||||
|
3: pw.FlexColumnWidth(2), // Order
|
||||||
|
4: pw.FlexColumnWidth(2), // Pendapatan
|
||||||
|
5: pw.FlexColumnWidth(2), // Average
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Produk'),
|
||||||
|
_buildHeaderCell('Kategori'),
|
||||||
|
_buildHeaderCell('Qty'),
|
||||||
|
_buildHeaderCell('Order'),
|
||||||
|
_buildHeaderCell('Pendapatan'),
|
||||||
|
_buildHeaderCell('Rata Rata'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: PdfColors.white,
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(2), // Kategori
|
||||||
|
2: pw.FlexColumnWidth(1), // qty
|
||||||
|
3: pw.FlexColumnWidth(2), // Order
|
||||||
|
4: pw.FlexColumnWidth(2), // Pendapatan
|
||||||
|
5: pw.FlexColumnWidth(2), // Average
|
||||||
|
},
|
||||||
|
children: productAnalyticData?.data
|
||||||
|
.map((item) => _buildItemDataRow(
|
||||||
|
product: item.productName,
|
||||||
|
category: item.categoryName,
|
||||||
|
qty: item.quantitySold.toString(),
|
||||||
|
order: item.orderCount.toString(),
|
||||||
|
pendapatan: item.revenue
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
average: item.averagePrice
|
||||||
|
.round()
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
isEven: productAnalyticData.data
|
||||||
|
.indexOf(item) %
|
||||||
|
2 ==
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.Container(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: primaryColor, // Purple color
|
||||||
|
borderRadius: pw.BorderRadius.only(
|
||||||
|
bottomLeft: pw.Radius.circular(8),
|
||||||
|
bottomRight: pw.Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: pw.FlexColumnWidth(2.5), // Produk
|
||||||
|
1: pw.FlexColumnWidth(2), // Kategori
|
||||||
|
2: pw.FlexColumnWidth(1), // qty
|
||||||
|
3: pw.FlexColumnWidth(2), // Order
|
||||||
|
4: pw.FlexColumnWidth(2), // Pendapatan
|
||||||
|
5: pw.FlexColumnWidth(2), // Average
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
_buildTotalCell(
|
||||||
|
productItemSummary['totalQuantitySold']
|
||||||
|
.toString()),
|
||||||
|
_buildTotalCell(productItemSummary['orderCount']
|
||||||
|
.toString()),
|
||||||
|
_buildTotalCell(
|
||||||
|
productItemSummary['totalRevenue']
|
||||||
|
.toString()
|
||||||
|
.currencyFormatRpV2),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return HelperPdfService.saveDocument(
|
||||||
|
name: 'Laporan Transaksi | $searchDateFormatted.pdf', pdf: pdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildSectionWidget(String title) {
|
||||||
|
return pw.Text(
|
||||||
|
title,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildSummaryItem(
|
||||||
|
String label,
|
||||||
|
String value, {
|
||||||
|
pw.TextStyle? valueStyle,
|
||||||
|
pw.TextStyle? labelStyle,
|
||||||
|
}) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.only(bottom: 8),
|
||||||
|
margin: pw.EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
border: pw.Border(
|
||||||
|
bottom: pw.BorderSide(
|
||||||
|
color: PdfColors.grey300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: pw.Row(
|
||||||
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
pw.Text(label, style: labelStyle),
|
||||||
|
pw.Text(
|
||||||
|
value,
|
||||||
|
style: valueStyle ??
|
||||||
|
pw.TextStyle(
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildHeaderCell(String text) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
color: PdfColors.white,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: pw.TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildDataCell(String text,
|
||||||
|
{pw.Alignment alignment = pw.Alignment.center, PdfColor? textColor}) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
alignment: alignment,
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: textColor ?? PdfColors.black,
|
||||||
|
fontWeight: pw.FontWeight.normal,
|
||||||
|
),
|
||||||
|
textAlign: alignment == pw.Alignment.centerLeft
|
||||||
|
? pw.TextAlign.left
|
||||||
|
: pw.TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.Widget _buildTotalCell(String text) {
|
||||||
|
return pw.Container(
|
||||||
|
padding: pw.EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
color: PdfColors.white,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: pw.TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.TableRow _buildPerProductDataRow({
|
||||||
|
required String product,
|
||||||
|
required String qty,
|
||||||
|
required String pendapatan,
|
||||||
|
required String hpp,
|
||||||
|
required String labaKotor,
|
||||||
|
required String margin,
|
||||||
|
required bool isEven,
|
||||||
|
}) {
|
||||||
|
return pw.TableRow(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(product, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(qty),
|
||||||
|
_buildDataCell(pendapatan),
|
||||||
|
_buildDataCell(hpp, textColor: PdfColors.red600),
|
||||||
|
_buildDataCell(labaKotor, textColor: PdfColors.green600),
|
||||||
|
_buildDataCell(margin),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.TableRow _buildPaymentMethodDataRow({
|
||||||
|
required String name,
|
||||||
|
required String tipe,
|
||||||
|
required String jumlahOrder,
|
||||||
|
required String totalAmount,
|
||||||
|
required String presentase,
|
||||||
|
required bool isEven,
|
||||||
|
}) {
|
||||||
|
return pw.TableRow(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(name, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(tipe),
|
||||||
|
_buildDataCell(jumlahOrder),
|
||||||
|
_buildDataCell(totalAmount),
|
||||||
|
_buildDataCell(presentase),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.TableRow _buildCategoryDataRow({
|
||||||
|
required String name,
|
||||||
|
required String totalProduct,
|
||||||
|
required String qty,
|
||||||
|
required String jumlahOrder,
|
||||||
|
required String pendapatan,
|
||||||
|
required bool isEven,
|
||||||
|
}) {
|
||||||
|
return pw.TableRow(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(name, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(totalProduct),
|
||||||
|
_buildDataCell(qty),
|
||||||
|
_buildDataCell(jumlahOrder),
|
||||||
|
_buildDataCell(pendapatan),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pw.TableRow _buildItemDataRow({
|
||||||
|
required String product,
|
||||||
|
required String category,
|
||||||
|
required String qty,
|
||||||
|
required String order,
|
||||||
|
required String pendapatan,
|
||||||
|
required String average,
|
||||||
|
required bool isEven,
|
||||||
|
}) {
|
||||||
|
return pw.TableRow(
|
||||||
|
decoration: pw.BoxDecoration(
|
||||||
|
color: isEven ? PdfColors.grey50 : PdfColors.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(product, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(category, alignment: pw.Alignment.centerLeft),
|
||||||
|
_buildDataCell(qty),
|
||||||
|
_buildDataCell(order),
|
||||||
|
_buildDataCell(pendapatan),
|
||||||
|
_buildDataCell(average),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,9 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:enaklo_pos/core/constants/variables.dart';
|
import 'package:enaklo_pos/core/constants/variables.dart';
|
||||||
import 'package:enaklo_pos/core/network/dio_client.dart';
|
import 'package:enaklo_pos/core/network/dio_client.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/dashboard_analytic_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/dashboard_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/inventory_analytic_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
|
||||||
@@ -184,4 +186,72 @@ class AnalyticRemoteDatasource {
|
|||||||
return left('Unexpected error occurred');
|
return left('Unexpected error occurred');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Either<String, CategoryAnalyticResponseModel>> getCategory({
|
||||||
|
required DateTime dateFrom,
|
||||||
|
required DateTime dateTo,
|
||||||
|
}) async {
|
||||||
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
final headers = {
|
||||||
|
'Authorization': 'Bearer ${authData.token}',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await dio.get(
|
||||||
|
'${Variables.baseUrl}/api/v1/analytics/categories',
|
||||||
|
queryParameters: {
|
||||||
|
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
|
||||||
|
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
|
||||||
|
},
|
||||||
|
options: Options(headers: headers),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return right(CategoryAnalyticResponseModel.fromMap(response.data));
|
||||||
|
} else {
|
||||||
|
return left('Terjadi Kesalahan, Coba lagi nanti.');
|
||||||
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
|
log('Dio error: ${e.message}');
|
||||||
|
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
|
||||||
|
} catch (e) {
|
||||||
|
log('Unexpected error: $e');
|
||||||
|
return left('Unexpected error occurred');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<String, InventoryAnalyticResponseModel>> getInventory({
|
||||||
|
required DateTime dateFrom,
|
||||||
|
required DateTime dateTo,
|
||||||
|
}) async {
|
||||||
|
final authData = await AuthLocalDataSource().getAuthData();
|
||||||
|
final headers = {
|
||||||
|
'Authorization': 'Bearer ${authData.token}',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await dio.get(
|
||||||
|
'${Variables.baseUrl}/api/v1/inventory/report/details/${authData.user?.outletId}',
|
||||||
|
queryParameters: {
|
||||||
|
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
|
||||||
|
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
|
||||||
|
},
|
||||||
|
options: Options(headers: headers),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return right(InventoryAnalyticResponseModel.fromMap(response.data));
|
||||||
|
} else {
|
||||||
|
return left('Terjadi Kesalahan, Coba lagi nanti.');
|
||||||
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
|
log('Dio error: ${e.message}');
|
||||||
|
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
|
||||||
|
} catch (e) {
|
||||||
|
log('Unexpected error: $e');
|
||||||
|
return left('Unexpected error occurred');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,373 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'package:enaklo_pos/core/database/database_handler.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
class CategoryLocalDatasource {
|
||||||
|
static CategoryLocalDatasource? _instance;
|
||||||
|
|
||||||
|
CategoryLocalDatasource._internal();
|
||||||
|
|
||||||
|
static CategoryLocalDatasource get instance {
|
||||||
|
_instance ??= CategoryLocalDatasource._internal();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Database> get _db async => await DatabaseHelper.instance.database;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CACHING SYSTEM
|
||||||
|
// ========================================
|
||||||
|
final Map<String, List<CategoryModel>> _queryCache = {};
|
||||||
|
final Duration _cacheExpiry =
|
||||||
|
Duration(minutes: 10); // Lebih lama untuk categories
|
||||||
|
final Map<String, DateTime> _cacheTimestamps = {};
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// BATCH SAVE CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<void> saveCategoriesBatch(List<CategoryModel> categories,
|
||||||
|
{bool clearFirst = false}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.transaction((txn) async {
|
||||||
|
if (clearFirst) {
|
||||||
|
log('🗑️ Clearing existing categories...');
|
||||||
|
await txn.delete('categories');
|
||||||
|
}
|
||||||
|
|
||||||
|
log('💾 Batch saving ${categories.length} categories...');
|
||||||
|
|
||||||
|
// Batch insert categories
|
||||||
|
final batch = txn.batch();
|
||||||
|
for (final category in categories) {
|
||||||
|
batch.insert(
|
||||||
|
'categories',
|
||||||
|
_categoryToMap(category),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await batch.commit(noResult: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear cache after update
|
||||||
|
clearCache();
|
||||||
|
log('✅ Successfully batch saved ${categories.length} categories');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error batch saving categories: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CACHED QUERY
|
||||||
|
// ========================================
|
||||||
|
Future<List<CategoryModel>> getCachedCategories({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
bool isActive = true,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
final cacheKey = _generateCacheKey(page, limit, isActive, search);
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
// Check cache first
|
||||||
|
if (_queryCache.containsKey(cacheKey) &&
|
||||||
|
_cacheTimestamps.containsKey(cacheKey)) {
|
||||||
|
final cacheTime = _cacheTimestamps[cacheKey]!;
|
||||||
|
if (now.difference(cacheTime) < _cacheExpiry) {
|
||||||
|
log('🚀 Cache HIT: $cacheKey (${_queryCache[cacheKey]!.length} categories)');
|
||||||
|
return _queryCache[cacheKey]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log('📀 Cache MISS: $cacheKey, querying database...');
|
||||||
|
|
||||||
|
// Cache miss, query database
|
||||||
|
final categories = await getCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: isActive,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store in cache
|
||||||
|
_queryCache[cacheKey] = categories;
|
||||||
|
_cacheTimestamps[cacheKey] = now;
|
||||||
|
|
||||||
|
log('💾 Cached ${categories.length} categories for key: $cacheKey');
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// REGULAR GET CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<List<CategoryModel>> getCategories({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
bool isActive = true,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String query = 'SELECT * FROM categories WHERE 1=1';
|
||||||
|
List<dynamic> whereArgs = [];
|
||||||
|
|
||||||
|
// Note: Assuming is_active will be added to database schema
|
||||||
|
if (isActive) {
|
||||||
|
query += ' AND is_active = ?';
|
||||||
|
whereArgs.add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search != null && search.isNotEmpty) {
|
||||||
|
query += ' AND (name LIKE ? OR description LIKE ?)';
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// query += ' ORDER BY name ASC';
|
||||||
|
|
||||||
|
if (limit > 0) {
|
||||||
|
query += ' LIMIT ?';
|
||||||
|
whereArgs.add(limit);
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
query += ' OFFSET ?';
|
||||||
|
whereArgs.add((page - 1) * limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Map<String, dynamic>> maps =
|
||||||
|
await db.rawQuery(query, whereArgs);
|
||||||
|
|
||||||
|
List<CategoryModel> categories = [];
|
||||||
|
for (final map in maps) {
|
||||||
|
categories.add(_mapToCategory(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
log('📊 Retrieved ${categories.length} categories from database');
|
||||||
|
return categories;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting categories: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// GET ALL CATEGORIES (For dropdowns)
|
||||||
|
// ========================================
|
||||||
|
Future<List<CategoryModel>> getAllCategories() async {
|
||||||
|
const cacheKey = 'all_categories';
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
// Check cache
|
||||||
|
if (_queryCache.containsKey(cacheKey) &&
|
||||||
|
_cacheTimestamps.containsKey(cacheKey)) {
|
||||||
|
final cacheTime = _cacheTimestamps[cacheKey]!;
|
||||||
|
if (now.difference(cacheTime) < _cacheExpiry) {
|
||||||
|
return _queryCache[cacheKey]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final List<Map<String, dynamic>> maps = await db.query(
|
||||||
|
'categories',
|
||||||
|
orderBy: 'name ASC',
|
||||||
|
);
|
||||||
|
|
||||||
|
final categories = maps.map((map) => _mapToCategory(map)).toList();
|
||||||
|
|
||||||
|
// Cache all categories
|
||||||
|
_queryCache[cacheKey] = categories;
|
||||||
|
_cacheTimestamps[cacheKey] = now;
|
||||||
|
|
||||||
|
log('📊 Retrieved ${categories.length} total categories');
|
||||||
|
return categories;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting all categories: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// GET CATEGORY BY ID
|
||||||
|
// ========================================
|
||||||
|
Future<CategoryModel?> getCategoryById(String id) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final List<Map<String, dynamic>> maps = await db.query(
|
||||||
|
'categories',
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (maps.isEmpty) {
|
||||||
|
log('❌ Category not found: $id');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final category = _mapToCategory(maps.first);
|
||||||
|
log('✅ Category found: ${category.name}');
|
||||||
|
return category;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting category by ID: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// GET TOTAL COUNT
|
||||||
|
// ========================================
|
||||||
|
Future<int> getTotalCount({bool isActive = true, String? search}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String query = 'SELECT COUNT(*) FROM categories WHERE 1=1';
|
||||||
|
List<dynamic> whereArgs = [];
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
query += ' AND is_active = ?';
|
||||||
|
whereArgs.add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search != null && search.isNotEmpty) {
|
||||||
|
query += ' AND (name LIKE ? OR description LIKE ?)';
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await db.rawQuery(query, whereArgs);
|
||||||
|
final count = Sqflite.firstIntValue(result) ?? 0;
|
||||||
|
log('📊 Category total count: $count (isActive: $isActive, search: $search)');
|
||||||
|
return count;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting category total count: $e');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// HAS CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<bool> hasCategories() async {
|
||||||
|
final count = await getTotalCount();
|
||||||
|
final hasData = count > 0;
|
||||||
|
log('🔍 Has categories: $hasData ($count categories)');
|
||||||
|
return hasData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CLEAR ALL CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<void> clearAllCategories() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.delete('categories');
|
||||||
|
clearCache();
|
||||||
|
log('🗑️ All categories cleared from local DB');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error clearing categories: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CACHE MANAGEMENT
|
||||||
|
// ========================================
|
||||||
|
String _generateCacheKey(int page, int limit, bool isActive, String? search) {
|
||||||
|
return 'categories_${page}_${limit}_${isActive}_${search ?? 'null'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearCache() {
|
||||||
|
final count = _queryCache.length;
|
||||||
|
_queryCache.clear();
|
||||||
|
_cacheTimestamps.clear();
|
||||||
|
log('🧹 Category cache cleared: $count entries removed');
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearExpiredCache() {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final expiredKeys = <String>[];
|
||||||
|
|
||||||
|
_cacheTimestamps.forEach((key, timestamp) {
|
||||||
|
if (now.difference(timestamp) > _cacheExpiry) {
|
||||||
|
expiredKeys.add(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final key in expiredKeys) {
|
||||||
|
_queryCache.remove(key);
|
||||||
|
_cacheTimestamps.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expiredKeys.isNotEmpty) {
|
||||||
|
log('⏰ Expired category cache cleared: ${expiredKeys.length} entries');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// DATABASE STATS
|
||||||
|
// ========================================
|
||||||
|
Future<Map<String, dynamic>> getDatabaseStats() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final categoryCount = Sqflite.firstIntValue(
|
||||||
|
await db.rawQuery('SELECT COUNT(*) FROM categories')) ??
|
||||||
|
0;
|
||||||
|
|
||||||
|
final activeCount = Sqflite.firstIntValue(await db.rawQuery(
|
||||||
|
'SELECT COUNT(*) FROM categories WHERE is_active = 1')) ??
|
||||||
|
0;
|
||||||
|
|
||||||
|
final stats = {
|
||||||
|
'total_categories': categoryCount,
|
||||||
|
'active_categories': activeCount,
|
||||||
|
'cache_entries': _queryCache.length,
|
||||||
|
};
|
||||||
|
|
||||||
|
log('📊 Category Database Stats: $stats');
|
||||||
|
return stats;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting category database stats: $e');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// HELPER METHODS
|
||||||
|
// ========================================
|
||||||
|
Map<String, dynamic> _categoryToMap(CategoryModel category) {
|
||||||
|
return {
|
||||||
|
'id': category.id,
|
||||||
|
'organization_id': category.organizationId,
|
||||||
|
'name': category.name,
|
||||||
|
'description': category.description,
|
||||||
|
'business_type': category.businessType,
|
||||||
|
'metadata': json.encode(category.metadata),
|
||||||
|
'is_active': 1, // Assuming all synced categories are active
|
||||||
|
'created_at': category.createdAt.toIso8601String(),
|
||||||
|
'updated_at': category.updatedAt.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CategoryModel _mapToCategory(Map<String, dynamic> map) {
|
||||||
|
return CategoryModel(
|
||||||
|
id: map['id'],
|
||||||
|
organizationId: map['organization_id'],
|
||||||
|
name: map['name'],
|
||||||
|
description: map['description'],
|
||||||
|
businessType: map['business_type'],
|
||||||
|
metadata: map['metadata'] != null ? json.decode(map['metadata']) : {},
|
||||||
|
createdAt: DateTime.parse(map['created_at']),
|
||||||
|
updatedAt: DateTime.parse(map['updated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -405,7 +405,7 @@ class OrderRemoteDatasource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<String, OrderDetailResponseModel>> addToOrder({
|
Future<Either<String, bool>> addToOrder({
|
||||||
required String orderId,
|
required String orderId,
|
||||||
required List<OrderItemRequest> orderItems,
|
required List<OrderItemRequest> orderItems,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -429,8 +429,7 @@ class OrderRemoteDatasource {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = OrderDetailResponseModel.fromMap(response.data);
|
return Right(true);
|
||||||
return Right(data);
|
|
||||||
} else {
|
} else {
|
||||||
return const Left('Gagal menambahkan pesanan pesanan');
|
return const Left('Gagal menambahkan pesanan pesanan');
|
||||||
}
|
}
|
||||||
@@ -442,7 +441,7 @@ class OrderRemoteDatasource {
|
|||||||
return Left(errorMessage);
|
return Left(errorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("💥 Unexpected error: $e");
|
log("💥 Unexpected error: $e");
|
||||||
return const Left('Terjadi kesalahan tak terduga');
|
return const Left('Terjadi kesalahan, coba lagi nanti.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -2,12 +2,12 @@ import 'dart:developer';
|
|||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:enaklo_pos/core/network/dio_client.dart';
|
import 'package:enaklo_pos/core/network/dio_client.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/outlet_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/models/tax_model.dart';
|
import 'package:enaklo_pos/presentation/setting/models/tax_model.dart';
|
||||||
import '../../core/constants/variables.dart';
|
import '../../../core/constants/variables.dart';
|
||||||
import 'auth_local_datasource.dart';
|
import '../auth_local_datasource.dart';
|
||||||
|
|
||||||
class OutletRemoteDataSource {
|
class OutletRemoteDataSource {
|
||||||
final Dio dio = DioClient.instance;
|
final Dio dio = DioClient.instance;
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'package:enaklo_pos/core/database/database_handler.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/print_model.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
|
class PrinterLocalDatasource {
|
||||||
|
static PrinterLocalDatasource? _instance;
|
||||||
|
|
||||||
|
PrinterLocalDatasource._internal();
|
||||||
|
|
||||||
|
static PrinterLocalDatasource get instance {
|
||||||
|
_instance ??= PrinterLocalDatasource._internal();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Database> get _db async => await DatabaseHelper.instance.database;
|
||||||
|
|
||||||
|
// Create new printer
|
||||||
|
Future<int> createPrinter(PrintModel printer) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Creating printer: ${printer.toString()}');
|
||||||
|
|
||||||
|
final id = await db.insert(
|
||||||
|
'printers',
|
||||||
|
printer.toMapForInsert(),
|
||||||
|
conflictAlgorithm:
|
||||||
|
ConflictAlgorithm.abort, // Fail if code already exists
|
||||||
|
);
|
||||||
|
|
||||||
|
log('Successfully created printer with ID: $id');
|
||||||
|
return id;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error creating printer: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update existing printer
|
||||||
|
Future<void> updatePrinter(PrintModel printer, int id) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Updating printer ID $id: ${printer.toString()}');
|
||||||
|
|
||||||
|
final updatedRows = await db.update(
|
||||||
|
'printers',
|
||||||
|
printer.toMapForUpdate(),
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (updatedRows == 0) {
|
||||||
|
throw Exception('Printer with ID $id not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Successfully updated printer ID: $id');
|
||||||
|
} catch (e) {
|
||||||
|
log('Error updating printer: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete printer by ID
|
||||||
|
Future<void> deletePrinter(int id) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Deleting printer ID: $id');
|
||||||
|
|
||||||
|
final deletedRows = await db.delete(
|
||||||
|
'printers',
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deletedRows == 0) {
|
||||||
|
throw Exception('Printer with ID $id not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Successfully deleted printer ID: $id');
|
||||||
|
} catch (e) {
|
||||||
|
log('Error deleting printer: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get printer by code
|
||||||
|
Future<PrintModel?> getPrinterByCode(String code) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Getting printer by code: $code');
|
||||||
|
|
||||||
|
final result = await db.query(
|
||||||
|
'printers',
|
||||||
|
where: 'code = ?',
|
||||||
|
whereArgs: [code],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.isEmpty) {
|
||||||
|
log('Printer with code $code not found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final printer = PrintModel.fromMap(result.first);
|
||||||
|
log('Found printer: ${printer.toString()}');
|
||||||
|
return printer;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error getting printer by code: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get printer by ID
|
||||||
|
Future<PrintModel?> getPrinterById(int id) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Getting printer by ID: $id');
|
||||||
|
|
||||||
|
final result = await db.query(
|
||||||
|
'printers',
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.isEmpty) {
|
||||||
|
log('Printer with ID $id not found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final printer = PrintModel.fromMap(result.first);
|
||||||
|
log('Found printer: ${printer.toString()}');
|
||||||
|
return printer;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error getting printer by ID: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all printers
|
||||||
|
Future<List<PrintModel>> getAllPrinters() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Getting all printers');
|
||||||
|
|
||||||
|
final result = await db.query(
|
||||||
|
'printers',
|
||||||
|
orderBy: 'name ASC',
|
||||||
|
);
|
||||||
|
|
||||||
|
final printers = result.map((map) => PrintModel.fromMap(map)).toList();
|
||||||
|
log('Found ${printers.length} printers');
|
||||||
|
|
||||||
|
return printers;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error getting all printers: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get printers by type
|
||||||
|
Future<List<PrintModel>> getPrintersByType(String type) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Getting printers by type: $type');
|
||||||
|
|
||||||
|
final result = await db.query(
|
||||||
|
'printers',
|
||||||
|
where: 'type = ?',
|
||||||
|
whereArgs: [type],
|
||||||
|
orderBy: 'name ASC',
|
||||||
|
);
|
||||||
|
|
||||||
|
final printers = result.map((map) => PrintModel.fromMap(map)).toList();
|
||||||
|
log('Found ${printers.length} printers with type $type');
|
||||||
|
|
||||||
|
return printers;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error getting printers by type: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search printers by name
|
||||||
|
Future<List<PrintModel>> searchPrintersByName(String query) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Searching printers by name: $query');
|
||||||
|
|
||||||
|
final result = await db.query(
|
||||||
|
'printers',
|
||||||
|
where: 'name LIKE ?',
|
||||||
|
whereArgs: ['%$query%'],
|
||||||
|
orderBy: 'name ASC',
|
||||||
|
);
|
||||||
|
|
||||||
|
final printers = result.map((map) => PrintModel.fromMap(map)).toList();
|
||||||
|
log('Found ${printers.length} printers matching "$query"');
|
||||||
|
|
||||||
|
return printers;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error searching printers: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if printer code exists
|
||||||
|
Future<bool> isPrinterCodeExists(String code, {int? excludeId}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String whereClause = 'code = ?';
|
||||||
|
List<dynamic> whereArgs = [code];
|
||||||
|
|
||||||
|
if (excludeId != null) {
|
||||||
|
whereClause += ' AND id != ?';
|
||||||
|
whereArgs.add(excludeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await db.query(
|
||||||
|
'printers',
|
||||||
|
where: whereClause,
|
||||||
|
whereArgs: whereArgs,
|
||||||
|
);
|
||||||
|
|
||||||
|
final exists = result.isNotEmpty;
|
||||||
|
log('Printer code "$code" exists: $exists');
|
||||||
|
return exists;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error checking printer code existence: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get printer statistics
|
||||||
|
Future<Map<String, dynamic>> getPrinterStats() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Total count
|
||||||
|
final totalResult =
|
||||||
|
await db.rawQuery('SELECT COUNT(*) as total FROM printers');
|
||||||
|
final totalCount = totalResult.first['total'] as int;
|
||||||
|
|
||||||
|
// Count by type
|
||||||
|
final typeResult = await db.rawQuery('''
|
||||||
|
SELECT type, COUNT(*) as count
|
||||||
|
FROM printers
|
||||||
|
WHERE type IS NOT NULL
|
||||||
|
GROUP BY type
|
||||||
|
''');
|
||||||
|
|
||||||
|
final typeStats = <String, int>{};
|
||||||
|
for (final row in typeResult) {
|
||||||
|
typeStats[row['type'] as String] = row['count'] as int;
|
||||||
|
}
|
||||||
|
|
||||||
|
final stats = {
|
||||||
|
'total_printers': totalCount,
|
||||||
|
'by_type': typeStats,
|
||||||
|
};
|
||||||
|
|
||||||
|
log('Printer stats: $stats');
|
||||||
|
return stats;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error getting printer stats: $e');
|
||||||
|
return {
|
||||||
|
'total_printers': 0,
|
||||||
|
'by_type': <String, int>{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all printers (for testing/reset purposes)
|
||||||
|
Future<void> clearAllPrinters() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Clearing all printers');
|
||||||
|
await db.delete('printers');
|
||||||
|
log('All printers cleared');
|
||||||
|
} catch (e) {
|
||||||
|
log('Error clearing printers: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch insert printers
|
||||||
|
Future<void> insertPrinters(List<PrintModel> printers) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('Batch inserting ${printers.length} printers');
|
||||||
|
|
||||||
|
await db.transaction((txn) async {
|
||||||
|
for (final printer in printers) {
|
||||||
|
await txn.insert(
|
||||||
|
'printers',
|
||||||
|
printer.toMapForInsert(),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log('Successfully batch inserted ${printers.length} printers');
|
||||||
|
} catch (e) {
|
||||||
|
log('Error batch inserting printers: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:enaklo_pos/core/database/database_handler.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
|
class ProductLocalDatasource {
|
||||||
|
static ProductLocalDatasource? _instance;
|
||||||
|
|
||||||
|
ProductLocalDatasource._internal();
|
||||||
|
|
||||||
|
static ProductLocalDatasource get instance {
|
||||||
|
_instance ??= ProductLocalDatasource._internal();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Database> get _db async => await DatabaseHelper.instance.database;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CACHING SYSTEM
|
||||||
|
// ========================================
|
||||||
|
final Map<String, List<Product>> _queryCache = {};
|
||||||
|
final Duration _cacheExpiry = Duration(minutes: 5);
|
||||||
|
final Map<String, DateTime> _cacheTimestamps = {};
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// ENHANCED BATCH SAVE
|
||||||
|
// ========================================
|
||||||
|
Future<void> saveProductsBatch(List<Product> products,
|
||||||
|
{bool clearFirst = false}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.transaction((txn) async {
|
||||||
|
if (clearFirst) {
|
||||||
|
log('🗑️ Clearing existing products...');
|
||||||
|
await txn.delete('product_variants');
|
||||||
|
await txn.delete('products');
|
||||||
|
}
|
||||||
|
|
||||||
|
log('💾 Batch saving ${products.length} products...');
|
||||||
|
|
||||||
|
// ✅ BATCH INSERT PRODUCTS - Much faster than individual inserts
|
||||||
|
final batch = txn.batch();
|
||||||
|
for (final product in products) {
|
||||||
|
batch.insert(
|
||||||
|
'products',
|
||||||
|
_productToMap(product),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await batch.commit(noResult: true);
|
||||||
|
|
||||||
|
// ✅ BATCH INSERT VARIANTS
|
||||||
|
final variantBatch = txn.batch();
|
||||||
|
for (final product in products) {
|
||||||
|
if (product.variants?.isNotEmpty == true) {
|
||||||
|
// Delete existing variants in batch
|
||||||
|
variantBatch.delete(
|
||||||
|
'product_variants',
|
||||||
|
where: 'product_id = ?',
|
||||||
|
whereArgs: [product.id],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert new variants
|
||||||
|
for (final variant in product.variants!) {
|
||||||
|
variantBatch.insert(
|
||||||
|
'product_variants',
|
||||||
|
_variantToMap(variant),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await variantBatch.commit(noResult: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear cache after update
|
||||||
|
clearCache();
|
||||||
|
log('✅ Successfully batch saved ${products.length} products');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error batch saving products: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CACHED QUERY - HIGH PERFORMANCE
|
||||||
|
// ========================================
|
||||||
|
Future<List<Product>> getCachedProducts({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
String? categoryId,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
final cacheKey = _generateCacheKey(page, limit, categoryId, search);
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
// ✅ CHECK CACHE FIRST
|
||||||
|
if (_queryCache.containsKey(cacheKey) &&
|
||||||
|
_cacheTimestamps.containsKey(cacheKey)) {
|
||||||
|
final cacheTime = _cacheTimestamps[cacheKey]!;
|
||||||
|
if (now.difference(cacheTime) < _cacheExpiry) {
|
||||||
|
log('🚀 Cache HIT: $cacheKey (${_queryCache[cacheKey]!.length} products)');
|
||||||
|
return _queryCache[cacheKey]!; // Return from cache - SUPER FAST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log('📀 Cache MISS: $cacheKey, querying database...');
|
||||||
|
|
||||||
|
// Cache miss, query database
|
||||||
|
final products = await getProducts(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
categoryId: categoryId,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
|
||||||
|
// ✅ STORE IN CACHE for next time
|
||||||
|
_queryCache[cacheKey] = products;
|
||||||
|
_cacheTimestamps[cacheKey] = now;
|
||||||
|
|
||||||
|
log('💾 Cached ${products.length} products for key: $cacheKey');
|
||||||
|
return products;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// REGULAR GET PRODUCTS (No Cache)
|
||||||
|
// ========================================
|
||||||
|
Future<List<Product>> getProducts({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
String? categoryId,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String query = 'SELECT * FROM products WHERE 1=1';
|
||||||
|
List<dynamic> whereArgs = [];
|
||||||
|
|
||||||
|
if (categoryId != null && categoryId.isNotEmpty) {
|
||||||
|
query += ' AND category_id = ?';
|
||||||
|
whereArgs.add(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search != null && search.isNotEmpty) {
|
||||||
|
query += ' AND (name LIKE ? OR sku LIKE ? OR description LIKE ?)';
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
}
|
||||||
|
|
||||||
|
query += ' ORDER BY created_at DESC';
|
||||||
|
|
||||||
|
if (limit > 0) {
|
||||||
|
query += ' LIMIT ?';
|
||||||
|
whereArgs.add(limit);
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
query += ' OFFSET ?';
|
||||||
|
whereArgs.add((page - 1) * limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Map<String, dynamic>> maps =
|
||||||
|
await db.rawQuery(query, whereArgs);
|
||||||
|
|
||||||
|
List<Product> products = [];
|
||||||
|
for (final map in maps) {
|
||||||
|
final variants = await _getProductVariants(db, map['id']);
|
||||||
|
final product = _mapToProduct(map, variants);
|
||||||
|
products.add(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
log('📊 Retrieved ${products.length} products from database');
|
||||||
|
return products;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting products: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// OPTIMIZED SEARCH with RANKING
|
||||||
|
// ========================================
|
||||||
|
Future<List<Product>> searchProductsOptimized(String query) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log('🔍 Optimized search for: "$query"');
|
||||||
|
|
||||||
|
// ✅ Smart query with prioritization
|
||||||
|
final List<Map<String, dynamic>> maps = await db.rawQuery('''
|
||||||
|
SELECT * FROM products
|
||||||
|
WHERE name LIKE ? OR sku LIKE ? OR description LIKE ?
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN name LIKE ? THEN 1 -- Highest priority: name match
|
||||||
|
WHEN sku LIKE ? THEN 2 -- Second priority: SKU match
|
||||||
|
ELSE 3 -- Lowest priority: description
|
||||||
|
END,
|
||||||
|
name ASC
|
||||||
|
LIMIT 50
|
||||||
|
''', [
|
||||||
|
'%$query%', '%$query%', '%$query%',
|
||||||
|
'$query%', '$query%' // Prioritize results that start with query
|
||||||
|
]);
|
||||||
|
|
||||||
|
List<Product> products = [];
|
||||||
|
for (final map in maps) {
|
||||||
|
final variants = await _getProductVariants(db, map['id']);
|
||||||
|
products.add(_mapToProduct(map, variants));
|
||||||
|
}
|
||||||
|
|
||||||
|
log('🎯 Optimized search found ${products.length} results');
|
||||||
|
return products;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error in optimized search: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// DATABASE ANALYTICS & MONITORING
|
||||||
|
// ========================================
|
||||||
|
Future<Map<String, dynamic>> getDatabaseStats() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final productCount = Sqflite.firstIntValue(
|
||||||
|
await db.rawQuery('SELECT COUNT(*) FROM products')) ??
|
||||||
|
0;
|
||||||
|
|
||||||
|
final variantCount = Sqflite.firstIntValue(
|
||||||
|
await db.rawQuery('SELECT COUNT(*) FROM product_variants')) ??
|
||||||
|
0;
|
||||||
|
|
||||||
|
final categoryCount = Sqflite.firstIntValue(await db.rawQuery(
|
||||||
|
'SELECT COUNT(DISTINCT category_id) FROM products WHERE category_id IS NOT NULL')) ??
|
||||||
|
0;
|
||||||
|
|
||||||
|
final dbSize = await _getDatabaseSize();
|
||||||
|
|
||||||
|
final stats = {
|
||||||
|
'total_products': productCount,
|
||||||
|
'total_variants': variantCount,
|
||||||
|
'total_categories': categoryCount,
|
||||||
|
'database_size_mb': dbSize,
|
||||||
|
'cache_entries': _queryCache.length,
|
||||||
|
'cache_size_mb': _getCacheSize(),
|
||||||
|
};
|
||||||
|
|
||||||
|
log('📊 Database Stats: $stats');
|
||||||
|
return stats;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting database stats: $e');
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<double> _getDatabaseSize() async {
|
||||||
|
try {
|
||||||
|
final dbPath = p.join(await getDatabasesPath(), 'pos_database.db');
|
||||||
|
final file = File(dbPath);
|
||||||
|
if (await file.exists()) {
|
||||||
|
final size = await file.length();
|
||||||
|
return size / (1024 * 1024); // Convert to MB
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('Error getting database size: $e');
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getCacheSize() {
|
||||||
|
double totalSize = 0;
|
||||||
|
_queryCache.forEach((key, products) {
|
||||||
|
totalSize += products.length * 0.001; // Rough estimate in MB
|
||||||
|
});
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CACHE MANAGEMENT
|
||||||
|
// ========================================
|
||||||
|
String _generateCacheKey(
|
||||||
|
int page, int limit, String? categoryId, String? search) {
|
||||||
|
return 'products_${page}_${limit}_${categoryId ?? 'null'}_${search ?? 'null'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearCache() {
|
||||||
|
final count = _queryCache.length;
|
||||||
|
_queryCache.clear();
|
||||||
|
_cacheTimestamps.clear();
|
||||||
|
log('🧹 Cache cleared: $count entries removed');
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearExpiredCache() {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final expiredKeys = <String>[];
|
||||||
|
|
||||||
|
_cacheTimestamps.forEach((key, timestamp) {
|
||||||
|
if (now.difference(timestamp) > _cacheExpiry) {
|
||||||
|
expiredKeys.add(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final key in expiredKeys) {
|
||||||
|
_queryCache.remove(key);
|
||||||
|
_cacheTimestamps.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expiredKeys.isNotEmpty) {
|
||||||
|
log('⏰ Expired cache cleared: ${expiredKeys.length} entries');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// OTHER METHODS (Same as basic but with enhanced logging)
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
Future<Product?> getProductById(String id) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final List<Map<String, dynamic>> maps = await db.query(
|
||||||
|
'products',
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (maps.isEmpty) {
|
||||||
|
log('❌ Product not found: $id');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final variants = await _getProductVariants(db, id);
|
||||||
|
final product = _mapToProduct(maps.first, variants);
|
||||||
|
log('✅ Product found: ${product.name}');
|
||||||
|
return product;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting product by ID: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> getTotalCount({String? categoryId, String? search}) async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String query = 'SELECT COUNT(*) FROM products WHERE 1=1';
|
||||||
|
List<dynamic> whereArgs = [];
|
||||||
|
|
||||||
|
if (categoryId != null && categoryId.isNotEmpty) {
|
||||||
|
query += ' AND category_id = ?';
|
||||||
|
whereArgs.add(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search != null && search.isNotEmpty) {
|
||||||
|
query += ' AND (name LIKE ? OR sku LIKE ? OR description LIKE ?)';
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
whereArgs.add('%$search%');
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await db.rawQuery(query, whereArgs);
|
||||||
|
final count = Sqflite.firstIntValue(result) ?? 0;
|
||||||
|
log('📊 Total count: $count (categoryId: $categoryId, search: $search)');
|
||||||
|
return count;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting total count: $e');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> hasProducts() async {
|
||||||
|
final count = await getTotalCount();
|
||||||
|
final hasData = count > 0;
|
||||||
|
log('🔍 Has products: $hasData ($count products)');
|
||||||
|
return hasData;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearAllProducts() async {
|
||||||
|
final db = await _db;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.transaction((txn) async {
|
||||||
|
await txn.delete('product_variants');
|
||||||
|
await txn.delete('products');
|
||||||
|
});
|
||||||
|
clearCache();
|
||||||
|
log('🗑️ All products cleared from local DB');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error clearing products: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// HELPER METHODS
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
Future<List<ProductVariant>> _getProductVariants(
|
||||||
|
Database db, String productId) async {
|
||||||
|
try {
|
||||||
|
final List<Map<String, dynamic>> maps = await db.query(
|
||||||
|
'product_variants',
|
||||||
|
where: 'product_id = ?',
|
||||||
|
whereArgs: [productId],
|
||||||
|
orderBy: 'name ASC',
|
||||||
|
);
|
||||||
|
|
||||||
|
return maps.map((map) => _mapToVariant(map)).toList();
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting variants for product $productId: $e');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _productToMap(Product product) {
|
||||||
|
return {
|
||||||
|
'id': product.id,
|
||||||
|
'organization_id': product.organizationId,
|
||||||
|
'category_id': product.categoryId,
|
||||||
|
'sku': product.sku,
|
||||||
|
'name': product.name,
|
||||||
|
'description': product.description,
|
||||||
|
'price': product.price,
|
||||||
|
'cost': product.cost,
|
||||||
|
'business_type': product.businessType,
|
||||||
|
'image_url': product.imageUrl,
|
||||||
|
'printer_type': product.printerType,
|
||||||
|
'metadata':
|
||||||
|
product.metadata != null ? json.encode(product.metadata) : null,
|
||||||
|
'is_active': product.isActive == true ? 1 : 0,
|
||||||
|
'created_at': product.createdAt?.toIso8601String(),
|
||||||
|
'updated_at': product.updatedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _variantToMap(ProductVariant variant) {
|
||||||
|
return {
|
||||||
|
'id': variant.id,
|
||||||
|
'product_id': variant.productId,
|
||||||
|
'name': variant.name,
|
||||||
|
'price_modifier': variant.priceModifier,
|
||||||
|
'cost': variant.cost,
|
||||||
|
'metadata':
|
||||||
|
variant.metadata != null ? json.encode(variant.metadata) : null,
|
||||||
|
'created_at': variant.createdAt?.toIso8601String(),
|
||||||
|
'updated_at': variant.updatedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Product _mapToProduct(
|
||||||
|
Map<String, dynamic> map, List<ProductVariant> variants) {
|
||||||
|
return Product(
|
||||||
|
id: map['id'],
|
||||||
|
organizationId: map['organization_id'],
|
||||||
|
categoryId: map['category_id'],
|
||||||
|
sku: map['sku'],
|
||||||
|
name: map['name'],
|
||||||
|
description: map['description'],
|
||||||
|
price: map['price'],
|
||||||
|
cost: map['cost'],
|
||||||
|
businessType: map['business_type'],
|
||||||
|
imageUrl: map['image_url'],
|
||||||
|
printerType: map['printer_type'],
|
||||||
|
metadata: map['metadata'] != null ? json.decode(map['metadata']) : null,
|
||||||
|
isActive: map['is_active'] == 1,
|
||||||
|
createdAt:
|
||||||
|
map['created_at'] != null ? DateTime.parse(map['created_at']) : null,
|
||||||
|
updatedAt:
|
||||||
|
map['updated_at'] != null ? DateTime.parse(map['updated_at']) : null,
|
||||||
|
variants: variants,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductVariant _mapToVariant(Map<String, dynamic> map) {
|
||||||
|
return ProductVariant(
|
||||||
|
id: map['id'],
|
||||||
|
productId: map['product_id'],
|
||||||
|
name: map['name'],
|
||||||
|
priceModifier: map['price_modifier'],
|
||||||
|
cost: map['cost'],
|
||||||
|
metadata: map['metadata'] != null ? json.decode(map['metadata']) : null,
|
||||||
|
createdAt:
|
||||||
|
map['created_at'] != null ? DateTime.parse(map['created_at']) : null,
|
||||||
|
updatedAt:
|
||||||
|
map['updated_at'] != null ? DateTime.parse(map['updated_at']) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,639 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:enaklo_pos/data/models/response/print_model.dart';
|
|
||||||
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
|
||||||
import 'package:enaklo_pos/data/models/response/table_model.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/models/order_model.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
|
||||||
|
|
||||||
import '../../presentation/home/models/product_quantity.dart';
|
|
||||||
|
|
||||||
class ProductLocalDatasource {
|
|
||||||
ProductLocalDatasource._init();
|
|
||||||
|
|
||||||
static final ProductLocalDatasource instance = ProductLocalDatasource._init();
|
|
||||||
|
|
||||||
final String tableProduct = 'products';
|
|
||||||
final String tableOrder = 'orders';
|
|
||||||
final String tableOrderItem = 'order_items';
|
|
||||||
final String tableManagement = 'table_management';
|
|
||||||
final String tablePrint = 'prints';
|
|
||||||
static Database? _database;
|
|
||||||
|
|
||||||
// "id": 1,
|
|
||||||
// "category_id": 1,
|
|
||||||
// "name": "Mie Ayam",
|
|
||||||
// "description": "Ipsa dolorem impedit dolor. Libero nisi quidem expedita quod mollitia ad. Voluptas ut quia nemo nisi odit fuga. Fugit autem qui ratione laborum eum.",
|
|
||||||
// "image": "https://via.placeholder.com/640x480.png/002200?text=nihil",
|
|
||||||
// "price": "2000.44",
|
|
||||||
// "stock": 94,
|
|
||||||
// "status": 1,
|
|
||||||
// "is_favorite": 1,
|
|
||||||
// "created_at": "2024-02-08T14:30:22.000000Z",
|
|
||||||
// "updated_at": "2024-02-08T15:14:22.000000Z"
|
|
||||||
|
|
||||||
Future<void> _createDb(Database db, int version) async {
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE $tableProduct (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
product_id INTEGER,
|
|
||||||
name TEXT,
|
|
||||||
printer_type TEXT,
|
|
||||||
categoryId INTEGER,
|
|
||||||
categoryName TEXT,
|
|
||||||
description TEXT,
|
|
||||||
image TEXT,
|
|
||||||
price TEXT,
|
|
||||||
stock INTEGER,
|
|
||||||
status INTEGER,
|
|
||||||
isFavorite INTEGER,
|
|
||||||
createdAt TEXT,
|
|
||||||
updatedAt TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE $tableOrder (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
payment_amount INTEGER,
|
|
||||||
sub_total INTEGER,
|
|
||||||
tax INTEGER,
|
|
||||||
discount INTEGER,
|
|
||||||
discount_amount INTEGER,
|
|
||||||
service_charge INTEGER,
|
|
||||||
total INTEGER,
|
|
||||||
payment_method TEXT,
|
|
||||||
total_item INTEGER,
|
|
||||||
id_kasir INTEGER,
|
|
||||||
nama_kasir TEXT,
|
|
||||||
transaction_time TEXT,
|
|
||||||
table_number INTEGER,
|
|
||||||
customer_name TEXT,
|
|
||||||
status TEXT,
|
|
||||||
payment_status TEXT,
|
|
||||||
order_type TEXT DEFAULT 'DINE IN',
|
|
||||||
is_sync INTEGER DEFAULT 0
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE $tableOrderItem (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
id_order INTEGER,
|
|
||||||
id_product INTEGER,
|
|
||||||
quantity INTEGER,
|
|
||||||
price INTEGER,
|
|
||||||
notes TEXT DEFAULT ''
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE $tableManagement (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
table_name Text,
|
|
||||||
start_time Text,
|
|
||||||
order_id INTEGER,
|
|
||||||
payment_amount INTEGER,
|
|
||||||
x_position REAL NOT NULL,
|
|
||||||
y_position REAL NOT NULL,
|
|
||||||
status TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE draft_orders (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
total_item INTEGER,
|
|
||||||
subtotal INTEGER,
|
|
||||||
tax INTEGER,
|
|
||||||
discount INTEGER,
|
|
||||||
discount_amount INTEGER,
|
|
||||||
service_charge INTEGER,
|
|
||||||
total INTEGER,
|
|
||||||
transaction_time TEXT,
|
|
||||||
table_number INTEGER,
|
|
||||||
draft_name TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE draft_order_items (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
id_draft_order INTEGER,
|
|
||||||
id_product INTEGER,
|
|
||||||
quantity INTEGER,
|
|
||||||
price INTEGER
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE $tablePrint (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT,
|
|
||||||
name TEXT,
|
|
||||||
address TEXT,
|
|
||||||
paper TEXT,
|
|
||||||
type TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Database> _initDB(String filePath) async {
|
|
||||||
final dbPath = await getDatabasesPath();
|
|
||||||
final path = dbPath + filePath;
|
|
||||||
|
|
||||||
// Force delete existing database to ensure new schema
|
|
||||||
try {
|
|
||||||
final dbExists = await databaseExists(path);
|
|
||||||
if (dbExists) {
|
|
||||||
log("Deleting existing database to ensure new schema with order_type column");
|
|
||||||
// await deleteDatabase(path);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log("Error deleting database: $e");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await openDatabase(
|
|
||||||
path,
|
|
||||||
version: 2,
|
|
||||||
onCreate: _createDb,
|
|
||||||
onUpgrade: _onUpgrade,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
|
||||||
if (oldVersion < 2) {
|
|
||||||
// Add order_type column to orders table if it doesn't exist
|
|
||||||
try {
|
|
||||||
await db.execute(
|
|
||||||
'ALTER TABLE $tableOrder ADD COLUMN order_type TEXT DEFAULT "DINE IN"');
|
|
||||||
log("Added order_type column to orders table");
|
|
||||||
} catch (e) {
|
|
||||||
log("order_type column might already exist: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Database> get database async {
|
|
||||||
if (_database != null) return _database!;
|
|
||||||
_database = await _initDB('dbresto36.db');
|
|
||||||
return _database!;
|
|
||||||
}
|
|
||||||
|
|
||||||
//save order
|
|
||||||
Future<int> saveOrder(OrderModel order) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
|
|
||||||
// Since we're forcing database recreation, order_type column should exist
|
|
||||||
final orderMap = order.toMap(includeOrderType: true);
|
|
||||||
log("Final orderMap for insertion: $orderMap");
|
|
||||||
|
|
||||||
int id = await db.insert(tableOrder, orderMap,
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
||||||
|
|
||||||
for (var item in order.orderItems) {
|
|
||||||
log("Item: ${item.toLocalMap(id)}");
|
|
||||||
await db.insert(tableOrderItem, item.toLocalMap(id),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
||||||
}
|
|
||||||
log("Success Order: ${order.toMap()}");
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get data order
|
|
||||||
Future<List<OrderModel>> getOrderByIsNotSync() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final List<Map<String, dynamic>> maps =
|
|
||||||
await db.query(tableOrder, where: 'is_sync = ?', whereArgs: [0]);
|
|
||||||
return List.generate(maps.length, (i) {
|
|
||||||
return OrderModel.fromMap(maps[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<OrderModel>> getAllOrder(
|
|
||||||
DateTime date,
|
|
||||||
) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
//date to iso8601
|
|
||||||
final dateIso = date.toIso8601String();
|
|
||||||
//get yyyy-MM-dd
|
|
||||||
final dateYYYYMMDD = dateIso.substring(0, 10);
|
|
||||||
// final formattedDate = DateFormat('yyyy-MM-dd').format(date);
|
|
||||||
final List<Map<String, dynamic>> maps = await db.query(
|
|
||||||
tableOrder,
|
|
||||||
where: 'transaction_time like ?',
|
|
||||||
whereArgs: ['$dateYYYYMMDD%'],
|
|
||||||
// where: 'transaction_time BETWEEN ? AND ?',
|
|
||||||
// whereArgs: [
|
|
||||||
// DateFormat.yMd().format(start),
|
|
||||||
// DateFormat.yMd().format(end)
|
|
||||||
// ],
|
|
||||||
);
|
|
||||||
return List.generate(maps.length, (i) {
|
|
||||||
log("Save save OrderModel: ${OrderModel.fromMap(maps[i])}");
|
|
||||||
return OrderModel.fromMap(maps[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<OrderModel>> getAllOrderByRange(
|
|
||||||
DateTime start, DateTime end) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
|
|
||||||
// Format ke ISO 8601 untuk range, hasil: yyyy-MM-ddTHH:mm:ss
|
|
||||||
final startIso = start.toIso8601String();
|
|
||||||
final endIso = end.toIso8601String();
|
|
||||||
|
|
||||||
final startDateYYYYMMDD = startIso.substring(0, 10);
|
|
||||||
final endDateYYYYMMDD = endIso.substring(0, 10);
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> maps = await db.query(
|
|
||||||
tableOrder,
|
|
||||||
where: 'substr(transaction_time, 1, 10) BETWEEN ? AND ?',
|
|
||||||
whereArgs: [startDateYYYYMMDD, endDateYYYYMMDD],
|
|
||||||
orderBy: 'transaction_time DESC',
|
|
||||||
);
|
|
||||||
log("Get All Order By Range: $startDateYYYYMMDD $endDateYYYYMMDD");
|
|
||||||
|
|
||||||
return List.generate(maps.length, (i) {
|
|
||||||
log("Save save OrderModel: ${OrderModel.fromMap(maps[i])}");
|
|
||||||
return OrderModel.fromMap(maps[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//get order item by order id
|
|
||||||
Future<List<ProductQuantity>> getOrderItemByOrderId(int orderId) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final List<Map<String, dynamic>> maps = await db
|
|
||||||
.query(tableOrderItem, where: 'id_order = ?', whereArgs: [orderId]);
|
|
||||||
return List.generate(maps.length, (i) {
|
|
||||||
log("ProductQuantity: ${ProductQuantity.fromLocalMap(maps[i])}");
|
|
||||||
return ProductQuantity.fromLocalMap(maps[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//update payment status by order id
|
|
||||||
Future<void> updatePaymentStatus(
|
|
||||||
int orderId, String paymentStatus, String status) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(
|
|
||||||
tableOrder, {'payment_status': paymentStatus, 'status': status},
|
|
||||||
where: 'id = ?', whereArgs: [orderId]);
|
|
||||||
log('update payment status success | order id: $orderId | payment status: $paymentStatus | status: $status');
|
|
||||||
}
|
|
||||||
|
|
||||||
//update order is sync
|
|
||||||
Future<void> updateOrderIsSync(int orderId) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(tableOrder, {'is_sync': 1},
|
|
||||||
where: 'id = ?', whereArgs: [orderId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//insert data product
|
|
||||||
|
|
||||||
Future<void> insertProduct(Product product) async {
|
|
||||||
log("Product: ${product.toMap()}");
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.insert(tableProduct, product.toMap(),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
//update product
|
|
||||||
Future<void> updateProduct(Product product) async {
|
|
||||||
log("Update Product: ${product.toMap()}");
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(
|
|
||||||
tableProduct,
|
|
||||||
product.toLocalMap(),
|
|
||||||
where: 'product_id = ?',
|
|
||||||
whereArgs: [product.id],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//insert list of product
|
|
||||||
Future<void> insertProducts(List<Product> products) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
log("Save Products to Local");
|
|
||||||
for (var product in products) {
|
|
||||||
await db.insert(tableProduct, product.toLocalMap(),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
||||||
log('inserted success id: ${product.id} | name: ${product.name} | price: ${product.price} ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//get all products
|
|
||||||
Future<List<Product>> getProducts() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final List<Map<String, dynamic>> maps = await db.query(tableProduct);
|
|
||||||
return List.generate(maps.length, (i) {
|
|
||||||
return Product.fromLocalMap(maps[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Product?> getProductById(int id) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final result =
|
|
||||||
await db.query(tableProduct, where: 'product_id = ?', whereArgs: [id]);
|
|
||||||
|
|
||||||
if (result.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Product.fromMap(result.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get Last Table Management
|
|
||||||
|
|
||||||
Future<TableModel?> getLastTableManagement() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final List<Map<String, dynamic>> maps =
|
|
||||||
await db.query(tableManagement, orderBy: 'id DESC', limit: 1);
|
|
||||||
if (maps.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return TableModel.fromMap(maps[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate table managent with count
|
|
||||||
Future<void> createTableManagement(String tableName, Offset position) async {
|
|
||||||
// final db = await instance.database;
|
|
||||||
// TableModel newTable = TableModel(
|
|
||||||
// tableName: tableName,
|
|
||||||
// status: 'available',
|
|
||||||
// orderId: 0,
|
|
||||||
// paymentAmount: 0,
|
|
||||||
// startTime: DateTime.now().toIso8601String(),
|
|
||||||
// position: position,
|
|
||||||
// );
|
|
||||||
// await db.insert(
|
|
||||||
// tableManagement,
|
|
||||||
// newTable.toMap(),
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
|
|
||||||
// change position table
|
|
||||||
Future<void> changePositionTable(int id, Offset position) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(
|
|
||||||
tableManagement,
|
|
||||||
{'x_position': position.dx, 'y_position': position.dy},
|
|
||||||
where: 'id = ?',
|
|
||||||
whereArgs: [id],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update table
|
|
||||||
Future<void> updateTable(TableModel table) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(
|
|
||||||
tableManagement,
|
|
||||||
table.toMap(),
|
|
||||||
where: 'id = ?',
|
|
||||||
whereArgs: [table.id],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all table
|
|
||||||
Future<List<TableModel>> getAllTable() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final List<Map<String, dynamic>> maps = await db.query(tableManagement);
|
|
||||||
log("Table Management: $maps");
|
|
||||||
return List.generate(maps.length, (i) {
|
|
||||||
return TableModel.fromMap(maps[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// get last order where table number
|
|
||||||
Future<OrderModel?> getLastOrderTable(int tableNumber) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final List<Map<String, dynamic>> maps = await db.query(
|
|
||||||
tableOrder,
|
|
||||||
where: 'table_number = ?',
|
|
||||||
whereArgs: [tableNumber],
|
|
||||||
orderBy: 'id DESC', // Urutkan berdasarkan id dari yang terbesar (terbaru)
|
|
||||||
limit: 1, // Ambil hanya satu data terakhir
|
|
||||||
);
|
|
||||||
|
|
||||||
if (maps.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OrderModel.fromMap(maps[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get table by status
|
|
||||||
Future<List<TableModel>> getTableByStatus(String status) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
List<Map<String, dynamic>> maps;
|
|
||||||
|
|
||||||
if (status == 'all') {
|
|
||||||
// Get all tables
|
|
||||||
maps = await db.query(tableManagement);
|
|
||||||
log("Getting all tables, found: ${maps.length}");
|
|
||||||
|
|
||||||
// If no tables exist, create some default tables
|
|
||||||
if (maps.isEmpty) {
|
|
||||||
log("No tables found, creating default tables...");
|
|
||||||
await _createDefaultTables();
|
|
||||||
maps = await db.query(tableManagement);
|
|
||||||
log("After creating default tables, found: ${maps.length}");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Get tables by specific status
|
|
||||||
maps = await db.query(
|
|
||||||
tableManagement,
|
|
||||||
where: 'status = ?',
|
|
||||||
whereArgs: [status],
|
|
||||||
);
|
|
||||||
log("Getting tables with status '$status', found: ${maps.length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
final tables = List.generate(maps.length, (i) {
|
|
||||||
return TableModel.fromMap(maps[i]);
|
|
||||||
});
|
|
||||||
|
|
||||||
log("Returning ${tables.length} tables");
|
|
||||||
tables.forEach((table) {
|
|
||||||
log("Table: ${table.tableName} (ID: ${table.id}, Status: ${table.status})");
|
|
||||||
});
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create default tables if none exist
|
|
||||||
Future<void> _createDefaultTables() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
|
|
||||||
// Create 5 default tables
|
|
||||||
for (int i = 1; i <= 5; i++) {
|
|
||||||
await db.insert(tableManagement, {
|
|
||||||
'table_name': 'Table $i',
|
|
||||||
'start_time': DateTime.now().toIso8601String(),
|
|
||||||
'order_id': 0,
|
|
||||||
'payment_amount': 0,
|
|
||||||
'x_position': 100.0 + (i * 50.0),
|
|
||||||
'y_position': 100.0 + (i * 50.0),
|
|
||||||
'status': 'available',
|
|
||||||
});
|
|
||||||
log("Created default table: Table $i");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update status tabel
|
|
||||||
Future<void> updateStatusTable(TableModel table) async {
|
|
||||||
log("Updating table status: ${table.toMap()}");
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(tableManagement, table.toMap(),
|
|
||||||
where: 'id = ?', whereArgs: [table.id]);
|
|
||||||
log("Success Update Status Table: ${table.toMap()}");
|
|
||||||
|
|
||||||
// Verify the update
|
|
||||||
final updatedTable = await db.query(
|
|
||||||
tableManagement,
|
|
||||||
where: 'id = ?',
|
|
||||||
whereArgs: [table.id],
|
|
||||||
);
|
|
||||||
if (updatedTable.isNotEmpty) {
|
|
||||||
log("Verified table update: ${updatedTable.first}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug method to reset all tables to available status
|
|
||||||
Future<void> resetAllTablesToAvailable() async {
|
|
||||||
log("Resetting all tables to available status...");
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.update(
|
|
||||||
tableManagement,
|
|
||||||
{
|
|
||||||
'status': 'available',
|
|
||||||
'order_id': 0,
|
|
||||||
'payment_amount': 0,
|
|
||||||
'start_time': DateTime.now().toIso8601String(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
log("All tables reset to available status");
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete all products
|
|
||||||
Future<void> deleteAllProducts() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.delete(tableProduct);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<int> saveDraftOrder(DraftOrderModel order) async {
|
|
||||||
log("save draft order: ${order.toMapForLocal()}");
|
|
||||||
final db = await instance.database;
|
|
||||||
int id = await db.insert('draft_orders', order.toMapForLocal());
|
|
||||||
log("draft order id: $id | ${order.discountAmount}");
|
|
||||||
for (var orderItem in order.orders) {
|
|
||||||
await db.insert('draft_order_items', orderItem.toMapForLocal(id));
|
|
||||||
log("draft order item ${orderItem.toMapForLocal(id)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get all draft order
|
|
||||||
Future<List<DraftOrderModel>> getAllDraftOrder() async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final result = await db.query('draft_orders', orderBy: 'id ASC');
|
|
||||||
|
|
||||||
List<DraftOrderModel> results = await Future.wait(result.map((item) async {
|
|
||||||
// Your asynchronous operation here
|
|
||||||
final draftOrderItem =
|
|
||||||
await getDraftOrderItemByOrderId(item['id'] as int);
|
|
||||||
return DraftOrderModel.newFromLocalMap(item, draftOrderItem);
|
|
||||||
}));
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get Darft Order by id
|
|
||||||
Future<DraftOrderModel?> getDraftOrderById(int id) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final result =
|
|
||||||
await db.query('draft_orders', where: 'id = ?', whereArgs: [id]);
|
|
||||||
if (result.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final draftOrderItem =
|
|
||||||
await getDraftOrderItemByOrderId(result.first['id'] as int);
|
|
||||||
log("draft order item: $draftOrderItem | ${result.first.toString()}");
|
|
||||||
return DraftOrderModel.newFromLocalMap(result.first, draftOrderItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
//get draft order item by id order
|
|
||||||
Future<List<DraftOrderItem>> getDraftOrderItemByOrderId(int idOrder) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final result =
|
|
||||||
await db.query('draft_order_items', where: 'id_draft_order = $idOrder');
|
|
||||||
|
|
||||||
List<DraftOrderItem> results = await Future.wait(result.map((item) async {
|
|
||||||
// Your asynchronous operation here
|
|
||||||
final product = await getProductById(item['id_product'] as int);
|
|
||||||
return DraftOrderItem(
|
|
||||||
product: product!, quantity: item['quantity'] as int);
|
|
||||||
}));
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove draft order by id
|
|
||||||
Future<void> removeDraftOrderById(int id) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.delete('draft_orders', where: 'id = ?', whereArgs: [id]);
|
|
||||||
await db.delete('draft_order_items',
|
|
||||||
where: 'id_draft_order = ?', whereArgs: [id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//update draft order
|
|
||||||
Future<void> updateDraftOrder(DraftOrderModel draftOrder) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
|
|
||||||
// Update the draft order
|
|
||||||
await db.update(
|
|
||||||
'draft_orders',
|
|
||||||
draftOrder.toMapForLocal(),
|
|
||||||
where: 'id = ?',
|
|
||||||
whereArgs: [draftOrder.id],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove existing items and add new ones
|
|
||||||
await db.delete('draft_order_items',
|
|
||||||
where: 'id_draft_order = ?', whereArgs: [draftOrder.id]);
|
|
||||||
|
|
||||||
for (var orderItem in draftOrder.orders) {
|
|
||||||
await db.insert(
|
|
||||||
'draft_order_items', orderItem.toMapForLocal(draftOrder.id!));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// create printer
|
|
||||||
Future<void> createPrinter(PrintModel print) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.insert(tablePrint, print.toMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updatePrinter(PrintModel print, int id) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
log("Update Printer: ${print.toMap()} | id: $id");
|
|
||||||
await db
|
|
||||||
.update(tablePrint, print.toMap(), where: 'id = ?', whereArgs: [id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deletePrinter(int id) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
await db.delete(tablePrint, where: 'id = ?', whereArgs: [id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get printer by code
|
|
||||||
Future<PrintModel?> getPrinterByCode(String code) async {
|
|
||||||
final db = await instance.database;
|
|
||||||
final result =
|
|
||||||
await db.query(tablePrint, where: 'code = ?', whereArgs: [code]);
|
|
||||||
if (result.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return PrintModel.fromMap(result.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,7 @@ class ProductRemoteDatasource {
|
|||||||
log("Dio error: ${e.message}");
|
log("Dio error: ${e.message}");
|
||||||
return Left(e.response?.data['message'] ?? 'Gagal mengambil produk');
|
return Left(e.response?.data['message'] ?? 'Gagal mengambil produk');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("Unexpected error: $e");
|
log("Unexpected error: $e", name: "productRemoteDatasource");
|
||||||
return const Left('Unexpected error occurred');
|
return const Left('Unexpected error occurred');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class SettingsLocalDatasource {
|
|||||||
return TaxModel(
|
return TaxModel(
|
||||||
name: 'Tax',
|
name: 'Tax',
|
||||||
type: TaxType.pajak,
|
type: TaxType.pajak,
|
||||||
value: 11,
|
value: 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class TableRemoteDataSource {
|
|||||||
|
|
||||||
Future<Either<String, TableResponseModel>> getTable({
|
Future<Either<String, TableResponseModel>> getTable({
|
||||||
int page = 1,
|
int page = 1,
|
||||||
int limit = 10,
|
int limit = 50,
|
||||||
String? status,
|
String? status,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import 'dart:convert';
|
|||||||
|
|
||||||
class AuthResponseModel {
|
class AuthResponseModel {
|
||||||
final String? token;
|
final String? token;
|
||||||
|
final String? refreshToken;
|
||||||
final User? user;
|
final User? user;
|
||||||
|
|
||||||
AuthResponseModel({
|
AuthResponseModel({
|
||||||
|
this.refreshToken,
|
||||||
this.token,
|
this.token,
|
||||||
this.user,
|
this.user,
|
||||||
});
|
});
|
||||||
@@ -17,11 +19,13 @@ class AuthResponseModel {
|
|||||||
factory AuthResponseModel.fromMap(Map<String, dynamic> json) =>
|
factory AuthResponseModel.fromMap(Map<String, dynamic> json) =>
|
||||||
AuthResponseModel(
|
AuthResponseModel(
|
||||||
token: json["token"],
|
token: json["token"],
|
||||||
|
refreshToken: json["refresh_token"],
|
||||||
user: json["user"] == null ? null : User.fromMap(json["user"]),
|
user: json["user"] == null ? null : User.fromMap(json["user"]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
"token": token,
|
"token": token,
|
||||||
|
"refresh_token": refreshToken,
|
||||||
"user": user?.toMap(),
|
"user": user?.toMap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
class CategoryAnalyticResponseModel {
|
||||||
|
final bool success;
|
||||||
|
final CategoryAnalyticData? data;
|
||||||
|
final dynamic errors;
|
||||||
|
|
||||||
|
CategoryAnalyticResponseModel({
|
||||||
|
required this.success,
|
||||||
|
required this.data,
|
||||||
|
this.errors,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dari JSON String ke Model
|
||||||
|
factory CategoryAnalyticResponseModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return CategoryAnalyticResponseModel(
|
||||||
|
success: json['success'],
|
||||||
|
data: json['data'] == null
|
||||||
|
? null
|
||||||
|
: CategoryAnalyticData.fromMap(json['data']),
|
||||||
|
errors: json['errors'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dari Model ke JSON String
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'success': success,
|
||||||
|
'data': data?.toMap(),
|
||||||
|
'errors': errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dari Map ke Model
|
||||||
|
factory CategoryAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||||
|
return CategoryAnalyticResponseModel(
|
||||||
|
success: map['success'],
|
||||||
|
data: CategoryAnalyticData.fromMap(map['data']),
|
||||||
|
errors: map['errors'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dari Model ke Map
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'success': success,
|
||||||
|
'data': data?.toMap(),
|
||||||
|
'errors': errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategoryAnalyticData {
|
||||||
|
final String organizationId;
|
||||||
|
final String outletId;
|
||||||
|
final DateTime dateFrom;
|
||||||
|
final DateTime dateTo;
|
||||||
|
final List<CategoryAnalyticItem> data;
|
||||||
|
|
||||||
|
CategoryAnalyticData({
|
||||||
|
required this.organizationId,
|
||||||
|
required this.outletId,
|
||||||
|
required this.dateFrom,
|
||||||
|
required this.dateTo,
|
||||||
|
required this.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CategoryAnalyticData.fromMap(Map<String, dynamic> map) {
|
||||||
|
return CategoryAnalyticData(
|
||||||
|
organizationId: map['organization_id'],
|
||||||
|
outletId: map['outlet_id'],
|
||||||
|
dateFrom: DateTime.parse(map['date_from']),
|
||||||
|
dateTo: DateTime.parse(map['date_to']),
|
||||||
|
data: map['data'] == null
|
||||||
|
? []
|
||||||
|
: List<CategoryAnalyticItem>.from(
|
||||||
|
map['data']?.map((x) => CategoryAnalyticItem.fromMap(x)) ?? [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'organization_id': organizationId,
|
||||||
|
'outlet_id': outletId,
|
||||||
|
'date_from': dateFrom.toIso8601String(),
|
||||||
|
'date_to': dateTo.toIso8601String(),
|
||||||
|
'data': data.map((x) => x.toMap()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategoryAnalyticItem {
|
||||||
|
final String categoryId;
|
||||||
|
final String categoryName;
|
||||||
|
final int totalRevenue;
|
||||||
|
final int totalQuantity;
|
||||||
|
final int productCount;
|
||||||
|
final int orderCount;
|
||||||
|
|
||||||
|
CategoryAnalyticItem({
|
||||||
|
required this.categoryId,
|
||||||
|
required this.categoryName,
|
||||||
|
required this.totalRevenue,
|
||||||
|
required this.totalQuantity,
|
||||||
|
required this.productCount,
|
||||||
|
required this.orderCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CategoryAnalyticItem.fromMap(Map<String, dynamic> map) {
|
||||||
|
return CategoryAnalyticItem(
|
||||||
|
categoryId: map['category_id'],
|
||||||
|
categoryName: map['category_name'],
|
||||||
|
totalRevenue: map['total_revenue'] ?? 0,
|
||||||
|
totalQuantity: map['total_quantity'] ?? 0,
|
||||||
|
productCount: map['product_count'] ?? 0,
|
||||||
|
orderCount: map['order_count'] ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'category_id': categoryId,
|
||||||
|
'category_name': categoryName,
|
||||||
|
'total_revenue': totalRevenue,
|
||||||
|
'total_quantity': totalQuantity,
|
||||||
|
'product_count': productCount,
|
||||||
|
'order_count': orderCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,4 +117,14 @@ class CategoryModel {
|
|||||||
'updated_at': updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory CategoryModel.all() => CategoryModel(
|
||||||
|
id: 'all',
|
||||||
|
organizationId: '',
|
||||||
|
name: 'Semua',
|
||||||
|
businessType: 'restaurant',
|
||||||
|
metadata: {},
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,12 +59,18 @@ class DashboardAnalyticData {
|
|||||||
dateFrom: map['date_from'],
|
dateFrom: map['date_from'],
|
||||||
dateTo: map['date_to'],
|
dateTo: map['date_to'],
|
||||||
overview: DashboardOverview.fromMap(map['overview']),
|
overview: DashboardOverview.fromMap(map['overview']),
|
||||||
topProducts: List<TopProduct>.from(
|
topProducts: map['top_products'] == null
|
||||||
map['top_products']?.map((x) => TopProduct.fromMap(x))),
|
? []
|
||||||
paymentMethods: List<PaymentMethodAnalytic>.from(map['payment_methods']
|
: List<TopProduct>.from(
|
||||||
?.map((x) => PaymentMethodAnalytic.fromMap(x))),
|
map['top_products']?.map((x) => TopProduct.fromMap(x))),
|
||||||
recentSales: List<RecentSale>.from(
|
paymentMethods: map['payment_methods'] == null
|
||||||
map['recent_sales']?.map((x) => RecentSale.fromMap(x))),
|
? []
|
||||||
|
: List<PaymentMethodAnalytic>.from(map['payment_methods']
|
||||||
|
?.map((x) => PaymentMethodAnalytic.fromMap(x))),
|
||||||
|
recentSales: map['recent_sales'] == null
|
||||||
|
? []
|
||||||
|
: List<RecentSale>.from(
|
||||||
|
map['recent_sales']?.map((x) => RecentSale.fromMap(x))),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
|
|||||||
@@ -0,0 +1,290 @@
|
|||||||
|
class InventoryAnalyticResponseModel {
|
||||||
|
final bool success;
|
||||||
|
final InventoryAnalyticData? data;
|
||||||
|
final dynamic errors;
|
||||||
|
|
||||||
|
InventoryAnalyticResponseModel({
|
||||||
|
required this.success,
|
||||||
|
required this.data,
|
||||||
|
this.errors,
|
||||||
|
});
|
||||||
|
|
||||||
|
// From JSON
|
||||||
|
factory InventoryAnalyticResponseModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return InventoryAnalyticResponseModel(
|
||||||
|
success: json['success'],
|
||||||
|
data: json['data'] != null
|
||||||
|
? InventoryAnalyticData.fromMap(json['data'])
|
||||||
|
: null,
|
||||||
|
errors: json['errors'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'success': success,
|
||||||
|
'data': data?.toMap(),
|
||||||
|
'errors': errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Map
|
||||||
|
factory InventoryAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||||
|
return InventoryAnalyticResponseModel(
|
||||||
|
success: map['success'],
|
||||||
|
data: map['data'] != null
|
||||||
|
? InventoryAnalyticData.fromMap(map['data'])
|
||||||
|
: null,
|
||||||
|
errors: map['errors'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To Map
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'success': success,
|
||||||
|
'data': data?.toMap(),
|
||||||
|
'errors': errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InventoryAnalyticData {
|
||||||
|
final InventorySummary summary;
|
||||||
|
final List<InventoryProductItem> products;
|
||||||
|
final List<InventoryIngredientItem> ingredients;
|
||||||
|
|
||||||
|
InventoryAnalyticData({
|
||||||
|
required this.summary,
|
||||||
|
required this.products,
|
||||||
|
required this.ingredients,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory InventoryAnalyticData.fromMap(Map<String, dynamic> map) {
|
||||||
|
return InventoryAnalyticData(
|
||||||
|
summary: InventorySummary.fromMap(map['summary']),
|
||||||
|
products: map['products'] == null
|
||||||
|
? []
|
||||||
|
: List<InventoryProductItem>.from(
|
||||||
|
map['products']?.map((x) => InventoryProductItem.fromMap(x)) ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
ingredients: map['ingredients'] == null
|
||||||
|
? []
|
||||||
|
: List<InventoryIngredientItem>.from(
|
||||||
|
map['ingredients']
|
||||||
|
?.map((x) => InventoryIngredientItem.fromMap(x)) ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'summary': summary.toMap(),
|
||||||
|
'products': products.map((x) => x.toMap()).toList(),
|
||||||
|
'ingredients': ingredients.map((x) => x.toMap()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InventorySummary {
|
||||||
|
final int totalProducts;
|
||||||
|
final int totalIngredients;
|
||||||
|
final int totalValue;
|
||||||
|
final int lowStockProducts;
|
||||||
|
final int lowStockIngredients;
|
||||||
|
final int zeroStockProducts;
|
||||||
|
final int zeroStockIngredients;
|
||||||
|
final int totalSoldProducts;
|
||||||
|
final int totalSoldIngredients;
|
||||||
|
final String outletId;
|
||||||
|
final String outletName;
|
||||||
|
final DateTime generatedAt;
|
||||||
|
|
||||||
|
InventorySummary({
|
||||||
|
required this.totalProducts,
|
||||||
|
required this.totalIngredients,
|
||||||
|
required this.totalValue,
|
||||||
|
required this.lowStockProducts,
|
||||||
|
required this.lowStockIngredients,
|
||||||
|
required this.zeroStockProducts,
|
||||||
|
required this.zeroStockIngredients,
|
||||||
|
required this.totalSoldProducts,
|
||||||
|
required this.totalSoldIngredients,
|
||||||
|
required this.outletId,
|
||||||
|
required this.outletName,
|
||||||
|
required this.generatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory InventorySummary.fromMap(Map<String, dynamic> map) {
|
||||||
|
return InventorySummary(
|
||||||
|
totalProducts: map['total_products'] ?? 0,
|
||||||
|
totalIngredients: map['total_ingredients'] ?? 0,
|
||||||
|
totalValue: map['total_value'] ?? 0,
|
||||||
|
lowStockProducts: map['low_stock_products'] ?? 0,
|
||||||
|
lowStockIngredients: map['low_stock_ingredients'] ?? 0,
|
||||||
|
zeroStockProducts: map['zero_stock_products'] ?? 0,
|
||||||
|
zeroStockIngredients: map['zero_stock_ingredients'] ?? 0,
|
||||||
|
totalSoldProducts: map['total_sold_products'] ?? 0,
|
||||||
|
totalSoldIngredients: map['total_sold_ingredients'] ?? 0,
|
||||||
|
outletId: map['outlet_id'],
|
||||||
|
outletName: map['outlet_name'],
|
||||||
|
generatedAt: DateTime.parse(map['generated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'total_products': totalProducts,
|
||||||
|
'total_ingredients': totalIngredients,
|
||||||
|
'total_value': totalValue,
|
||||||
|
'low_stock_products': lowStockProducts,
|
||||||
|
'low_stock_ingredients': lowStockIngredients,
|
||||||
|
'zero_stock_products': zeroStockProducts,
|
||||||
|
'zero_stock_ingredients': zeroStockIngredients,
|
||||||
|
'total_sold_products': totalSoldProducts,
|
||||||
|
'total_sold_ingredients': totalSoldIngredients,
|
||||||
|
'outlet_id': outletId,
|
||||||
|
'outlet_name': outletName,
|
||||||
|
'generated_at': generatedAt.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InventoryProductItem {
|
||||||
|
final String id;
|
||||||
|
final String productId;
|
||||||
|
final String productName;
|
||||||
|
final String categoryName;
|
||||||
|
final int quantity;
|
||||||
|
final int reorderLevel;
|
||||||
|
final num unitCost;
|
||||||
|
final int totalValue;
|
||||||
|
final int totalIn;
|
||||||
|
final int totalOut;
|
||||||
|
final bool isLowStock;
|
||||||
|
final bool isZeroStock;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
|
||||||
|
InventoryProductItem({
|
||||||
|
required this.id,
|
||||||
|
required this.productId,
|
||||||
|
required this.productName,
|
||||||
|
required this.categoryName,
|
||||||
|
required this.quantity,
|
||||||
|
required this.reorderLevel,
|
||||||
|
required this.unitCost,
|
||||||
|
required this.totalValue,
|
||||||
|
required this.totalIn,
|
||||||
|
required this.totalOut,
|
||||||
|
required this.isLowStock,
|
||||||
|
required this.isZeroStock,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory InventoryProductItem.fromMap(Map<String, dynamic> map) {
|
||||||
|
return InventoryProductItem(
|
||||||
|
id: map['id'],
|
||||||
|
productId: map['product_id'],
|
||||||
|
productName: map['product_name'],
|
||||||
|
categoryName: map['category_name'],
|
||||||
|
quantity: map['quantity'] ?? 0,
|
||||||
|
reorderLevel: map['reorder_level'] ?? 0,
|
||||||
|
unitCost: map['unit_cost'] ?? 0,
|
||||||
|
totalValue: map['total_value'] ?? 0,
|
||||||
|
totalIn: map['total_in'] ?? 0,
|
||||||
|
totalOut: map['total_out'] ?? 0,
|
||||||
|
isLowStock: map['is_low_stock'] ?? false,
|
||||||
|
isZeroStock: map['is_zero_stock'] ?? false,
|
||||||
|
updatedAt: DateTime.parse(map['updated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'product_id': productId,
|
||||||
|
'product_name': productName,
|
||||||
|
'category_name': categoryName,
|
||||||
|
'quantity': quantity,
|
||||||
|
'reorder_level': reorderLevel,
|
||||||
|
'unit_cost': unitCost,
|
||||||
|
'total_value': totalValue,
|
||||||
|
'total_in': totalIn,
|
||||||
|
'total_out': totalOut,
|
||||||
|
'is_low_stock': isLowStock,
|
||||||
|
'is_zero_stock': isZeroStock,
|
||||||
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InventoryIngredientItem {
|
||||||
|
final String id;
|
||||||
|
final String ingredientId;
|
||||||
|
final String ingredientName;
|
||||||
|
final String unitName;
|
||||||
|
final int quantity;
|
||||||
|
final int reorderLevel;
|
||||||
|
final num unitCost;
|
||||||
|
final int totalValue;
|
||||||
|
final int totalIn;
|
||||||
|
final int totalOut;
|
||||||
|
final bool isLowStock;
|
||||||
|
final bool isZeroStock;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
|
||||||
|
InventoryIngredientItem({
|
||||||
|
required this.id,
|
||||||
|
required this.ingredientId,
|
||||||
|
required this.ingredientName,
|
||||||
|
required this.unitName,
|
||||||
|
required this.quantity,
|
||||||
|
required this.reorderLevel,
|
||||||
|
required this.unitCost,
|
||||||
|
required this.totalValue,
|
||||||
|
required this.totalIn,
|
||||||
|
required this.totalOut,
|
||||||
|
required this.isLowStock,
|
||||||
|
required this.isZeroStock,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory InventoryIngredientItem.fromMap(Map<String, dynamic> map) {
|
||||||
|
return InventoryIngredientItem(
|
||||||
|
id: map['id'],
|
||||||
|
ingredientId: map['ingredient_id'],
|
||||||
|
ingredientName: map['ingredient_name'],
|
||||||
|
unitName: map['unit_name'],
|
||||||
|
quantity: map['quantity'] ?? 0,
|
||||||
|
reorderLevel: map['reorder_level'] ?? 0,
|
||||||
|
unitCost: map['unit_cost'] ?? 0,
|
||||||
|
totalValue: map['total_value'] ?? 0,
|
||||||
|
totalIn: map['total_in'] ?? 0,
|
||||||
|
totalOut: map['total_out'] ?? 0,
|
||||||
|
isLowStock: map['is_low_stock'] ?? false,
|
||||||
|
isZeroStock: map['is_zero_stock'] ?? false,
|
||||||
|
updatedAt: DateTime.parse(map['updated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'ingredient_id': ingredientId,
|
||||||
|
'ingredient_name': ingredientName,
|
||||||
|
'unit_name': unitName,
|
||||||
|
'quantity': quantity,
|
||||||
|
'reorder_level': reorderLevel,
|
||||||
|
'unit_cost': unitCost,
|
||||||
|
'total_value': totalValue,
|
||||||
|
'total_in': totalIn,
|
||||||
|
'total_out': totalOut,
|
||||||
|
'is_low_stock': isLowStock,
|
||||||
|
'is_zero_stock': isZeroStock,
|
||||||
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
|
|
||||||
class OrderDetailResponseModel {
|
class OrderDetailResponseModel {
|
||||||
final bool? success;
|
final bool? success;
|
||||||
final Order? data;
|
final Order? data;
|
||||||
@@ -81,7 +84,7 @@ class OrderData {
|
|||||||
orders: map["orders"] == null
|
orders: map["orders"] == null
|
||||||
? []
|
? []
|
||||||
: List<Order>.from(map['orders']?.map((x) => Order.fromMap(x))),
|
: List<Order>.from(map['orders']?.map((x) => Order.fromMap(x))),
|
||||||
payments: map["orders"] == null
|
payments: map["payments"] == null
|
||||||
? []
|
? []
|
||||||
: List<Payment>.from(map['payments']?.map((x) => Payment.fromMap(x))),
|
: List<Payment>.from(map['payments']?.map((x) => Payment.fromMap(x))),
|
||||||
totalCount: map['total_count'],
|
totalCount: map['total_count'],
|
||||||
@@ -115,7 +118,7 @@ class Order {
|
|||||||
final int? taxAmount;
|
final int? taxAmount;
|
||||||
final int? discountAmount;
|
final int? discountAmount;
|
||||||
final int? totalAmount;
|
final int? totalAmount;
|
||||||
final int? totalCost;
|
final num? totalCost;
|
||||||
final int? remainingAmount;
|
final int? remainingAmount;
|
||||||
final String? paymentStatus;
|
final String? paymentStatus;
|
||||||
final int? refundAmount;
|
final int? refundAmount;
|
||||||
@@ -380,3 +383,121 @@ class Payment {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension OrderItemListExtension on List<OrderItem> {
|
||||||
|
List<ProductQuantity> toProductQuantities() => map((e) => ProductQuantity(
|
||||||
|
product: Product(
|
||||||
|
id: e.productId,
|
||||||
|
name: e.productName,
|
||||||
|
price: e.unitPrice,
|
||||||
|
printerType: e.printerType,
|
||||||
|
),
|
||||||
|
variant: ProductVariant(
|
||||||
|
id: e.productVariantId,
|
||||||
|
name: e.productVariantName,
|
||||||
|
productId: e.productId,
|
||||||
|
),
|
||||||
|
notes: e.notes ?? '',
|
||||||
|
quantity: e.quantity ?? 0,
|
||||||
|
)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OrderCopyWith on Order {
|
||||||
|
Order copyWith({
|
||||||
|
String? id,
|
||||||
|
String? orderNumber,
|
||||||
|
String? outletId,
|
||||||
|
String? userId,
|
||||||
|
String? tableNumber,
|
||||||
|
String? orderType,
|
||||||
|
String? status,
|
||||||
|
int? subtotal,
|
||||||
|
int? taxAmount,
|
||||||
|
int? discountAmount,
|
||||||
|
int? totalAmount,
|
||||||
|
num? totalCost,
|
||||||
|
int? remainingAmount,
|
||||||
|
String? paymentStatus,
|
||||||
|
int? refundAmount,
|
||||||
|
bool? isVoid,
|
||||||
|
bool? isRefund,
|
||||||
|
String? notes,
|
||||||
|
Map<String, dynamic>? metadata,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
List<OrderItem>? orderItems,
|
||||||
|
List<Payment>? payments,
|
||||||
|
int? totalPaid,
|
||||||
|
int? paymentCount,
|
||||||
|
String? splitType,
|
||||||
|
}) {
|
||||||
|
return Order(
|
||||||
|
id: id ?? this.id,
|
||||||
|
orderNumber: orderNumber ?? this.orderNumber,
|
||||||
|
outletId: outletId ?? this.outletId,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
tableNumber: tableNumber ?? this.tableNumber,
|
||||||
|
orderType: orderType ?? this.orderType,
|
||||||
|
status: status ?? this.status,
|
||||||
|
subtotal: subtotal ?? this.subtotal,
|
||||||
|
taxAmount: taxAmount ?? this.taxAmount,
|
||||||
|
discountAmount: discountAmount ?? this.discountAmount,
|
||||||
|
totalAmount: totalAmount ?? this.totalAmount,
|
||||||
|
totalCost: totalCost ?? this.totalCost,
|
||||||
|
remainingAmount: remainingAmount ?? this.remainingAmount,
|
||||||
|
paymentStatus: paymentStatus ?? this.paymentStatus,
|
||||||
|
refundAmount: refundAmount ?? this.refundAmount,
|
||||||
|
isVoid: isVoid ?? this.isVoid,
|
||||||
|
isRefund: isRefund ?? this.isRefund,
|
||||||
|
notes: notes ?? this.notes,
|
||||||
|
metadata: metadata ?? this.metadata,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
orderItems: orderItems ?? this.orderItems,
|
||||||
|
payments: payments ?? this.payments,
|
||||||
|
totalPaid: totalPaid ?? this.totalPaid,
|
||||||
|
paymentCount: paymentCount ?? this.paymentCount,
|
||||||
|
splitType: splitType ?? this.splitType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OrderItemCopyWith on OrderItem {
|
||||||
|
OrderItem copyWith({
|
||||||
|
String? id,
|
||||||
|
String? orderId,
|
||||||
|
String? productId,
|
||||||
|
String? productName,
|
||||||
|
String? productVariantId,
|
||||||
|
String? productVariantName,
|
||||||
|
int? quantity,
|
||||||
|
int? unitPrice,
|
||||||
|
int? totalPrice,
|
||||||
|
List<dynamic>? modifiers,
|
||||||
|
String? notes,
|
||||||
|
String? status,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
String? printerType,
|
||||||
|
int? paidQuantity,
|
||||||
|
}) {
|
||||||
|
return OrderItem(
|
||||||
|
id: id ?? this.id,
|
||||||
|
orderId: orderId ?? this.orderId,
|
||||||
|
productId: productId ?? this.productId,
|
||||||
|
productName: productName ?? this.productName,
|
||||||
|
productVariantId: productVariantId ?? this.productVariantId,
|
||||||
|
productVariantName: productVariantName ?? this.productVariantName,
|
||||||
|
quantity: quantity ?? this.quantity,
|
||||||
|
unitPrice: unitPrice ?? this.unitPrice,
|
||||||
|
totalPrice: totalPrice ?? this.totalPrice,
|
||||||
|
modifiers: modifiers ?? this.modifiers,
|
||||||
|
notes: notes ?? this.notes,
|
||||||
|
status: status ?? this.status,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
printerType: printerType ?? this.printerType,
|
||||||
|
paidQuantity: paidQuantity ?? this.paidQuantity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class PaymentMethodAnalyticResponseModel {
|
class PaymentMethodAnalyticResponseModel {
|
||||||
final bool success;
|
final bool success;
|
||||||
final PaymentMethodAnalyticData data;
|
final PaymentMethodAnalyticData? data;
|
||||||
final dynamic errors;
|
final dynamic errors;
|
||||||
|
|
||||||
PaymentMethodAnalyticResponseModel({
|
PaymentMethodAnalyticResponseModel({
|
||||||
@@ -18,7 +18,9 @@ class PaymentMethodAnalyticResponseModel {
|
|||||||
factory PaymentMethodAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
factory PaymentMethodAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||||
return PaymentMethodAnalyticResponseModel(
|
return PaymentMethodAnalyticResponseModel(
|
||||||
success: map['success'],
|
success: map['success'],
|
||||||
data: PaymentMethodAnalyticData.fromMap(map['data']),
|
data: map['data'] == null
|
||||||
|
? null
|
||||||
|
: PaymentMethodAnalyticData.fromMap(map['data']),
|
||||||
errors: map['errors'],
|
errors: map['errors'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -26,7 +28,7 @@ class PaymentMethodAnalyticResponseModel {
|
|||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
'success': success,
|
'success': success,
|
||||||
'data': data.toMap(),
|
'data': data?.toMap(),
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -64,9 +66,12 @@ class PaymentMethodAnalyticData {
|
|||||||
dateTo: DateTime.parse(map['date_to']),
|
dateTo: DateTime.parse(map['date_to']),
|
||||||
groupBy: map['group_by'],
|
groupBy: map['group_by'],
|
||||||
summary: PaymentSummary.fromMap(map['summary']),
|
summary: PaymentSummary.fromMap(map['summary']),
|
||||||
data: List<PaymentMethodAnalyticItem>.from(
|
data: map['data'] == null
|
||||||
map['data']?.map((x) => PaymentMethodAnalyticItem.fromMap(x)) ?? [],
|
? []
|
||||||
),
|
: List<PaymentMethodAnalyticItem>.from(
|
||||||
|
map['data']?.map((x) => PaymentMethodAnalyticItem.fromMap(x)) ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +132,7 @@ class PaymentMethodAnalyticItem {
|
|||||||
final int totalAmount;
|
final int totalAmount;
|
||||||
final int orderCount;
|
final int orderCount;
|
||||||
final int paymentCount;
|
final int paymentCount;
|
||||||
final int percentage;
|
final num percentage;
|
||||||
|
|
||||||
PaymentMethodAnalyticItem({
|
PaymentMethodAnalyticItem({
|
||||||
required this.paymentMethodId,
|
required this.paymentMethodId,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ class PrintModel {
|
|||||||
final String address;
|
final String address;
|
||||||
final String paper;
|
final String paper;
|
||||||
final String type;
|
final String type;
|
||||||
|
final DateTime? createdAt;
|
||||||
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
PrintModel({
|
PrintModel({
|
||||||
this.id,
|
this.id,
|
||||||
@@ -13,26 +15,130 @@ class PrintModel {
|
|||||||
required this.address,
|
required this.address,
|
||||||
required this.paper,
|
required this.paper,
|
||||||
required this.type,
|
required this.type,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
// from map
|
// Factory constructor from map (updated)
|
||||||
factory PrintModel.fromMap(Map<String, dynamic> map) {
|
factory PrintModel.fromMap(Map<String, dynamic> map) {
|
||||||
return PrintModel(
|
return PrintModel(
|
||||||
id: map['id'],
|
id: map['id'] as int?,
|
||||||
code: map['code'],
|
code: map['code'] as String,
|
||||||
name: map['name'],
|
name: map['name'] as String,
|
||||||
address: map['address'],
|
address: map['address'] as String,
|
||||||
paper: map['paper'],
|
paper: map['paper'] as String,
|
||||||
type: map['type'],
|
type: map['type'] as String,
|
||||||
|
createdAt: map['created_at'] != null
|
||||||
|
? DateTime.tryParse(map['created_at'] as String)
|
||||||
|
: null,
|
||||||
|
updatedAt: map['updated_at'] != null
|
||||||
|
? DateTime.tryParse(map['updated_at'] as String)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// to map
|
// Convert to map for database insertion (without id, with timestamps)
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMapForInsert() {
|
||||||
"code": code,
|
final now = DateTime.now().toIso8601String();
|
||||||
"name": name,
|
return {
|
||||||
"address": address,
|
'code': code,
|
||||||
"paper": paper,
|
'name': name,
|
||||||
"type": type,
|
'address': address,
|
||||||
};
|
'paper': paper,
|
||||||
|
'type': type,
|
||||||
|
'created_at': now,
|
||||||
|
'updated_at': now,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to map for database update (without id and created_at)
|
||||||
|
Map<String, dynamic> toMapForUpdate() {
|
||||||
|
return {
|
||||||
|
'code': code,
|
||||||
|
'name': name,
|
||||||
|
'address': address,
|
||||||
|
'paper': paper,
|
||||||
|
'type': type,
|
||||||
|
'updated_at': DateTime.now().toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to complete map (original method, enhanced)
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
if (id != null) 'id': id,
|
||||||
|
'code': code,
|
||||||
|
'name': name,
|
||||||
|
'address': address,
|
||||||
|
'paper': paper,
|
||||||
|
'type': type,
|
||||||
|
if (createdAt != null) 'created_at': createdAt!.toIso8601String(),
|
||||||
|
if (updatedAt != null) 'updated_at': updatedAt!.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy with method for creating modified instances
|
||||||
|
PrintModel copyWith({
|
||||||
|
int? id,
|
||||||
|
String? code,
|
||||||
|
String? name,
|
||||||
|
String? address,
|
||||||
|
String? paper,
|
||||||
|
String? type,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
}) {
|
||||||
|
return PrintModel(
|
||||||
|
id: id ?? this.id,
|
||||||
|
code: code ?? this.code,
|
||||||
|
name: name ?? this.name,
|
||||||
|
address: address ?? this.address,
|
||||||
|
paper: paper ?? this.paper,
|
||||||
|
type: type ?? this.type,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equality and hashCode
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
return other is PrintModel &&
|
||||||
|
other.id == id &&
|
||||||
|
other.code == code &&
|
||||||
|
other.name == name &&
|
||||||
|
other.address == address &&
|
||||||
|
other.paper == paper &&
|
||||||
|
other.type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return Object.hash(id, code, name, address, paper, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// String representation for debugging (matches datasource logging)
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'PrintModel(id: $id, code: $code, name: $name, address: $address, paper: $paper, type: $type, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation methods
|
||||||
|
bool get isValid {
|
||||||
|
return code.isNotEmpty &&
|
||||||
|
name.isNotEmpty &&
|
||||||
|
address.isNotEmpty &&
|
||||||
|
paper.isNotEmpty &&
|
||||||
|
type.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get validationError {
|
||||||
|
if (code.isEmpty) return 'Printer code cannot be empty';
|
||||||
|
if (name.isEmpty) return 'Printer name cannot be empty';
|
||||||
|
if (address.isEmpty) return 'Printer address cannot be empty';
|
||||||
|
if (paper.isEmpty) return 'Paper size cannot be empty';
|
||||||
|
if (type.isEmpty) return 'Printer type cannot be empty';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class ProductAnalyticResponseModel {
|
class ProductAnalyticResponseModel {
|
||||||
final bool success;
|
final bool success;
|
||||||
final ProductAnalyticData data;
|
final ProductAnalyticData? data;
|
||||||
final dynamic errors;
|
final dynamic errors;
|
||||||
|
|
||||||
ProductAnalyticResponseModel({
|
ProductAnalyticResponseModel({
|
||||||
@@ -17,7 +17,8 @@ class ProductAnalyticResponseModel {
|
|||||||
factory ProductAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
factory ProductAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
|
||||||
return ProductAnalyticResponseModel(
|
return ProductAnalyticResponseModel(
|
||||||
success: map['success'] ?? false,
|
success: map['success'] ?? false,
|
||||||
data: ProductAnalyticData.fromMap(map['data']),
|
data:
|
||||||
|
map['data'] == null ? null : ProductAnalyticData.fromMap(map['data']),
|
||||||
errors: map['errors'],
|
errors: map['errors'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -25,7 +26,7 @@ class ProductAnalyticResponseModel {
|
|||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
'success': success,
|
'success': success,
|
||||||
'data': data.toMap(),
|
'data': data?.toMap(),
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -52,9 +53,11 @@ class ProductAnalyticData {
|
|||||||
outletId: map['outlet_id'],
|
outletId: map['outlet_id'],
|
||||||
dateFrom: DateTime.parse(map['date_from']),
|
dateFrom: DateTime.parse(map['date_from']),
|
||||||
dateTo: DateTime.parse(map['date_to']),
|
dateTo: DateTime.parse(map['date_to']),
|
||||||
data: List<ProductAnalyticItem>.from(
|
data: map['data'] == null
|
||||||
map['data'].map((x) => ProductAnalyticItem.fromMap(x)),
|
? []
|
||||||
),
|
: List<ProductAnalyticItem>.from(
|
||||||
|
map['data'].map((x) => ProductAnalyticItem.fromMap(x)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
int? parseInt(dynamic value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
if (value is int) return value;
|
||||||
|
if (value is double) return value.toInt();
|
||||||
|
if (value is String) return int.tryParse(value);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
class ProductResponseModel {
|
class ProductResponseModel {
|
||||||
final bool? success;
|
final bool? success;
|
||||||
final ProductData? data;
|
final ProductData? data;
|
||||||
@@ -51,10 +59,10 @@ class ProductData {
|
|||||||
? []
|
? []
|
||||||
: List<Product>.from(
|
: List<Product>.from(
|
||||||
json["products"].map((x) => Product.fromMap(x))),
|
json["products"].map((x) => Product.fromMap(x))),
|
||||||
totalCount: json["total_count"],
|
totalCount: parseInt(json["total_count"]),
|
||||||
page: json["page"],
|
page: parseInt(json["page"]),
|
||||||
limit: json["limit"],
|
limit: parseInt(json["limit"]),
|
||||||
totalPages: json["total_pages"],
|
totalPages: parseInt(json["total_pages"]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
@@ -116,8 +124,8 @@ class Product {
|
|||||||
sku: json["sku"],
|
sku: json["sku"],
|
||||||
name: json["name"],
|
name: json["name"],
|
||||||
description: json["description"],
|
description: json["description"],
|
||||||
price: json["price"],
|
price: parseInt(json["price"]),
|
||||||
cost: json["cost"],
|
cost: parseInt(json["cost"]),
|
||||||
businessType: json["business_type"],
|
businessType: json["business_type"],
|
||||||
imageUrl: json["image_url"],
|
imageUrl: json["image_url"],
|
||||||
printerType: json["printer_type"],
|
printerType: json["printer_type"],
|
||||||
@@ -137,7 +145,7 @@ class Product {
|
|||||||
|
|
||||||
factory Product.fromOrderMap(Map<String, dynamic> json) => Product(
|
factory Product.fromOrderMap(Map<String, dynamic> json) => Product(
|
||||||
id: json["id_product"],
|
id: json["id_product"],
|
||||||
price: json["price"],
|
price: parseInt(json["price"]),
|
||||||
);
|
);
|
||||||
|
|
||||||
factory Product.fromLocalMap(Map<String, dynamic> json) => Product(
|
factory Product.fromLocalMap(Map<String, dynamic> json) => Product(
|
||||||
@@ -147,8 +155,8 @@ class Product {
|
|||||||
sku: json["sku"],
|
sku: json["sku"],
|
||||||
name: json["name"],
|
name: json["name"],
|
||||||
description: json["description"],
|
description: json["description"],
|
||||||
price: json["price"],
|
price: parseInt(json["price"]),
|
||||||
cost: json["cost"],
|
cost: parseInt(json["cost"]),
|
||||||
businessType: json["business_type"],
|
businessType: json["business_type"],
|
||||||
imageUrl: json["image_url"],
|
imageUrl: json["image_url"],
|
||||||
printerType: json["printer_type"],
|
printerType: json["printer_type"],
|
||||||
@@ -207,96 +215,6 @@ class Product {
|
|||||||
? []
|
? []
|
||||||
: List<dynamic>.from(variants!.map((x) => x.toMap())),
|
: List<dynamic>.from(variants!.map((x) => x.toMap())),
|
||||||
};
|
};
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(covariant Product other) {
|
|
||||||
if (identical(this, other)) return true;
|
|
||||||
|
|
||||||
return other.id == id &&
|
|
||||||
other.organizationId == organizationId &&
|
|
||||||
other.categoryId == categoryId &&
|
|
||||||
other.sku == sku &&
|
|
||||||
other.name == name &&
|
|
||||||
other.description == description &&
|
|
||||||
other.price == price &&
|
|
||||||
other.cost == cost &&
|
|
||||||
other.businessType == businessType &&
|
|
||||||
other.imageUrl == imageUrl &&
|
|
||||||
other.printerType == printerType &&
|
|
||||||
other.metadata == metadata &&
|
|
||||||
other.isActive == isActive &&
|
|
||||||
other.createdAt == createdAt &&
|
|
||||||
other.updatedAt == updatedAt &&
|
|
||||||
_listEquals(other.variants, variants);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _listEquals(List<ProductVariant>? a, List<ProductVariant>? b) {
|
|
||||||
if (a == null && b == null) return true;
|
|
||||||
if (a == null || b == null) return false;
|
|
||||||
if (a.length != b.length) return false;
|
|
||||||
for (int i = 0; i < a.length; i++) {
|
|
||||||
if (a[i] != b[i]) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode {
|
|
||||||
return id.hashCode ^
|
|
||||||
organizationId.hashCode ^
|
|
||||||
categoryId.hashCode ^
|
|
||||||
sku.hashCode ^
|
|
||||||
name.hashCode ^
|
|
||||||
description.hashCode ^
|
|
||||||
price.hashCode ^
|
|
||||||
cost.hashCode ^
|
|
||||||
businessType.hashCode ^
|
|
||||||
imageUrl.hashCode ^
|
|
||||||
printerType.hashCode ^
|
|
||||||
metadata.hashCode ^
|
|
||||||
isActive.hashCode ^
|
|
||||||
createdAt.hashCode ^
|
|
||||||
updatedAt.hashCode ^
|
|
||||||
variants.hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
Product copyWith({
|
|
||||||
String? id,
|
|
||||||
String? organizationId,
|
|
||||||
String? categoryId,
|
|
||||||
String? sku,
|
|
||||||
String? name,
|
|
||||||
String? description,
|
|
||||||
int? price,
|
|
||||||
int? cost,
|
|
||||||
String? businessType,
|
|
||||||
String? imageUrl,
|
|
||||||
String? printerType,
|
|
||||||
Map<String, dynamic>? metadata,
|
|
||||||
bool? isActive,
|
|
||||||
DateTime? createdAt,
|
|
||||||
DateTime? updatedAt,
|
|
||||||
List<ProductVariant>? variants,
|
|
||||||
}) {
|
|
||||||
return Product(
|
|
||||||
id: id ?? this.id,
|
|
||||||
organizationId: organizationId ?? this.organizationId,
|
|
||||||
categoryId: categoryId ?? this.categoryId,
|
|
||||||
sku: sku ?? this.sku,
|
|
||||||
name: name ?? this.name,
|
|
||||||
description: description ?? this.description,
|
|
||||||
price: price ?? this.price,
|
|
||||||
cost: cost ?? this.cost,
|
|
||||||
businessType: businessType ?? this.businessType,
|
|
||||||
imageUrl: imageUrl ?? this.imageUrl,
|
|
||||||
printerType: printerType ?? this.printerType,
|
|
||||||
metadata: metadata ?? this.metadata,
|
|
||||||
isActive: isActive ?? this.isActive,
|
|
||||||
createdAt: createdAt ?? this.createdAt,
|
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
|
||||||
variants: variants ?? this.variants,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Category {
|
class Category {
|
||||||
@@ -341,28 +259,6 @@ class Category {
|
|||||||
"created_at": createdAt?.toIso8601String(),
|
"created_at": createdAt?.toIso8601String(),
|
||||||
"updated_at": updatedAt?.toIso8601String(),
|
"updated_at": updatedAt?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(covariant Category other) {
|
|
||||||
if (identical(this, other)) return true;
|
|
||||||
|
|
||||||
return other.id == id &&
|
|
||||||
other.name == name &&
|
|
||||||
other.description == description &&
|
|
||||||
other.image == image &&
|
|
||||||
other.createdAt == createdAt &&
|
|
||||||
other.updatedAt == updatedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode {
|
|
||||||
return id.hashCode ^
|
|
||||||
name.hashCode ^
|
|
||||||
description.hashCode ^
|
|
||||||
image.hashCode ^
|
|
||||||
createdAt.hashCode ^
|
|
||||||
updatedAt.hashCode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProductVariant {
|
class ProductVariant {
|
||||||
@@ -390,8 +286,8 @@ class ProductVariant {
|
|||||||
id: json["id"],
|
id: json["id"],
|
||||||
productId: json["product_id"],
|
productId: json["product_id"],
|
||||||
name: json["name"],
|
name: json["name"],
|
||||||
priceModifier: json["price_modifier"],
|
priceModifier: parseInt(json["price_modifier"]),
|
||||||
cost: json["cost"],
|
cost: parseInt(json["cost"]),
|
||||||
metadata: json["metadata"] ?? {},
|
metadata: json["metadata"] ?? {},
|
||||||
createdAt: json["created_at"] == null
|
createdAt: json["created_at"] == null
|
||||||
? null
|
? null
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class ProfitLossResponseModel {
|
class ProfitLossResponseModel {
|
||||||
final bool success;
|
final bool success;
|
||||||
final ProfitLossData data;
|
final ProfitLossData? data;
|
||||||
final dynamic errors;
|
final dynamic errors;
|
||||||
|
|
||||||
ProfitLossResponseModel({
|
ProfitLossResponseModel({
|
||||||
@@ -13,7 +13,7 @@ class ProfitLossResponseModel {
|
|||||||
factory ProfitLossResponseModel.fromJson(Map<String, dynamic> json) {
|
factory ProfitLossResponseModel.fromJson(Map<String, dynamic> json) {
|
||||||
return ProfitLossResponseModel(
|
return ProfitLossResponseModel(
|
||||||
success: json['success'],
|
success: json['success'],
|
||||||
data: ProfitLossData.fromMap(json['data']),
|
data: json['data'] == null ? null : ProfitLossData.fromMap(json['data']),
|
||||||
errors: json['errors'],
|
errors: json['errors'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ class ProfitLossResponseModel {
|
|||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'success': success,
|
'success': success,
|
||||||
'data': data.toMap(),
|
'data': data?.toMap(),
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ class ProfitLossResponseModel {
|
|||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
'success': success,
|
'success': success,
|
||||||
'data': data.toMap(),
|
'data': data?.toMap(),
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -72,10 +72,14 @@ class ProfitLossData {
|
|||||||
dateTo: map['date_to'],
|
dateTo: map['date_to'],
|
||||||
groupBy: map['group_by'],
|
groupBy: map['group_by'],
|
||||||
summary: ProfitLossSummary.fromMap(map['summary']),
|
summary: ProfitLossSummary.fromMap(map['summary']),
|
||||||
data: List<ProfitLossItem>.from(
|
data: map['data'] == null
|
||||||
map['data'].map((x) => ProfitLossItem.fromMap(x))),
|
? []
|
||||||
productData: List<ProfitLossProduct>.from(
|
: List<ProfitLossItem>.from(
|
||||||
map['product_data'].map((x) => ProfitLossProduct.fromMap(x))),
|
map['data'].map((x) => ProfitLossItem.fromMap(x))),
|
||||||
|
productData: map['product_data'] == null
|
||||||
|
? []
|
||||||
|
: List<ProfitLossProduct>.from(
|
||||||
|
map['product_data'].map((x) => ProfitLossProduct.fromMap(x))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,15 +98,15 @@ class ProfitLossData {
|
|||||||
|
|
||||||
class ProfitLossSummary {
|
class ProfitLossSummary {
|
||||||
final int totalRevenue;
|
final int totalRevenue;
|
||||||
final int totalCost;
|
final num totalCost;
|
||||||
final int grossProfit;
|
final num grossProfit;
|
||||||
final double grossProfitMargin;
|
final double grossProfitMargin;
|
||||||
final int totalTax;
|
final int totalTax;
|
||||||
final int totalDiscount;
|
final int totalDiscount;
|
||||||
final int netProfit;
|
final num netProfit;
|
||||||
final double netProfitMargin;
|
final double netProfitMargin;
|
||||||
final int totalOrders;
|
final int totalOrders;
|
||||||
final int averageProfit;
|
final double averageProfit;
|
||||||
final double profitabilityRatio;
|
final double profitabilityRatio;
|
||||||
|
|
||||||
ProfitLossSummary({
|
ProfitLossSummary({
|
||||||
@@ -130,7 +134,7 @@ class ProfitLossSummary {
|
|||||||
netProfit: map['net_profit'],
|
netProfit: map['net_profit'],
|
||||||
netProfitMargin: (map['net_profit_margin'] as num).toDouble(),
|
netProfitMargin: (map['net_profit_margin'] as num).toDouble(),
|
||||||
totalOrders: map['total_orders'],
|
totalOrders: map['total_orders'],
|
||||||
averageProfit: map['average_profit'],
|
averageProfit: (map['average_profit'] as num).toDouble(),
|
||||||
profitabilityRatio: (map['profitability_ratio'] as num).toDouble(),
|
profitabilityRatio: (map['profitability_ratio'] as num).toDouble(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -155,12 +159,12 @@ class ProfitLossSummary {
|
|||||||
class ProfitLossItem {
|
class ProfitLossItem {
|
||||||
final String date;
|
final String date;
|
||||||
final int revenue;
|
final int revenue;
|
||||||
final int cost;
|
final num cost;
|
||||||
final int grossProfit;
|
final num grossProfit;
|
||||||
final double grossProfitMargin;
|
final double grossProfitMargin;
|
||||||
final int tax;
|
final int tax;
|
||||||
final int discount;
|
final int discount;
|
||||||
final int netProfit;
|
final num netProfit;
|
||||||
final double netProfitMargin;
|
final double netProfitMargin;
|
||||||
final int orders;
|
final int orders;
|
||||||
|
|
||||||
@@ -215,12 +219,12 @@ class ProfitLossProduct {
|
|||||||
final String categoryName;
|
final String categoryName;
|
||||||
final int quantitySold;
|
final int quantitySold;
|
||||||
final int revenue;
|
final int revenue;
|
||||||
final int cost;
|
final num cost;
|
||||||
final int grossProfit;
|
final num grossProfit;
|
||||||
final double grossProfitMargin;
|
final double grossProfitMargin;
|
||||||
final int averagePrice;
|
final int averagePrice;
|
||||||
final int averageCost;
|
final num averageCost;
|
||||||
final int profitPerUnit;
|
final num profitPerUnit;
|
||||||
|
|
||||||
ProfitLossProduct({
|
ProfitLossProduct({
|
||||||
required this.productId,
|
required this.productId,
|
||||||
@@ -248,8 +252,8 @@ class ProfitLossProduct {
|
|||||||
cost: map['cost'],
|
cost: map['cost'],
|
||||||
grossProfit: map['gross_profit'],
|
grossProfit: map['gross_profit'],
|
||||||
grossProfitMargin: (map['gross_profit_margin'] as num).toDouble(),
|
grossProfitMargin: (map['gross_profit_margin'] as num).toDouble(),
|
||||||
averagePrice: map['average_price'],
|
averagePrice: (map['average_price'] as num).toInt(),
|
||||||
averageCost: map['average_cost'],
|
averageCost: (map['average_cost'] as num).toInt(),
|
||||||
profitPerUnit: map['profit_per_unit'],
|
profitPerUnit: map['profit_per_unit'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
|
||||||
|
|
||||||
|
class CategoryRepository {
|
||||||
|
static CategoryRepository? _instance;
|
||||||
|
|
||||||
|
final CategoryLocalDatasource _localDatasource;
|
||||||
|
final CategoryRemoteDatasource _remoteDatasource;
|
||||||
|
|
||||||
|
CategoryRepository._internal()
|
||||||
|
: _localDatasource = CategoryLocalDatasource.instance,
|
||||||
|
_remoteDatasource = CategoryRemoteDatasource();
|
||||||
|
|
||||||
|
static CategoryRepository get instance {
|
||||||
|
_instance ??= CategoryRepository._internal();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// SYNC STRATEGY: REMOTE-FIRST WITH LOCAL FALLBACK
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, CategoryResponseModel>> getCategories({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
bool isActive = true,
|
||||||
|
String? search,
|
||||||
|
bool forceRemote = false,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
log('📱 Getting categories - page: $page, isActive: $isActive, search: $search, forceRemote: $forceRemote');
|
||||||
|
|
||||||
|
// Clean expired cache
|
||||||
|
_localDatasource.clearExpiredCache();
|
||||||
|
|
||||||
|
// Check if we should try remote first
|
||||||
|
if (forceRemote || !await _localDatasource.hasCategories()) {
|
||||||
|
log('🌐 Attempting remote fetch first...');
|
||||||
|
|
||||||
|
final remoteResult = await _getRemoteCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: isActive,
|
||||||
|
);
|
||||||
|
|
||||||
|
return await remoteResult.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Remote fetch failed: $failure');
|
||||||
|
log('📱 Falling back to local data...');
|
||||||
|
return _getLocalCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: isActive,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
log('✅ Remote fetch successful, syncing to local...');
|
||||||
|
|
||||||
|
// Sync remote data to local
|
||||||
|
if (response.data.categories.isNotEmpty) {
|
||||||
|
await _syncToLocal(response.data.categories,
|
||||||
|
clearFirst: page == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Right(response);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log('📱 Using local data (cache available)...');
|
||||||
|
return _getLocalCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: isActive,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error in getCategories: $e');
|
||||||
|
return Left('Gagal memuat kategori: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// PURE LOCAL OPERATIONS
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, CategoryResponseModel>> _getLocalCategories({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
bool isActive = true,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final cachedCategories = await _localDatasource.getCachedCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: isActive,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
|
||||||
|
final totalCount = await _localDatasource.getTotalCount(
|
||||||
|
isActive: isActive,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
|
||||||
|
final categoryData = CategoryData(
|
||||||
|
categories: cachedCategories,
|
||||||
|
totalCount: totalCount,
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
totalPages: totalCount > 0 ? (totalCount / limit).ceil() : 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
final response = CategoryResponseModel(
|
||||||
|
success: true,
|
||||||
|
data: categoryData,
|
||||||
|
);
|
||||||
|
|
||||||
|
log('✅ Returned ${cachedCategories.length} local categories (${totalCount} total)');
|
||||||
|
return Right(response);
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting local categories: $e');
|
||||||
|
return Left('Gagal memuat kategori dari database lokal: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// REMOTE FETCH
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, CategoryResponseModel>> _getRemoteCategories({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
bool isActive = true,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
log('🌐 Fetching categories from remote...');
|
||||||
|
return await _remoteDatasource.getCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: isActive,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Remote fetch error: $e');
|
||||||
|
return Left('Gagal mengambil data dari server: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// SYNC TO LOCAL
|
||||||
|
// ========================================
|
||||||
|
Future<void> _syncToLocal(List<CategoryModel> categories,
|
||||||
|
{bool clearFirst = false}) async {
|
||||||
|
try {
|
||||||
|
log('💾 Syncing ${categories.length} categories to local database...');
|
||||||
|
await _localDatasource.saveCategoriesBatch(categories,
|
||||||
|
clearFirst: clearFirst);
|
||||||
|
log('✅ Categories synced to local successfully');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error syncing categories to local: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// MANUAL SYNC OPERATIONS
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, String>> syncAllCategories() async {
|
||||||
|
try {
|
||||||
|
log('🔄 Starting manual sync of all categories...');
|
||||||
|
|
||||||
|
int page = 1;
|
||||||
|
const limit = 50; // Higher limit for bulk sync
|
||||||
|
bool hasMore = true;
|
||||||
|
int totalSynced = 0;
|
||||||
|
|
||||||
|
// Clear local data first for fresh sync
|
||||||
|
await _localDatasource.clearAllCategories();
|
||||||
|
|
||||||
|
while (hasMore) {
|
||||||
|
log('📄 Syncing page $page...');
|
||||||
|
|
||||||
|
final result = await _remoteDatasource.getCategories(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
isActive: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Sync failed at page $page: $failure');
|
||||||
|
throw Exception(failure);
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final categories = response.data.categories;
|
||||||
|
|
||||||
|
if (categories.isNotEmpty) {
|
||||||
|
await _localDatasource.saveCategoriesBatch(
|
||||||
|
categories,
|
||||||
|
clearFirst: false, // Don't clear on subsequent pages
|
||||||
|
);
|
||||||
|
totalSynced += categories.length;
|
||||||
|
|
||||||
|
// Check if we have more pages
|
||||||
|
hasMore = page < response.data.totalPages;
|
||||||
|
page++;
|
||||||
|
|
||||||
|
log('📦 Page $page synced: ${categories.length} categories');
|
||||||
|
} else {
|
||||||
|
hasMore = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final message = 'Berhasil sinkronisasi $totalSynced kategori';
|
||||||
|
log('✅ $message');
|
||||||
|
return Right(message);
|
||||||
|
} catch (e) {
|
||||||
|
final error = 'Gagal sinkronisasi kategori: $e';
|
||||||
|
log('❌ $error');
|
||||||
|
return Left(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// UTILITY METHODS
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, CategoryResponseModel>> refreshCategories({
|
||||||
|
bool isActive = true,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
log('🔄 Refreshing categories...');
|
||||||
|
clearCache();
|
||||||
|
|
||||||
|
return await getCategories(
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
isActive: isActive,
|
||||||
|
search: search,
|
||||||
|
forceRemote: true, // Force remote refresh
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<CategoryModel?> getCategoryById(String id) async {
|
||||||
|
log('🔍 Getting category by ID: $id');
|
||||||
|
return await _localDatasource.getCategoryById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<CategoryModel>> getAllCategories() async {
|
||||||
|
log('📋 Getting all categories for dropdown...');
|
||||||
|
return await _localDatasource.getAllCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> hasLocalCategories() async {
|
||||||
|
final hasCategories = await _localDatasource.hasCategories();
|
||||||
|
log('📊 Has local categories: $hasCategories');
|
||||||
|
return hasCategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getDatabaseStats() async {
|
||||||
|
final stats = await _localDatasource.getDatabaseStats();
|
||||||
|
log('📊 Category database stats: $stats');
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearCache() {
|
||||||
|
log('🧹 Clearing category cache');
|
||||||
|
_localDatasource.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> isLocalDatabaseReady() async {
|
||||||
|
try {
|
||||||
|
final stats = await getDatabaseStats();
|
||||||
|
final categoryCount = stats['total_categories'] ?? 0;
|
||||||
|
final isReady = categoryCount > 0;
|
||||||
|
log('🔍 Category database ready: $isReady ($categoryCount categories)');
|
||||||
|
return isReady;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error checking category database readiness: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearAllCategories() async {
|
||||||
|
try {
|
||||||
|
log('🗑️ Clearing all categories from repository...');
|
||||||
|
await _localDatasource.clearAllCategories();
|
||||||
|
clearCache();
|
||||||
|
log('✅ All categories cleared successfully');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error clearing all categories: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
||||||
|
|
||||||
|
class ProductRepository {
|
||||||
|
static ProductRepository? _instance;
|
||||||
|
|
||||||
|
final ProductLocalDatasource _localDatasource;
|
||||||
|
final ProductRemoteDatasource _remoteDatasource;
|
||||||
|
|
||||||
|
ProductRepository._internal()
|
||||||
|
: _localDatasource = ProductLocalDatasource.instance,
|
||||||
|
_remoteDatasource = ProductRemoteDatasource();
|
||||||
|
|
||||||
|
static ProductRepository get instance {
|
||||||
|
_instance ??= ProductRepository._internal();
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// PURE LOCAL DATABASE OPERATIONS
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, ProductResponseModel>> getProducts({
|
||||||
|
int page = 1,
|
||||||
|
int limit = 10,
|
||||||
|
String? categoryId,
|
||||||
|
String? search,
|
||||||
|
bool forceRefresh = false, // Ignored - kept for compatibility
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
log('📱 Getting products from local database - page: $page, categoryId: $categoryId, search: $search');
|
||||||
|
|
||||||
|
// Clean expired cache for optimal performance
|
||||||
|
_localDatasource.clearExpiredCache();
|
||||||
|
|
||||||
|
// Use cached query for maximum performance
|
||||||
|
final cachedProducts = await _localDatasource.getCachedProducts(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
categoryId: categoryId,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
|
||||||
|
final totalCount = await _localDatasource.getTotalCount(
|
||||||
|
categoryId: categoryId,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
|
||||||
|
final productData = ProductData(
|
||||||
|
products: cachedProducts,
|
||||||
|
totalCount: totalCount,
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
totalPages: totalCount > 0 ? (totalCount / limit).ceil() : 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
final response = ProductResponseModel(
|
||||||
|
success: true,
|
||||||
|
data: productData,
|
||||||
|
errors: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
log('✅ Returned ${cachedProducts.length} local products (${totalCount} total)');
|
||||||
|
return Right(response);
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting local products: $e');
|
||||||
|
return Left('Gagal memuat produk dari database lokal: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// OPTIMIZED LOCAL SEARCH
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, List<Product>>> searchProductsOptimized(
|
||||||
|
String query) async {
|
||||||
|
try {
|
||||||
|
log('🔍 Local optimized search for: "$query"');
|
||||||
|
|
||||||
|
final products = await _localDatasource.searchProductsOptimized(query);
|
||||||
|
|
||||||
|
log('✅ Local search completed: ${products.length} results');
|
||||||
|
return Right(products);
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error in local search: $e');
|
||||||
|
return Left('Pencarian lokal gagal: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// PRODUCT SYNC OPERATIONS
|
||||||
|
// ========================================
|
||||||
|
Future<Either<String, String>> syncAllProducts() async {
|
||||||
|
try {
|
||||||
|
log('🔄 Starting manual sync of all products...');
|
||||||
|
|
||||||
|
int page = 1;
|
||||||
|
const limit = 50; // Higher limit for bulk sync
|
||||||
|
bool hasMore = true;
|
||||||
|
int totalSynced = 0;
|
||||||
|
|
||||||
|
// Clear local data first for fresh sync
|
||||||
|
await _localDatasource.clearAllProducts();
|
||||||
|
|
||||||
|
while (hasMore) {
|
||||||
|
log('📄 Syncing page $page...');
|
||||||
|
|
||||||
|
final result = await _remoteDatasource.getProducts(
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Sync failed at page $page: $failure');
|
||||||
|
throw Exception(failure);
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final products = response.data?.products ?? [];
|
||||||
|
|
||||||
|
if (products.isNotEmpty) {
|
||||||
|
await _localDatasource.saveProductsBatch(
|
||||||
|
products,
|
||||||
|
clearFirst: false, // Don't clear on subsequent pages
|
||||||
|
);
|
||||||
|
totalSynced += products.length;
|
||||||
|
|
||||||
|
// Check if we have more pages
|
||||||
|
hasMore = page < (response.data?.totalPages ?? 0);
|
||||||
|
page++;
|
||||||
|
|
||||||
|
log('📦 Page $page synced: ${products.length} products');
|
||||||
|
} else {
|
||||||
|
hasMore = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final message = 'Berhasil sinkronisasi $totalSynced produk';
|
||||||
|
log('✅ $message');
|
||||||
|
return Right(message);
|
||||||
|
} catch (e) {
|
||||||
|
final error = 'Gagal sinkronisasi produk: $e';
|
||||||
|
log('❌ $error');
|
||||||
|
return Left(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// LOCAL DATABASE OPERATIONS
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
// Refresh just cleans cache and reloads from local
|
||||||
|
Future<Either<String, ProductResponseModel>> refreshProducts({
|
||||||
|
String? categoryId,
|
||||||
|
String? search,
|
||||||
|
}) async {
|
||||||
|
log('🔄 Refreshing local products...');
|
||||||
|
|
||||||
|
// Clear cache for fresh local data
|
||||||
|
clearCache();
|
||||||
|
|
||||||
|
return await getProducts(
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
categoryId: categoryId,
|
||||||
|
search: search,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Product?> getProductById(String id) async {
|
||||||
|
log('🔍 Getting product by ID from local: $id');
|
||||||
|
return await _localDatasource.getProductById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> hasLocalProducts() async {
|
||||||
|
final hasProducts = await _localDatasource.hasProducts();
|
||||||
|
log('📊 Has local products: $hasProducts');
|
||||||
|
return hasProducts;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getDatabaseStats() async {
|
||||||
|
final stats = await _localDatasource.getDatabaseStats();
|
||||||
|
log('📊 Database stats: $stats');
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearCache() {
|
||||||
|
log('🧹 Clearing local cache');
|
||||||
|
_localDatasource.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to check if local database is populated
|
||||||
|
Future<bool> isLocalDatabaseReady() async {
|
||||||
|
try {
|
||||||
|
final stats = await getDatabaseStats();
|
||||||
|
final productCount = stats['total_products'] ?? 0;
|
||||||
|
final isReady = productCount > 0;
|
||||||
|
log('🔍 Local database ready: $isReady ($productCount products)');
|
||||||
|
return isReady;
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error checking database readiness: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearAllProducts() async {
|
||||||
|
try {
|
||||||
|
log('🗑️ Clearing all products from repository...');
|
||||||
|
await _localDatasource.clearAllProducts();
|
||||||
|
clearCache();
|
||||||
|
log('✅ All products cleared successfully');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error clearing all products: $e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
|
import 'package:enaklo_pos/data/repositories/product/product_repository.dart';
|
||||||
|
|
||||||
|
class SyncManager {
|
||||||
|
final ProductRepository _productRepository;
|
||||||
|
final Connectivity _connectivity = Connectivity();
|
||||||
|
|
||||||
|
Timer? _syncTimer;
|
||||||
|
bool _isSyncing = false;
|
||||||
|
StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;
|
||||||
|
|
||||||
|
SyncManager(this._productRepository) {
|
||||||
|
_startPeriodicSync();
|
||||||
|
_listenToConnectivityChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startPeriodicSync() {
|
||||||
|
// Sync setiap 5 menit jika ada koneksi
|
||||||
|
_syncTimer = Timer.periodic(Duration(minutes: 5), (timer) {
|
||||||
|
_performBackgroundSync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listenToConnectivityChanges() {
|
||||||
|
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(
|
||||||
|
(List<ConnectivityResult> results) {
|
||||||
|
// Check if any connection is available
|
||||||
|
final hasConnection =
|
||||||
|
results.any((result) => result != ConnectivityResult.none);
|
||||||
|
|
||||||
|
if (hasConnection) {
|
||||||
|
log('Connection restored, starting background sync');
|
||||||
|
_performBackgroundSync();
|
||||||
|
} else {
|
||||||
|
log('Connection lost');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _performBackgroundSync() async {
|
||||||
|
if (_isSyncing) return;
|
||||||
|
|
||||||
|
// Check current connectivity before syncing
|
||||||
|
final connectivityResults = await _connectivity.checkConnectivity();
|
||||||
|
final hasConnection =
|
||||||
|
connectivityResults.any((result) => result != ConnectivityResult.none);
|
||||||
|
|
||||||
|
if (!hasConnection) {
|
||||||
|
log('No internet connection, skipping sync');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_isSyncing = true;
|
||||||
|
log('Starting background sync');
|
||||||
|
|
||||||
|
await _productRepository.refreshProducts();
|
||||||
|
|
||||||
|
log('Background sync completed');
|
||||||
|
} catch (e) {
|
||||||
|
log('Background sync failed: $e');
|
||||||
|
} finally {
|
||||||
|
_isSyncing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public method untuk manual sync
|
||||||
|
Future<void> performManualSync() async {
|
||||||
|
await _performBackgroundSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_syncTimer?.cancel();
|
||||||
|
_connectivitySubscription?.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
-39
@@ -1,14 +1,17 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
import 'dart:ui';
|
||||||
import 'package:enaklo_pos/core/constants/theme.dart';
|
import 'package:enaklo_pos/core/constants/theme.dart';
|
||||||
import 'package:enaklo_pos/core/network/dio_client.dart';
|
import 'package:enaklo_pos/core/network/dio_client.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/customer_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/customer_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/file_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/file_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_remote_data_source.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/table_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/table_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/user_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/user_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/presentation/customer/bloc/customer_form/customer_form_bloc.dart';
|
import 'package:enaklo_pos/presentation/customer/bloc/customer_form/customer_form_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/customer/bloc/customer_loader/customer_loader_bloc.dart';
|
import 'package:enaklo_pos/presentation/customer/bloc/customer_loader/customer_loader_bloc.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/data_sync/bloc/data_sync_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/current_outlet/current_outlet_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/current_outlet/current_outlet_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart';
|
||||||
@@ -16,21 +19,25 @@ import 'package:enaklo_pos/presentation/home/bloc/outlet_loader/outlet_loader_bl
|
|||||||
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart';
|
import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/report/blocs/category_report/category_report_bloc.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/report/blocs/inventory_report/inventory_report_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
|
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart';
|
import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
|
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/upload_file/upload_file_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/upload_file/upload_file_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/blocs/transfer_table/transfer_table_bloc.dart';
|
import 'package:enaklo_pos/presentation/table/blocs/transfer_table/transfer_table_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/void/bloc/void_order_bloc.dart';
|
import 'package:enaklo_pos/presentation/void/bloc/void_order_bloc.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/category/category_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/discount_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/discount_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/midtrans_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/midtrans_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/payment_methods_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/payment_methods_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
||||||
@@ -40,14 +47,11 @@ import 'package:enaklo_pos/presentation/home/bloc/get_table_status/get_table_sta
|
|||||||
import 'package:enaklo_pos/presentation/home/bloc/online_checker/online_checker_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/online_checker/online_checker_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/status_table/status_table_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/checkout/checkout_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/blocs/item_sales_report/item_sales_report_bloc.dart';
|
import 'package:enaklo_pos/presentation/report/blocs/item_sales_report/item_sales_report_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/blocs/payment_method_report/payment_method_report_bloc.dart';
|
import 'package:enaklo_pos/presentation/report/blocs/payment_method_report/payment_method_report_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/blocs/product_sales/product_sales_bloc.dart';
|
import 'package:enaklo_pos/presentation/report/blocs/product_sales/product_sales_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/blocs/summary/summary_bloc.dart';
|
import 'package:enaklo_pos/presentation/report/blocs/summary/summary_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/sales/blocs/bloc/last_order_table_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/sales/blocs/day_sales/day_sales_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/add_product/add_product_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/add_product/add_product_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/get_categories/get_categories_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/get_categories/get_categories_bloc.dart';
|
||||||
@@ -61,18 +65,14 @@ import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_print
|
|||||||
import 'package:enaklo_pos/presentation/table/blocs/change_position_table/change_position_table_bloc.dart';
|
import 'package:enaklo_pos/presentation/table/blocs/change_position_table/change_position_table_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/blocs/create_table/create_table_bloc.dart';
|
import 'package:enaklo_pos/presentation/table/blocs/create_table/create_table_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
|
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/local_product/local_product_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/order/order_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/order/order_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/blocs/transaction_report/transaction_report_bloc.dart';
|
import 'package:enaklo_pos/presentation/report/blocs/transaction_report/transaction_report_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/add_discount/add_discount_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/add_discount/add_discount_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/discount/discount_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/discount/discount_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_product/sync_product_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/sync_product/sync_product_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/tax_settings/tax_settings_bloc.dart';
|
import 'package:enaklo_pos/presentation/setting/bloc/tax_settings/tax_settings_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/blocs/update_table/update_table_bloc.dart';
|
import 'package:enaklo_pos/presentation/table/blocs/update_table/update_table_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/add_order_items/add_order_items_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/add_order_items/add_order_items_bloc.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'presentation/auth/bloc/login/login_bloc.dart';
|
import 'presentation/auth/bloc/login/login_bloc.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@@ -80,16 +80,28 @@ import 'presentation/home/pages/dashboard_page.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
runZonedGuarded(() async {
|
||||||
await SystemChrome.setPreferredOrientations([
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
DeviceOrientation.landscapeLeft,
|
|
||||||
DeviceOrientation.landscapeRight,
|
await Firebase.initializeApp();
|
||||||
]);
|
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
|
||||||
// await LamanPrint.init();
|
|
||||||
// final dir = await getApplicationDocumentsDirectory();
|
// ✅ Tambah custom logs
|
||||||
// Hive.init(dir.path);
|
FlutterError.onError = (errorDetails) {
|
||||||
// Hive.registerAdapter(TableDataAdapter());
|
FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
|
||||||
runApp(ProviderScope(child: MyApp()));
|
};
|
||||||
|
|
||||||
|
// ✅ Catch platform errors juga
|
||||||
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
runApp(ProviderScope(child: MyApp()));
|
||||||
|
}, (error, stack) {
|
||||||
|
// ✅ Ini udah bener
|
||||||
|
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
@@ -130,10 +142,6 @@ class _MyAppState extends State<MyApp> {
|
|||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => SyncProductBloc(ProductRemoteDatasource()),
|
create: (context) => SyncProductBloc(ProductRemoteDatasource()),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
|
||||||
create: (context) =>
|
|
||||||
LocalProductBloc(ProductLocalDatasource.instance),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) =>
|
create: (context) =>
|
||||||
CheckoutBloc(settingsLocalDatasource: SettingsLocalDatasource()),
|
CheckoutBloc(settingsLocalDatasource: SettingsLocalDatasource()),
|
||||||
@@ -147,9 +155,6 @@ class _MyAppState extends State<MyApp> {
|
|||||||
return OrderBloc(OrderRemoteDatasource());
|
return OrderBloc(OrderRemoteDatasource());
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
BlocProvider(
|
|
||||||
create: (context) => SyncOrderBloc(OrderRemoteDatasource()),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => DiscountBloc(DiscountRemoteDatasource()),
|
create: (context) => DiscountBloc(DiscountRemoteDatasource()),
|
||||||
),
|
),
|
||||||
@@ -171,13 +176,6 @@ class _MyAppState extends State<MyApp> {
|
|||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => UpdateTableBloc(),
|
create: (context) => UpdateTableBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
|
||||||
create: (context) => StatusTableBloc(ProductLocalDatasource.instance),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
|
||||||
create: (context) =>
|
|
||||||
LastOrderTableBloc(ProductLocalDatasource.instance),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => GetTableStatusBloc(TableRemoteDataSource()),
|
create: (context) => GetTableStatusBloc(TableRemoteDataSource()),
|
||||||
),
|
),
|
||||||
@@ -209,9 +207,6 @@ class _MyAppState extends State<MyApp> {
|
|||||||
create: (context) =>
|
create: (context) =>
|
||||||
PaymentMethodReportBloc(AnalyticRemoteDatasource()),
|
PaymentMethodReportBloc(AnalyticRemoteDatasource()),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
|
||||||
create: (context) => DaySalesBloc(ProductLocalDatasource.instance),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => QrisBloc(MidtransRemoteDatasource()),
|
create: (context) => QrisBloc(MidtransRemoteDatasource()),
|
||||||
),
|
),
|
||||||
@@ -244,7 +239,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
create: (context) => AddOrderItemsBloc(OrderRemoteDatasource()),
|
create: (context) => AddOrderItemsBloc(OrderRemoteDatasource()),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => ProductLoaderBloc(ProductRemoteDatasource()),
|
create: (context) => ProductLoaderBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => OrderFormBloc(OrderRemoteDatasource()),
|
create: (context) => OrderFormBloc(OrderRemoteDatasource()),
|
||||||
@@ -280,7 +275,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
create: (context) => UploadFileBloc(FileRemoteDataSource()),
|
create: (context) => UploadFileBloc(FileRemoteDataSource()),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => CategoryLoaderBloc(CategoryRemoteDatasource()),
|
create: (context) => CategoryLoaderBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => GetPrinterTicketBloc(),
|
create: (context) => GetPrinterTicketBloc(),
|
||||||
@@ -288,6 +283,18 @@ class _MyAppState extends State<MyApp> {
|
|||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => TransferTableBloc(TableRemoteDataSource()),
|
create: (context) => TransferTableBloc(TableRemoteDataSource()),
|
||||||
),
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => ReportBloc(AnalyticRemoteDatasource()),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => InventoryReportBloc(AnalyticRemoteDatasource()),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => CategoryReportBloc(AnalyticRemoteDatasource()),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => DataSyncBloc(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
navigatorKey: AuthInterceptor.navigatorKey,
|
navigatorKey: AuthInterceptor.navigatorKey,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:enaklo_pos/presentation/data_sync/pages/data_sync_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
@@ -9,7 +10,6 @@ import '../../core/components/buttons.dart';
|
|||||||
import '../../core/components/custom_text_field.dart';
|
import '../../core/components/custom_text_field.dart';
|
||||||
import '../../core/components/spaces.dart';
|
import '../../core/components/spaces.dart';
|
||||||
import '../../core/constants/colors.dart';
|
import '../../core/constants/colors.dart';
|
||||||
import '../home/pages/dashboard_page.dart';
|
|
||||||
import 'bloc/login/login_bloc.dart';
|
import 'bloc/login/login_bloc.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
@@ -104,7 +104,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const DashboardPage(),
|
builder: (context) => const DataSyncPage(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/product/product_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/category/category_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/repositories/category/category_repository.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import '../../../data/datasources/product_remote_datasource.dart';
|
||||||
|
|
||||||
|
part 'data_sync_event.dart';
|
||||||
|
part 'data_sync_state.dart';
|
||||||
|
part 'data_sync_bloc.freezed.dart';
|
||||||
|
|
||||||
|
enum SyncStep { categories, products, variants, completed }
|
||||||
|
|
||||||
|
class SyncStats {
|
||||||
|
final int totalProducts;
|
||||||
|
final int totalCategories;
|
||||||
|
final int totalVariants;
|
||||||
|
final double databaseSizeMB;
|
||||||
|
|
||||||
|
SyncStats({
|
||||||
|
required this.totalProducts,
|
||||||
|
required this.totalCategories,
|
||||||
|
required this.totalVariants,
|
||||||
|
required this.databaseSizeMB,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataSyncBloc extends Bloc<DataSyncEvent, DataSyncState> {
|
||||||
|
final ProductRemoteDatasource _productRemoteDatasource =
|
||||||
|
ProductRemoteDatasource();
|
||||||
|
final ProductLocalDatasource _productLocalDatasource =
|
||||||
|
ProductLocalDatasource.instance;
|
||||||
|
final CategoryLocalDatasource _categoryLocalDatasource =
|
||||||
|
CategoryLocalDatasource.instance;
|
||||||
|
final CategoryRepository _categoryRepository = CategoryRepository.instance;
|
||||||
|
|
||||||
|
Timer? _progressTimer;
|
||||||
|
bool _isCancelled = false;
|
||||||
|
|
||||||
|
DataSyncBloc() : super(const DataSyncState.initial()) {
|
||||||
|
on<_StartSync>(_onStartSync);
|
||||||
|
on<_CancelSync>(_onCancelSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_progressTimer?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onStartSync(
|
||||||
|
_StartSync event,
|
||||||
|
Emitter<DataSyncState> emit,
|
||||||
|
) async {
|
||||||
|
log('🔄 Starting full data sync (categories + products)...');
|
||||||
|
_isCancelled = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Step 1: Clear existing local data
|
||||||
|
emit(const DataSyncState.syncing(
|
||||||
|
SyncStep.categories, 0.05, 'Membersihkan data lama...'));
|
||||||
|
|
||||||
|
await _productLocalDatasource.clearAllProducts();
|
||||||
|
await _categoryLocalDatasource.clearAllCategories();
|
||||||
|
|
||||||
|
if (_isCancelled) return;
|
||||||
|
|
||||||
|
// Step 2: Sync categories first (products depend on categories)
|
||||||
|
await _syncCategories(emit);
|
||||||
|
|
||||||
|
if (_isCancelled) return;
|
||||||
|
|
||||||
|
// Step 3: Sync products
|
||||||
|
await _syncProducts(emit);
|
||||||
|
|
||||||
|
if (_isCancelled) return;
|
||||||
|
|
||||||
|
// Step 4: Generate final stats
|
||||||
|
emit(const DataSyncState.syncing(
|
||||||
|
SyncStep.completed, 0.95, 'Menyelesaikan sinkronisasi...'));
|
||||||
|
|
||||||
|
final stats = await _generateSyncStats();
|
||||||
|
|
||||||
|
emit(DataSyncState.completed(stats));
|
||||||
|
log('✅ Full sync completed successfully');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Sync failed: $e');
|
||||||
|
emit(DataSyncState.error('Gagal sinkronisasi: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _syncCategories(Emitter<DataSyncState> emit) async {
|
||||||
|
log('📁 Syncing categories...');
|
||||||
|
|
||||||
|
emit(const DataSyncState.syncing(
|
||||||
|
SyncStep.categories,
|
||||||
|
0.1,
|
||||||
|
'Mengunduh kategori...',
|
||||||
|
));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use CategoryRepository sync method
|
||||||
|
final result = await _categoryRepository.syncAllCategories();
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
throw Exception('Gagal sync kategori: $failure');
|
||||||
|
},
|
||||||
|
(successMessage) async {
|
||||||
|
log('✅ Categories sync completed: $successMessage');
|
||||||
|
emit(const DataSyncState.syncing(
|
||||||
|
SyncStep.categories,
|
||||||
|
0.2,
|
||||||
|
'Kategori berhasil diunduh',
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Category sync failed: $e');
|
||||||
|
throw Exception('Gagal sync kategori: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _syncProducts(Emitter<DataSyncState> emit) async {
|
||||||
|
log('📦 Syncing products...');
|
||||||
|
|
||||||
|
int page = 1;
|
||||||
|
int totalSynced = 0;
|
||||||
|
int? totalCount;
|
||||||
|
int? totalPages;
|
||||||
|
bool shouldContinue = true;
|
||||||
|
|
||||||
|
while (!_isCancelled && shouldContinue) {
|
||||||
|
// Calculate accurate progress (categories = 0.2, products = 0.2-0.9)
|
||||||
|
double progress = 0.2;
|
||||||
|
if (totalCount != null && (totalCount ?? 0) > 0) {
|
||||||
|
progress = 0.2 + (totalSynced / (totalCount ?? 0)) * 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(DataSyncState.syncing(
|
||||||
|
SyncStep.products,
|
||||||
|
progress,
|
||||||
|
totalCount != null
|
||||||
|
? 'Mengunduh produk... ($totalSynced dari $totalCount)'
|
||||||
|
: 'Mengunduh produk... ($totalSynced produk)',
|
||||||
|
));
|
||||||
|
|
||||||
|
final result = await _productRemoteDatasource.getProducts(
|
||||||
|
page: page,
|
||||||
|
limit: 50, // Bigger batch for sync
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
throw Exception(failure);
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final products = response.data?.products ?? [];
|
||||||
|
final responseData = response.data;
|
||||||
|
|
||||||
|
// Get pagination info from first response
|
||||||
|
if (page == 1 && responseData != null) {
|
||||||
|
totalCount = responseData.totalCount;
|
||||||
|
totalPages = responseData.totalPages;
|
||||||
|
log('📊 Total products to sync: $totalCount (${totalPages} pages)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (products.isEmpty) {
|
||||||
|
shouldContinue = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to local database in batches
|
||||||
|
await _productLocalDatasource.saveProductsBatch(products);
|
||||||
|
|
||||||
|
totalSynced += products.length;
|
||||||
|
page++;
|
||||||
|
|
||||||
|
log('📦 Synced page ${page - 1}: ${products.length} products (Total: $totalSynced)');
|
||||||
|
|
||||||
|
// Check if we reached the end using pagination info
|
||||||
|
if (totalPages != null && page > (totalPages ?? 0)) {
|
||||||
|
shouldContinue = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback check if pagination info not available
|
||||||
|
if (products.length < 50) {
|
||||||
|
shouldContinue = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small delay to prevent overwhelming the server
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(DataSyncState.syncing(
|
||||||
|
SyncStep.products,
|
||||||
|
0.9,
|
||||||
|
'Produk berhasil diunduh ($totalSynced dari ${totalCount ?? totalSynced})',
|
||||||
|
));
|
||||||
|
|
||||||
|
log('✅ Products sync completed: $totalSynced products synced');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SyncStats> _generateSyncStats() async {
|
||||||
|
final productStats = await _productLocalDatasource.getDatabaseStats();
|
||||||
|
final categoryStats = await _categoryLocalDatasource.getDatabaseStats();
|
||||||
|
|
||||||
|
return SyncStats(
|
||||||
|
totalProducts: productStats['total_products'] ?? 0,
|
||||||
|
totalCategories: categoryStats['total_categories'] ?? 0,
|
||||||
|
totalVariants: productStats['total_variants'] ?? 0,
|
||||||
|
databaseSizeMB: (productStats['database_size_mb'] ?? 0.0) +
|
||||||
|
(categoryStats['database_size_mb'] ?? 0.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onCancelSync(
|
||||||
|
_CancelSync event,
|
||||||
|
Emitter<DataSyncState> emit,
|
||||||
|
) async {
|
||||||
|
log('⏹️ Cancelling sync...');
|
||||||
|
_isCancelled = true;
|
||||||
|
_progressTimer?.cancel();
|
||||||
|
emit(const DataSyncState.initial());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,962 @@
|
|||||||
|
// 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 'data_sync_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 _$DataSyncEvent {
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() startSync,
|
||||||
|
required TResult Function() cancelSync,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? startSync,
|
||||||
|
TResult? Function()? cancelSync,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? startSync,
|
||||||
|
TResult Function()? cancelSync,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_StartSync value) startSync,
|
||||||
|
required TResult Function(_CancelSync value) cancelSync,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_StartSync value)? startSync,
|
||||||
|
TResult? Function(_CancelSync value)? cancelSync,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_StartSync value)? startSync,
|
||||||
|
TResult Function(_CancelSync value)? cancelSync,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DataSyncEventCopyWith<$Res> {
|
||||||
|
factory $DataSyncEventCopyWith(
|
||||||
|
DataSyncEvent value, $Res Function(DataSyncEvent) then) =
|
||||||
|
_$DataSyncEventCopyWithImpl<$Res, DataSyncEvent>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$DataSyncEventCopyWithImpl<$Res, $Val extends DataSyncEvent>
|
||||||
|
implements $DataSyncEventCopyWith<$Res> {
|
||||||
|
_$DataSyncEventCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$StartSyncImplCopyWith<$Res> {
|
||||||
|
factory _$$StartSyncImplCopyWith(
|
||||||
|
_$StartSyncImpl value, $Res Function(_$StartSyncImpl) then) =
|
||||||
|
__$$StartSyncImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$StartSyncImplCopyWithImpl<$Res>
|
||||||
|
extends _$DataSyncEventCopyWithImpl<$Res, _$StartSyncImpl>
|
||||||
|
implements _$$StartSyncImplCopyWith<$Res> {
|
||||||
|
__$$StartSyncImplCopyWithImpl(
|
||||||
|
_$StartSyncImpl _value, $Res Function(_$StartSyncImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$StartSyncImpl implements _StartSync {
|
||||||
|
const _$StartSyncImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DataSyncEvent.startSync()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType && other is _$StartSyncImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() startSync,
|
||||||
|
required TResult Function() cancelSync,
|
||||||
|
}) {
|
||||||
|
return startSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? startSync,
|
||||||
|
TResult? Function()? cancelSync,
|
||||||
|
}) {
|
||||||
|
return startSync?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? startSync,
|
||||||
|
TResult Function()? cancelSync,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (startSync != null) {
|
||||||
|
return startSync();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_StartSync value) startSync,
|
||||||
|
required TResult Function(_CancelSync value) cancelSync,
|
||||||
|
}) {
|
||||||
|
return startSync(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_StartSync value)? startSync,
|
||||||
|
TResult? Function(_CancelSync value)? cancelSync,
|
||||||
|
}) {
|
||||||
|
return startSync?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_StartSync value)? startSync,
|
||||||
|
TResult Function(_CancelSync value)? cancelSync,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (startSync != null) {
|
||||||
|
return startSync(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _StartSync implements DataSyncEvent {
|
||||||
|
const factory _StartSync() = _$StartSyncImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$CancelSyncImplCopyWith<$Res> {
|
||||||
|
factory _$$CancelSyncImplCopyWith(
|
||||||
|
_$CancelSyncImpl value, $Res Function(_$CancelSyncImpl) then) =
|
||||||
|
__$$CancelSyncImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$CancelSyncImplCopyWithImpl<$Res>
|
||||||
|
extends _$DataSyncEventCopyWithImpl<$Res, _$CancelSyncImpl>
|
||||||
|
implements _$$CancelSyncImplCopyWith<$Res> {
|
||||||
|
__$$CancelSyncImplCopyWithImpl(
|
||||||
|
_$CancelSyncImpl _value, $Res Function(_$CancelSyncImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$CancelSyncImpl implements _CancelSync {
|
||||||
|
const _$CancelSyncImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DataSyncEvent.cancelSync()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType && other is _$CancelSyncImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() startSync,
|
||||||
|
required TResult Function() cancelSync,
|
||||||
|
}) {
|
||||||
|
return cancelSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? startSync,
|
||||||
|
TResult? Function()? cancelSync,
|
||||||
|
}) {
|
||||||
|
return cancelSync?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? startSync,
|
||||||
|
TResult Function()? cancelSync,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (cancelSync != null) {
|
||||||
|
return cancelSync();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_StartSync value) startSync,
|
||||||
|
required TResult Function(_CancelSync value) cancelSync,
|
||||||
|
}) {
|
||||||
|
return cancelSync(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_StartSync value)? startSync,
|
||||||
|
TResult? Function(_CancelSync value)? cancelSync,
|
||||||
|
}) {
|
||||||
|
return cancelSync?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_StartSync value)? startSync,
|
||||||
|
TResult Function(_CancelSync value)? cancelSync,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (cancelSync != null) {
|
||||||
|
return cancelSync(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _CancelSync implements DataSyncEvent {
|
||||||
|
const factory _CancelSync() = _$CancelSyncImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$DataSyncState {
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() initial,
|
||||||
|
required TResult Function(SyncStep step, double progress, String message)
|
||||||
|
syncing,
|
||||||
|
required TResult Function(SyncStats stats) completed,
|
||||||
|
required TResult Function(String message) error,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? initial,
|
||||||
|
TResult? Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult? Function(SyncStats stats)? completed,
|
||||||
|
TResult? Function(String message)? error,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? initial,
|
||||||
|
TResult Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult Function(SyncStats stats)? completed,
|
||||||
|
TResult Function(String message)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Initial value) initial,
|
||||||
|
required TResult Function(_Syncing value) syncing,
|
||||||
|
required TResult Function(_Completed value) completed,
|
||||||
|
required TResult Function(_Error value) error,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Initial value)? initial,
|
||||||
|
TResult? Function(_Syncing value)? syncing,
|
||||||
|
TResult? Function(_Completed value)? completed,
|
||||||
|
TResult? Function(_Error value)? error,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Initial value)? initial,
|
||||||
|
TResult Function(_Syncing value)? syncing,
|
||||||
|
TResult Function(_Completed value)? completed,
|
||||||
|
TResult Function(_Error value)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DataSyncStateCopyWith<$Res> {
|
||||||
|
factory $DataSyncStateCopyWith(
|
||||||
|
DataSyncState value, $Res Function(DataSyncState) then) =
|
||||||
|
_$DataSyncStateCopyWithImpl<$Res, DataSyncState>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$DataSyncStateCopyWithImpl<$Res, $Val extends DataSyncState>
|
||||||
|
implements $DataSyncStateCopyWith<$Res> {
|
||||||
|
_$DataSyncStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$InitialImplCopyWith<$Res> {
|
||||||
|
factory _$$InitialImplCopyWith(
|
||||||
|
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
|
||||||
|
__$$InitialImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$InitialImplCopyWithImpl<$Res>
|
||||||
|
extends _$DataSyncStateCopyWithImpl<$Res, _$InitialImpl>
|
||||||
|
implements _$$InitialImplCopyWith<$Res> {
|
||||||
|
__$$InitialImplCopyWithImpl(
|
||||||
|
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$InitialImpl implements _Initial {
|
||||||
|
const _$InitialImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DataSyncState.initial()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType && other is _$InitialImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() initial,
|
||||||
|
required TResult Function(SyncStep step, double progress, String message)
|
||||||
|
syncing,
|
||||||
|
required TResult Function(SyncStats stats) completed,
|
||||||
|
required TResult Function(String message) error,
|
||||||
|
}) {
|
||||||
|
return initial();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? initial,
|
||||||
|
TResult? Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult? Function(SyncStats stats)? completed,
|
||||||
|
TResult? Function(String message)? error,
|
||||||
|
}) {
|
||||||
|
return initial?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? initial,
|
||||||
|
TResult Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult Function(SyncStats stats)? completed,
|
||||||
|
TResult Function(String message)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (initial != null) {
|
||||||
|
return initial();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Initial value) initial,
|
||||||
|
required TResult Function(_Syncing value) syncing,
|
||||||
|
required TResult Function(_Completed value) completed,
|
||||||
|
required TResult Function(_Error value) error,
|
||||||
|
}) {
|
||||||
|
return initial(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Initial value)? initial,
|
||||||
|
TResult? Function(_Syncing value)? syncing,
|
||||||
|
TResult? Function(_Completed value)? completed,
|
||||||
|
TResult? Function(_Error value)? error,
|
||||||
|
}) {
|
||||||
|
return initial?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Initial value)? initial,
|
||||||
|
TResult Function(_Syncing value)? syncing,
|
||||||
|
TResult Function(_Completed value)? completed,
|
||||||
|
TResult Function(_Error value)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (initial != null) {
|
||||||
|
return initial(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Initial implements DataSyncState {
|
||||||
|
const factory _Initial() = _$InitialImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SyncingImplCopyWith<$Res> {
|
||||||
|
factory _$$SyncingImplCopyWith(
|
||||||
|
_$SyncingImpl value, $Res Function(_$SyncingImpl) then) =
|
||||||
|
__$$SyncingImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({SyncStep step, double progress, String message});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SyncingImplCopyWithImpl<$Res>
|
||||||
|
extends _$DataSyncStateCopyWithImpl<$Res, _$SyncingImpl>
|
||||||
|
implements _$$SyncingImplCopyWith<$Res> {
|
||||||
|
__$$SyncingImplCopyWithImpl(
|
||||||
|
_$SyncingImpl _value, $Res Function(_$SyncingImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? step = null,
|
||||||
|
Object? progress = null,
|
||||||
|
Object? message = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$SyncingImpl(
|
||||||
|
null == step
|
||||||
|
? _value.step
|
||||||
|
: step // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SyncStep,
|
||||||
|
null == progress
|
||||||
|
? _value.progress
|
||||||
|
: progress // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,
|
||||||
|
null == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$SyncingImpl implements _Syncing {
|
||||||
|
const _$SyncingImpl(this.step, this.progress, this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final SyncStep step;
|
||||||
|
@override
|
||||||
|
final double progress;
|
||||||
|
@override
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DataSyncState.syncing(step: $step, progress: $progress, message: $message)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SyncingImpl &&
|
||||||
|
(identical(other.step, step) || other.step == step) &&
|
||||||
|
(identical(other.progress, progress) ||
|
||||||
|
other.progress == progress) &&
|
||||||
|
(identical(other.message, message) || other.message == message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, step, progress, message);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SyncingImplCopyWith<_$SyncingImpl> get copyWith =>
|
||||||
|
__$$SyncingImplCopyWithImpl<_$SyncingImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() initial,
|
||||||
|
required TResult Function(SyncStep step, double progress, String message)
|
||||||
|
syncing,
|
||||||
|
required TResult Function(SyncStats stats) completed,
|
||||||
|
required TResult Function(String message) error,
|
||||||
|
}) {
|
||||||
|
return syncing(step, progress, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? initial,
|
||||||
|
TResult? Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult? Function(SyncStats stats)? completed,
|
||||||
|
TResult? Function(String message)? error,
|
||||||
|
}) {
|
||||||
|
return syncing?.call(step, progress, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? initial,
|
||||||
|
TResult Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult Function(SyncStats stats)? completed,
|
||||||
|
TResult Function(String message)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (syncing != null) {
|
||||||
|
return syncing(step, progress, message);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Initial value) initial,
|
||||||
|
required TResult Function(_Syncing value) syncing,
|
||||||
|
required TResult Function(_Completed value) completed,
|
||||||
|
required TResult Function(_Error value) error,
|
||||||
|
}) {
|
||||||
|
return syncing(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Initial value)? initial,
|
||||||
|
TResult? Function(_Syncing value)? syncing,
|
||||||
|
TResult? Function(_Completed value)? completed,
|
||||||
|
TResult? Function(_Error value)? error,
|
||||||
|
}) {
|
||||||
|
return syncing?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Initial value)? initial,
|
||||||
|
TResult Function(_Syncing value)? syncing,
|
||||||
|
TResult Function(_Completed value)? completed,
|
||||||
|
TResult Function(_Error value)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (syncing != null) {
|
||||||
|
return syncing(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Syncing implements DataSyncState {
|
||||||
|
const factory _Syncing(
|
||||||
|
final SyncStep step, final double progress, final String message) =
|
||||||
|
_$SyncingImpl;
|
||||||
|
|
||||||
|
SyncStep get step;
|
||||||
|
double get progress;
|
||||||
|
String get message;
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$SyncingImplCopyWith<_$SyncingImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$CompletedImplCopyWith<$Res> {
|
||||||
|
factory _$$CompletedImplCopyWith(
|
||||||
|
_$CompletedImpl value, $Res Function(_$CompletedImpl) then) =
|
||||||
|
__$$CompletedImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({SyncStats stats});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$CompletedImplCopyWithImpl<$Res>
|
||||||
|
extends _$DataSyncStateCopyWithImpl<$Res, _$CompletedImpl>
|
||||||
|
implements _$$CompletedImplCopyWith<$Res> {
|
||||||
|
__$$CompletedImplCopyWithImpl(
|
||||||
|
_$CompletedImpl _value, $Res Function(_$CompletedImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? stats = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$CompletedImpl(
|
||||||
|
null == stats
|
||||||
|
? _value.stats
|
||||||
|
: stats // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SyncStats,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$CompletedImpl implements _Completed {
|
||||||
|
const _$CompletedImpl(this.stats);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final SyncStats stats;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DataSyncState.completed(stats: $stats)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$CompletedImpl &&
|
||||||
|
(identical(other.stats, stats) || other.stats == stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, stats);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$CompletedImplCopyWith<_$CompletedImpl> get copyWith =>
|
||||||
|
__$$CompletedImplCopyWithImpl<_$CompletedImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() initial,
|
||||||
|
required TResult Function(SyncStep step, double progress, String message)
|
||||||
|
syncing,
|
||||||
|
required TResult Function(SyncStats stats) completed,
|
||||||
|
required TResult Function(String message) error,
|
||||||
|
}) {
|
||||||
|
return completed(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? initial,
|
||||||
|
TResult? Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult? Function(SyncStats stats)? completed,
|
||||||
|
TResult? Function(String message)? error,
|
||||||
|
}) {
|
||||||
|
return completed?.call(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? initial,
|
||||||
|
TResult Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult Function(SyncStats stats)? completed,
|
||||||
|
TResult Function(String message)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (completed != null) {
|
||||||
|
return completed(stats);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Initial value) initial,
|
||||||
|
required TResult Function(_Syncing value) syncing,
|
||||||
|
required TResult Function(_Completed value) completed,
|
||||||
|
required TResult Function(_Error value) error,
|
||||||
|
}) {
|
||||||
|
return completed(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Initial value)? initial,
|
||||||
|
TResult? Function(_Syncing value)? syncing,
|
||||||
|
TResult? Function(_Completed value)? completed,
|
||||||
|
TResult? Function(_Error value)? error,
|
||||||
|
}) {
|
||||||
|
return completed?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Initial value)? initial,
|
||||||
|
TResult Function(_Syncing value)? syncing,
|
||||||
|
TResult Function(_Completed value)? completed,
|
||||||
|
TResult Function(_Error value)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (completed != null) {
|
||||||
|
return completed(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Completed implements DataSyncState {
|
||||||
|
const factory _Completed(final SyncStats stats) = _$CompletedImpl;
|
||||||
|
|
||||||
|
SyncStats get stats;
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$CompletedImplCopyWith<_$CompletedImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$ErrorImplCopyWith<$Res> {
|
||||||
|
factory _$$ErrorImplCopyWith(
|
||||||
|
_$ErrorImpl value, $Res Function(_$ErrorImpl) then) =
|
||||||
|
__$$ErrorImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({String message});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$ErrorImplCopyWithImpl<$Res>
|
||||||
|
extends _$DataSyncStateCopyWithImpl<$Res, _$ErrorImpl>
|
||||||
|
implements _$$ErrorImplCopyWith<$Res> {
|
||||||
|
__$$ErrorImplCopyWithImpl(
|
||||||
|
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? message = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$ErrorImpl(
|
||||||
|
null == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$ErrorImpl implements _Error {
|
||||||
|
const _$ErrorImpl(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DataSyncState.error(message: $message)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$ErrorImpl &&
|
||||||
|
(identical(other.message, message) || other.message == message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, message);
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
|
||||||
|
__$$ErrorImplCopyWithImpl<_$ErrorImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() initial,
|
||||||
|
required TResult Function(SyncStep step, double progress, String message)
|
||||||
|
syncing,
|
||||||
|
required TResult Function(SyncStats stats) completed,
|
||||||
|
required TResult Function(String message) error,
|
||||||
|
}) {
|
||||||
|
return error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? initial,
|
||||||
|
TResult? Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult? Function(SyncStats stats)? completed,
|
||||||
|
TResult? Function(String message)? error,
|
||||||
|
}) {
|
||||||
|
return error?.call(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? initial,
|
||||||
|
TResult Function(SyncStep step, double progress, String message)? syncing,
|
||||||
|
TResult Function(SyncStats stats)? completed,
|
||||||
|
TResult Function(String message)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (error != null) {
|
||||||
|
return error(message);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Initial value) initial,
|
||||||
|
required TResult Function(_Syncing value) syncing,
|
||||||
|
required TResult Function(_Completed value) completed,
|
||||||
|
required TResult Function(_Error value) error,
|
||||||
|
}) {
|
||||||
|
return error(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Initial value)? initial,
|
||||||
|
TResult? Function(_Syncing value)? syncing,
|
||||||
|
TResult? Function(_Completed value)? completed,
|
||||||
|
TResult? Function(_Error value)? error,
|
||||||
|
}) {
|
||||||
|
return error?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Initial value)? initial,
|
||||||
|
TResult Function(_Syncing value)? syncing,
|
||||||
|
TResult Function(_Completed value)? completed,
|
||||||
|
TResult Function(_Error value)? error,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (error != null) {
|
||||||
|
return error(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Error implements DataSyncState {
|
||||||
|
const factory _Error(final String message) = _$ErrorImpl;
|
||||||
|
|
||||||
|
String get message;
|
||||||
|
|
||||||
|
/// Create a copy of DataSyncState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
part of 'data_sync_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class DataSyncEvent with _$DataSyncEvent {
|
||||||
|
const factory DataSyncEvent.startSync() = _StartSync;
|
||||||
|
const factory DataSyncEvent.cancelSync() = _CancelSync;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
part of 'data_sync_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class DataSyncState with _$DataSyncState {
|
||||||
|
const factory DataSyncState.initial() = _Initial;
|
||||||
|
const factory DataSyncState.syncing(
|
||||||
|
SyncStep step,
|
||||||
|
double progress,
|
||||||
|
String message,
|
||||||
|
) = _Syncing;
|
||||||
|
const factory DataSyncState.completed(SyncStats stats) = _Completed;
|
||||||
|
const factory DataSyncState.error(String message) = _Error;
|
||||||
|
}
|
||||||
@@ -0,0 +1,779 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import '../../../core/components/buttons.dart';
|
||||||
|
import '../../../core/constants/colors.dart';
|
||||||
|
import '../bloc/data_sync_bloc.dart';
|
||||||
|
|
||||||
|
class DataSyncPage extends StatefulWidget {
|
||||||
|
const DataSyncPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DataSyncPage> createState() => _DataSyncPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DataSyncPageState extends State<DataSyncPage>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
late AnimationController _animationController;
|
||||||
|
late Animation<double> _progressAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
duration: Duration(milliseconds: 500),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
_progressAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _animationController,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Auto start sync
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
context.read<DataSyncBloc>().add(const DataSyncEvent.startSync());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final isLandscape = mediaQuery.orientation == Orientation.landscape;
|
||||||
|
final screenHeight = mediaQuery.size.height;
|
||||||
|
final screenWidth = mediaQuery.size.width;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade50,
|
||||||
|
body: SafeArea(
|
||||||
|
child: BlocConsumer<DataSyncBloc, DataSyncState>(
|
||||||
|
listener: (context, state) {
|
||||||
|
state.maybeWhen(
|
||||||
|
orElse: () {},
|
||||||
|
syncing: (step, progress, message) {
|
||||||
|
_animationController.animateTo(progress);
|
||||||
|
},
|
||||||
|
completed: (stats) {
|
||||||
|
_animationController.animateTo(1.0);
|
||||||
|
// Navigate to home after delay
|
||||||
|
Future.delayed(Duration(seconds: 2), () {
|
||||||
|
context.pushReplacement(DashboardPage());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: (message) {
|
||||||
|
_animationController.stop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
builder: (context, state) {
|
||||||
|
if (isLandscape) {
|
||||||
|
return _buildLandscapeLayout(state, screenWidth, screenHeight);
|
||||||
|
} else {
|
||||||
|
return _buildPortraitLayout(state, screenHeight);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Portrait layout
|
||||||
|
Widget _buildPortraitLayout(DataSyncState state, double screenHeight) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: screenHeight * 0.08),
|
||||||
|
_buildHeader(false),
|
||||||
|
SizedBox(height: screenHeight * 0.08),
|
||||||
|
Expanded(
|
||||||
|
child: state.when(
|
||||||
|
initial: () => _buildInitialState(false),
|
||||||
|
syncing: (step, progress, message) =>
|
||||||
|
_buildSyncingState(step, progress, message, false),
|
||||||
|
completed: (stats) => _buildCompletedState(stats, false),
|
||||||
|
error: (message) => _buildErrorState(message, false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
_buildActions(state),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Landscape layout
|
||||||
|
Widget _buildLandscapeLayout(
|
||||||
|
DataSyncState state, double screenWidth, double screenHeight) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_buildHeader(true),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
_buildActions(state),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 40),
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Container(
|
||||||
|
height: screenHeight * 0.8,
|
||||||
|
child: state.when(
|
||||||
|
initial: () => _buildInitialState(true),
|
||||||
|
syncing: (step, progress, message) =>
|
||||||
|
_buildSyncingState(step, progress, message, true),
|
||||||
|
completed: (stats) => _buildCompletedState(stats, true),
|
||||||
|
error: (message) => _buildErrorState(message, true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(bool isLandscape) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: isLandscape ? 60 : 80,
|
||||||
|
height: isLandscape ? 60 : 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.primary.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(isLandscape ? 15 : 20),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.sync,
|
||||||
|
size: isLandscape ? 30 : 40,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
Text(
|
||||||
|
'Sinkronisasi Data',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 20 : 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 4 : 8),
|
||||||
|
Text(
|
||||||
|
'Mengunduh kategori dan produk terbaru',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 14 : 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInitialState(bool isLandscape) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.download_rounded,
|
||||||
|
size: isLandscape ? 48 : 64,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
Text(
|
||||||
|
'Siap untuk sinkronisasi',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 16 : 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 4 : 8),
|
||||||
|
Text(
|
||||||
|
'Tekan tombol mulai untuk mengunduh data',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSyncingState(
|
||||||
|
SyncStep step, double progress, String message, bool isLandscape) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Progress circle
|
||||||
|
Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: isLandscape ? 100 : 120,
|
||||||
|
height: isLandscape ? 100 : 120,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _progressAnimation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
value: _progressAnimation.value,
|
||||||
|
strokeWidth: isLandscape ? 6 : 8,
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
valueColor:
|
||||||
|
AlwaysStoppedAnimation<Color>(AppColors.primary),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
_getSyncIcon(step),
|
||||||
|
size: isLandscape ? 24 : 32,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
SizedBox(height: 2),
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _progressAnimation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return Text(
|
||||||
|
'${(_progressAnimation.value * 100).toInt()}%',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 14 : 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 20 : 30),
|
||||||
|
|
||||||
|
// Step indicator
|
||||||
|
_buildStepIndicator(step, isLandscape),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
|
||||||
|
// Current message
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: isLandscape ? 16 : 20,
|
||||||
|
vertical: isLandscape ? 8 : 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.blue.shade50,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
message,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.blue.shade700,
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
|
||||||
|
// Sync details
|
||||||
|
_buildSyncDetails(step, progress, isLandscape),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStepIndicator(SyncStep currentStep, bool isLandscape) {
|
||||||
|
final steps = [
|
||||||
|
('Kategori', SyncStep.categories, Icons.category),
|
||||||
|
('Produk', SyncStep.products, Icons.inventory_2),
|
||||||
|
('Variant', SyncStep.variants, Icons.tune),
|
||||||
|
('Selesai', SyncStep.completed, Icons.check_circle),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isLandscape) {
|
||||||
|
// Vertical layout for landscape
|
||||||
|
return Column(
|
||||||
|
children: steps.map((stepData) {
|
||||||
|
final (label, step, icon) = stepData;
|
||||||
|
final isActive = step == currentStep;
|
||||||
|
final isCompleted = step.index < currentStep.index;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 2),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isActive
|
||||||
|
? AppColors.primary.withOpacity(0.1)
|
||||||
|
: isCompleted
|
||||||
|
? Colors.green.shade50
|
||||||
|
: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
isCompleted ? Icons.check : icon,
|
||||||
|
size: 12,
|
||||||
|
color: isActive
|
||||||
|
? AppColors.primary
|
||||||
|
: isCompleted
|
||||||
|
? Colors.green.shade600
|
||||||
|
: Colors.grey.shade500,
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
|
||||||
|
color: isActive
|
||||||
|
? AppColors.primary
|
||||||
|
: isCompleted
|
||||||
|
? Colors.green.shade600
|
||||||
|
: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Horizontal layout for portrait
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: steps.map((stepData) {
|
||||||
|
final (label, step, icon) = stepData;
|
||||||
|
final isActive = step == currentStep;
|
||||||
|
final isCompleted = step.index < currentStep.index;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isActive
|
||||||
|
? AppColors.primary.withOpacity(0.1)
|
||||||
|
: isCompleted
|
||||||
|
? Colors.green.shade50
|
||||||
|
: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
isCompleted ? Icons.check : icon,
|
||||||
|
size: 14,
|
||||||
|
color: isActive
|
||||||
|
? AppColors.primary
|
||||||
|
: isCompleted
|
||||||
|
? Colors.green.shade600
|
||||||
|
: Colors.grey.shade500,
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
|
||||||
|
color: isActive
|
||||||
|
? AppColors.primary
|
||||||
|
: isCompleted
|
||||||
|
? Colors.green.shade600
|
||||||
|
: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSyncDetails(SyncStep step, double progress, bool isLandscape) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(isLandscape ? 12 : 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: Colors.grey.shade200),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Status:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_getStepLabel(step),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 6 : 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Progress:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${(progress * 100).toInt()}%',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCompletedState(SyncStats stats, bool isLandscape) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Success icon
|
||||||
|
Container(
|
||||||
|
width: isLandscape ? 80 : 100,
|
||||||
|
height: isLandscape ? 80 : 100,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.green.shade50,
|
||||||
|
borderRadius: BorderRadius.circular(isLandscape ? 40 : 50),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
size: isLandscape ? 48 : 60,
|
||||||
|
color: Colors.green.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 20 : 30),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
'Sinkronisasi Berhasil!',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 18 : 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 8 : 16),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
'Data berhasil diunduh ke perangkat',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 14 : 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 20 : 30),
|
||||||
|
|
||||||
|
// Stats cards
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(isLandscape ? 16 : 20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: Colors.grey.shade200),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Data yang Diunduh',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 14 : 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 12 : 16),
|
||||||
|
if (isLandscape)
|
||||||
|
// Vertical layout for landscape
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
_buildStatItem('Kategori', '${stats.totalCategories}',
|
||||||
|
Icons.category, Colors.blue, isLandscape),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
_buildStatItem('Produk', '${stats.totalProducts}',
|
||||||
|
Icons.inventory_2, Colors.green, isLandscape),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
_buildStatItem('Variant', '${stats.totalVariants}',
|
||||||
|
Icons.tune, Colors.orange, isLandscape),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
// Horizontal layout for portrait
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildStatItem('Kategori', '${stats.totalCategories}',
|
||||||
|
Icons.category, Colors.blue, isLandscape),
|
||||||
|
_buildStatItem('Produk', '${stats.totalProducts}',
|
||||||
|
Icons.inventory_2, Colors.green, isLandscape),
|
||||||
|
_buildStatItem('Variant', '${stats.totalVariants}',
|
||||||
|
Icons.tune, Colors.orange, isLandscape),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Mengalihkan ke halaman utama...',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
fontSize: isLandscape ? 10 : 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildErrorState(String message, bool isLandscape) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.error_outline,
|
||||||
|
size: isLandscape ? 48 : 64,
|
||||||
|
color: Colors.red.shade400,
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
Text(
|
||||||
|
'Sinkronisasi Gagal',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 16 : 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 8 : 12),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(isLandscape ? 12 : 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red.shade50,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
message,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
color: Colors.red.shade700,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: isLandscape ? 12 : 20),
|
||||||
|
Text(
|
||||||
|
'Periksa koneksi internet dan coba lagi',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: isLandscape ? 12 : 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatItem(String label, String value, IconData icon, Color color,
|
||||||
|
bool isLandscape) {
|
||||||
|
if (isLandscape) {
|
||||||
|
// Horizontal layout for landscape
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(icon, size: 20, color: color),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Vertical layout for portrait
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Icon(icon, size: 24, color: color),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildActions(DataSyncState state) {
|
||||||
|
return state.when(
|
||||||
|
initial: () => Button.filled(
|
||||||
|
onPressed: () {
|
||||||
|
context.read<DataSyncBloc>().add(const DataSyncEvent.startSync());
|
||||||
|
},
|
||||||
|
label: 'Mulai Sinkronisasi',
|
||||||
|
),
|
||||||
|
syncing: (step, progress, message) => Button.outlined(
|
||||||
|
onPressed: () {
|
||||||
|
context.read<DataSyncBloc>().add(const DataSyncEvent.cancelSync());
|
||||||
|
},
|
||||||
|
label: 'Batalkan',
|
||||||
|
),
|
||||||
|
completed: (stats) => Button.filled(
|
||||||
|
onPressed: () {
|
||||||
|
context.pushReplacement(DashboardPage());
|
||||||
|
},
|
||||||
|
label: 'Lanjutkan ke Aplikasi',
|
||||||
|
),
|
||||||
|
error: (message) => Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Button.outlined(
|
||||||
|
onPressed: () {
|
||||||
|
context.pushReplacement(DashboardPage());
|
||||||
|
},
|
||||||
|
label: 'Lewati',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Button.filled(
|
||||||
|
onPressed: () {
|
||||||
|
context
|
||||||
|
.read<DataSyncBloc>()
|
||||||
|
.add(const DataSyncEvent.startSync());
|
||||||
|
},
|
||||||
|
label: 'Coba Lagi',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconData _getSyncIcon(SyncStep step) {
|
||||||
|
switch (step) {
|
||||||
|
case SyncStep.categories:
|
||||||
|
return Icons.category;
|
||||||
|
case SyncStep.products:
|
||||||
|
return Icons.inventory_2;
|
||||||
|
case SyncStep.variants:
|
||||||
|
return Icons.tune;
|
||||||
|
case SyncStep.completed:
|
||||||
|
return Icons.check_circle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getStepLabel(SyncStep step) {
|
||||||
|
switch (step) {
|
||||||
|
case SyncStep.categories:
|
||||||
|
return 'Mengunduh Kategori';
|
||||||
|
case SyncStep.products:
|
||||||
|
return 'Mengunduh Produk';
|
||||||
|
case SyncStep.variants:
|
||||||
|
return 'Mengunduh Variant';
|
||||||
|
case SyncStep.completed:
|
||||||
|
return 'Selesai';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'dart:async';
|
||||||
import 'package:enaklo_pos/data/datasources/category_remote_datasource.dart';
|
import 'dart:developer';
|
||||||
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/repositories/category/category_repository.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'category_loader_event.dart';
|
part 'category_loader_event.dart';
|
||||||
@@ -9,35 +11,314 @@ part 'category_loader_bloc.freezed.dart';
|
|||||||
|
|
||||||
class CategoryLoaderBloc
|
class CategoryLoaderBloc
|
||||||
extends Bloc<CategoryLoaderEvent, CategoryLoaderState> {
|
extends Bloc<CategoryLoaderEvent, CategoryLoaderState> {
|
||||||
final CategoryRemoteDatasource _datasource;
|
final CategoryRepository _categoryRepository = CategoryRepository.instance;
|
||||||
CategoryLoaderBloc(this._datasource) : super(CategoryLoaderState.initial()) {
|
|
||||||
on<_Get>((event, emit) async {
|
Timer? _searchDebounce;
|
||||||
emit(const _Loading());
|
bool _isLoadingMore = false;
|
||||||
final result = await _datasource.getCategories(limit: 50);
|
|
||||||
result.fold(
|
CategoryLoaderBloc() : super(const CategoryLoaderState.initial()) {
|
||||||
(l) => emit(_Error(l)),
|
on<_GetCategories>(_onGetCategories);
|
||||||
(r) async {
|
on<_LoadMore>(_onLoadMore);
|
||||||
List<CategoryModel> categories = r.data.categories;
|
on<_Refresh>(_onRefresh);
|
||||||
categories.insert(
|
on<_Search>(_onSearch);
|
||||||
0,
|
on<_SyncAll>(_onSyncAll);
|
||||||
CategoryModel(
|
on<_GetAllCategories>(_onGetAllCategories);
|
||||||
id: "",
|
on<_ClearCache>(_onClearCache);
|
||||||
name: 'Semua',
|
on<_GetDatabaseStats>(_onGetDatabaseStats);
|
||||||
organizationId: '',
|
}
|
||||||
businessType: '',
|
|
||||||
metadata: {},
|
@override
|
||||||
createdAt: DateTime.now(),
|
Future<void> close() {
|
||||||
updatedAt: DateTime.now(),
|
_searchDebounce?.cancel();
|
||||||
),
|
return super.close();
|
||||||
);
|
}
|
||||||
emit(_Loaded(categories, null));
|
|
||||||
|
// ========================================
|
||||||
|
// GET CATEGORIES (Remote-first with local fallback)
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onGetCategories(
|
||||||
|
_GetCategories event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
emit(const CategoryLoaderState.loading());
|
||||||
|
_isLoadingMore = false;
|
||||||
|
|
||||||
|
log('📱 Loading categories - isActive: ${event.isActive}, forceRemote: ${event.forceRemote}');
|
||||||
|
|
||||||
|
final result = await _categoryRepository.getCategories(
|
||||||
|
page: 1,
|
||||||
|
limit: 50,
|
||||||
|
isActive: event.isActive,
|
||||||
|
search: event.search,
|
||||||
|
forceRemote: event.forceRemote,
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Error loading categories: $failure');
|
||||||
|
emit(CategoryLoaderState.error(failure));
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final categories = [
|
||||||
|
CategoryModel.all(),
|
||||||
|
...response.data.categories,
|
||||||
|
];
|
||||||
|
|
||||||
|
final totalPages = response.data.totalPages;
|
||||||
|
final hasReachedMax = categories.length < 50 || 1 >= totalPages;
|
||||||
|
|
||||||
|
log('✅ Categories loaded: ${categories.length}, hasReachedMax: $hasReachedMax');
|
||||||
|
|
||||||
|
emit(CategoryLoaderState.loaded(
|
||||||
|
categories: categories,
|
||||||
|
hasReachedMax: hasReachedMax,
|
||||||
|
currentPage: 1,
|
||||||
|
isLoadingMore: false,
|
||||||
|
isActive: event.isActive,
|
||||||
|
searchQuery: event.search,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// LOAD MORE CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onLoadMore(
|
||||||
|
_LoadMore event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
final currentState = state;
|
||||||
|
|
||||||
|
if (currentState is! _Loaded ||
|
||||||
|
currentState.hasReachedMax ||
|
||||||
|
_isLoadingMore ||
|
||||||
|
currentState.isLoadingMore) {
|
||||||
|
log('⏹️ Load more blocked - state: ${currentState.runtimeType}, isLoadingMore: $_isLoadingMore');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLoadingMore = true;
|
||||||
|
emit(currentState.copyWith(isLoadingMore: true));
|
||||||
|
|
||||||
|
final nextPage = currentState.currentPage + 1;
|
||||||
|
log('📄 Loading more categories - page: $nextPage');
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = await _categoryRepository.getCategories(
|
||||||
|
page: nextPage,
|
||||||
|
limit: 10,
|
||||||
|
isActive: currentState.isActive,
|
||||||
|
search: currentState.searchQuery,
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Error loading more categories: $failure');
|
||||||
|
emit(currentState.copyWith(isLoadingMore: false));
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final newCategories = response.data.categories;
|
||||||
|
final totalPages = response.data.totalPages;
|
||||||
|
|
||||||
|
// Prevent duplicate categories
|
||||||
|
final currentCategoryIds =
|
||||||
|
currentState.categories.map((c) => c.id).toSet();
|
||||||
|
final filteredNewCategories = newCategories
|
||||||
|
.where((category) => !currentCategoryIds.contains(category.id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final allCategories =
|
||||||
|
List<CategoryModel>.from(currentState.categories)
|
||||||
|
..addAll(filteredNewCategories);
|
||||||
|
|
||||||
|
final hasReachedMax =
|
||||||
|
newCategories.length < 10 || nextPage >= totalPages;
|
||||||
|
|
||||||
|
log('✅ More categories loaded: ${filteredNewCategories.length} new, total: ${allCategories.length}');
|
||||||
|
|
||||||
|
emit(CategoryLoaderState.loaded(
|
||||||
|
categories: allCategories,
|
||||||
|
hasReachedMax: hasReachedMax,
|
||||||
|
currentPage: nextPage,
|
||||||
|
isLoadingMore: false,
|
||||||
|
isActive: currentState.isActive,
|
||||||
|
searchQuery: currentState.searchQuery,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Exception loading more categories: $e');
|
||||||
|
emit(currentState.copyWith(isLoadingMore: false));
|
||||||
|
} finally {
|
||||||
|
_isLoadingMore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// REFRESH CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onRefresh(
|
||||||
|
_Refresh event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
final currentState = state;
|
||||||
|
bool isActive = true;
|
||||||
|
String? searchQuery;
|
||||||
|
|
||||||
|
if (currentState is _Loaded) {
|
||||||
|
isActive = currentState.isActive;
|
||||||
|
searchQuery = currentState.searchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLoadingMore = false;
|
||||||
|
_searchDebounce?.cancel();
|
||||||
|
|
||||||
|
log('🔄 Refreshing categories');
|
||||||
|
|
||||||
|
// Clear local cache
|
||||||
|
_categoryRepository.clearCache();
|
||||||
|
|
||||||
|
add(CategoryLoaderEvent.getCategories(
|
||||||
|
isActive: isActive,
|
||||||
|
search: searchQuery,
|
||||||
|
forceRemote: true, // Force remote refresh
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// SEARCH CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onSearch(
|
||||||
|
_Search event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
// Cancel previous search
|
||||||
|
_searchDebounce?.cancel();
|
||||||
|
|
||||||
|
// Debounce search for better UX
|
||||||
|
_searchDebounce = Timer(Duration(milliseconds: 300), () async {
|
||||||
|
emit(const CategoryLoaderState.loading());
|
||||||
|
_isLoadingMore = false;
|
||||||
|
|
||||||
|
log('🔍 Searching categories: "${event.query}"');
|
||||||
|
|
||||||
|
final result = await _categoryRepository.getCategories(
|
||||||
|
page: 1,
|
||||||
|
limit: 20, // More results for search
|
||||||
|
isActive: event.isActive,
|
||||||
|
search: event.query,
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Search error: $failure');
|
||||||
|
emit(CategoryLoaderState.error(failure));
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final categories = response.data.categories;
|
||||||
|
final totalPages = response.data.totalPages;
|
||||||
|
final hasReachedMax = categories.length < 20 || 1 >= totalPages;
|
||||||
|
|
||||||
|
log('✅ Search results: ${categories.length} categories found');
|
||||||
|
|
||||||
|
emit(CategoryLoaderState.loaded(
|
||||||
|
categories: categories,
|
||||||
|
hasReachedMax: hasReachedMax,
|
||||||
|
currentPage: 1,
|
||||||
|
isLoadingMore: false,
|
||||||
|
isActive: event.isActive,
|
||||||
|
searchQuery: event.query,
|
||||||
|
));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
on<_SetCategoryId>((event, emit) async {
|
}
|
||||||
var currentState = state as _Loaded;
|
|
||||||
|
|
||||||
emit(_Loaded(currentState.categories, event.categoryId));
|
// ========================================
|
||||||
});
|
// SYNC ALL CATEGORIES
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onSyncAll(
|
||||||
|
_SyncAll event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
emit(const CategoryLoaderState.syncing());
|
||||||
|
|
||||||
|
log('🔄 Starting full category sync...');
|
||||||
|
|
||||||
|
final result = await _categoryRepository.syncAllCategories();
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Sync failed: $failure');
|
||||||
|
emit(CategoryLoaderState.syncError(failure));
|
||||||
|
|
||||||
|
// After sync error, try to load local data
|
||||||
|
Timer(Duration(seconds: 2), () {
|
||||||
|
add(const CategoryLoaderEvent.getCategories());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(successMessage) async {
|
||||||
|
log('✅ Sync completed: $successMessage');
|
||||||
|
emit(CategoryLoaderState.syncSuccess(successMessage));
|
||||||
|
|
||||||
|
// After successful sync, load the updated data
|
||||||
|
Timer(Duration(seconds: 1), () {
|
||||||
|
add(const CategoryLoaderEvent.getCategories());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// GET ALL CATEGORIES (For Dropdown)
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onGetAllCategories(
|
||||||
|
_GetAllCategories event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
log('📋 Loading all categories for dropdown...');
|
||||||
|
|
||||||
|
final categories = await _categoryRepository.getAllCategories();
|
||||||
|
|
||||||
|
emit(CategoryLoaderState.allCategoriesLoaded(categories));
|
||||||
|
log('✅ All categories loaded: ${categories.length}');
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error loading all categories: $e');
|
||||||
|
emit(CategoryLoaderState.error('Gagal memuat semua kategori: $e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// GET DATABASE STATS
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onGetDatabaseStats(
|
||||||
|
_GetDatabaseStats event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final stats = await _categoryRepository.getDatabaseStats();
|
||||||
|
log('📊 Category database stats retrieved: $stats');
|
||||||
|
|
||||||
|
// You can emit a special state here if needed for UI updates
|
||||||
|
// For now, just log the stats
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting category database stats: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// CLEAR CACHE
|
||||||
|
// ========================================
|
||||||
|
Future<void> _onClearCache(
|
||||||
|
_ClearCache event,
|
||||||
|
Emitter<CategoryLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
log('🧹 Manually clearing category cache');
|
||||||
|
_categoryRepository.clearCache();
|
||||||
|
|
||||||
|
// Refresh current data after cache clear
|
||||||
|
add(const CategoryLoaderEvent.refresh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,26 @@ part of 'category_loader_bloc.dart';
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class CategoryLoaderEvent with _$CategoryLoaderEvent {
|
class CategoryLoaderEvent with _$CategoryLoaderEvent {
|
||||||
const factory CategoryLoaderEvent.get() = _Get;
|
const factory CategoryLoaderEvent.getCategories({
|
||||||
const factory CategoryLoaderEvent.setCategoryId(String categoryId) =
|
@Default(true) bool isActive,
|
||||||
_SetCategoryId;
|
String? search,
|
||||||
|
@Default(false) bool forceRemote,
|
||||||
|
}) = _GetCategories;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.loadMore() = _LoadMore;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.refresh() = _Refresh;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.search({
|
||||||
|
required String query,
|
||||||
|
@Default(true) bool isActive,
|
||||||
|
}) = _Search;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.syncAll() = _SyncAll;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.getAllCategories() = _GetAllCategories;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.getDatabaseStats() = _GetDatabaseStats;
|
||||||
|
|
||||||
|
const factory CategoryLoaderEvent.clearCache() = _ClearCache;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,29 @@ part of 'category_loader_bloc.dart';
|
|||||||
@freezed
|
@freezed
|
||||||
class CategoryLoaderState with _$CategoryLoaderState {
|
class CategoryLoaderState with _$CategoryLoaderState {
|
||||||
const factory CategoryLoaderState.initial() = _Initial;
|
const factory CategoryLoaderState.initial() = _Initial;
|
||||||
|
|
||||||
const factory CategoryLoaderState.loading() = _Loading;
|
const factory CategoryLoaderState.loading() = _Loading;
|
||||||
const factory CategoryLoaderState.loaded(
|
|
||||||
List<CategoryModel> categories, String? categoryId) = _Loaded;
|
const factory CategoryLoaderState.loaded({
|
||||||
|
required List<CategoryModel> categories,
|
||||||
|
required bool hasReachedMax,
|
||||||
|
required int currentPage,
|
||||||
|
required bool isLoadingMore,
|
||||||
|
required bool isActive,
|
||||||
|
String? searchQuery,
|
||||||
|
}) = _Loaded;
|
||||||
|
|
||||||
const factory CategoryLoaderState.error(String message) = _Error;
|
const factory CategoryLoaderState.error(String message) = _Error;
|
||||||
|
|
||||||
|
// Sync-specific states
|
||||||
|
const factory CategoryLoaderState.syncing() = _Syncing;
|
||||||
|
|
||||||
|
const factory CategoryLoaderState.syncSuccess(String message) = _SyncSuccess;
|
||||||
|
|
||||||
|
const factory CategoryLoaderState.syncError(String message) = _SyncError;
|
||||||
|
|
||||||
|
// For dropdown/all categories
|
||||||
|
const factory CategoryLoaderState.allCategoriesLoaded(
|
||||||
|
List<CategoryModel> categories,
|
||||||
|
) = _AllCategoriesLoaded;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/delivery_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/discount_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/discount_response_model.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
|
import 'package:enaklo_pos/presentation/table/models/draft_order_item.dart';
|
||||||
@@ -131,7 +130,7 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
|
|||||||
final serviceCharge = await settingsLocalDatasource.getServiceCharge();
|
final serviceCharge = await settingsLocalDatasource.getServiceCharge();
|
||||||
|
|
||||||
emit(_Loaded(
|
emit(_Loaded(
|
||||||
[],
|
event.items,
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -310,9 +309,9 @@ class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
|
|||||||
DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()),
|
DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()),
|
||||||
);
|
);
|
||||||
log("draftOrder12: ${draftOrder.toMapForLocal()}");
|
log("draftOrder12: ${draftOrder.toMapForLocal()}");
|
||||||
final orderDraftId =
|
// final orderDraftId =
|
||||||
await ProductLocalDatasource.instance.saveDraftOrder(draftOrder);
|
// await ProductLocalDatasource.instance.saveDraftOrder(draftOrder);
|
||||||
emit(_SavedDraftOrder(orderDraftId));
|
emit(_SavedDraftOrder(0));
|
||||||
});
|
});
|
||||||
|
|
||||||
//load draft order
|
//load draft order
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
|||||||
mixin _$CheckoutEvent {
|
mixin _$CheckoutEvent {
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -41,7 +41,7 @@ mixin _$CheckoutEvent {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -61,7 +61,7 @@ mixin _$CheckoutEvent {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -166,6 +166,8 @@ abstract class _$$StartedImplCopyWith<$Res> {
|
|||||||
factory _$$StartedImplCopyWith(
|
factory _$$StartedImplCopyWith(
|
||||||
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
|
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
|
||||||
__$$StartedImplCopyWithImpl<$Res>;
|
__$$StartedImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({List<ProductQuantity> items});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -178,31 +180,62 @@ class __$$StartedImplCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of CheckoutEvent
|
/// Create a copy of CheckoutEvent
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? items = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$StartedImpl(
|
||||||
|
null == items
|
||||||
|
? _value._items
|
||||||
|
: items // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<ProductQuantity>,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$StartedImpl implements _Started {
|
class _$StartedImpl implements _Started {
|
||||||
const _$StartedImpl();
|
const _$StartedImpl(final List<ProductQuantity> items) : _items = items;
|
||||||
|
|
||||||
|
final List<ProductQuantity> _items;
|
||||||
|
@override
|
||||||
|
List<ProductQuantity> get items {
|
||||||
|
if (_items is EqualUnmodifiableListView) return _items;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_items);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'CheckoutEvent.started()';
|
return 'CheckoutEvent.started(items: $items)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType && other is _$StartedImpl);
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$StartedImpl &&
|
||||||
|
const DeepCollectionEquality().equals(other._items, _items));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, const DeepCollectionEquality().hash(_items));
|
||||||
|
|
||||||
|
/// Create a copy of CheckoutEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$StartedImplCopyWith<_$StartedImpl> get copyWith =>
|
||||||
|
__$$StartedImplCopyWithImpl<_$StartedImpl>(this, _$identity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -222,13 +255,13 @@ class _$StartedImpl implements _Started {
|
|||||||
required TResult Function(DraftOrderModel data) loadDraftOrder,
|
required TResult Function(DraftOrderModel data) loadDraftOrder,
|
||||||
required TResult Function(DeliveryModel delivery) updateDeliveryType,
|
required TResult Function(DeliveryModel delivery) updateDeliveryType,
|
||||||
}) {
|
}) {
|
||||||
return started();
|
return started(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -245,13 +278,13 @@ class _$StartedImpl implements _Started {
|
|||||||
TResult? Function(DraftOrderModel data)? loadDraftOrder,
|
TResult? Function(DraftOrderModel data)? loadDraftOrder,
|
||||||
TResult? Function(DeliveryModel delivery)? updateDeliveryType,
|
TResult? Function(DeliveryModel delivery)? updateDeliveryType,
|
||||||
}) {
|
}) {
|
||||||
return started?.call();
|
return started?.call(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -270,7 +303,7 @@ class _$StartedImpl implements _Started {
|
|||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (started != null) {
|
||||||
return started();
|
return started(items);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
@@ -347,7 +380,15 @@ class _$StartedImpl implements _Started {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Started implements CheckoutEvent {
|
abstract class _Started implements CheckoutEvent {
|
||||||
const factory _Started() = _$StartedImpl;
|
const factory _Started(final List<ProductQuantity> items) = _$StartedImpl;
|
||||||
|
|
||||||
|
List<ProductQuantity> get items;
|
||||||
|
|
||||||
|
/// Create a copy of CheckoutEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$StartedImplCopyWith<_$StartedImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -426,7 +467,7 @@ class _$AddItemImpl implements _AddItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -452,7 +493,7 @@ class _$AddItemImpl implements _AddItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -475,7 +516,7 @@ class _$AddItemImpl implements _AddItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -660,7 +701,7 @@ class _$RemoveItemImpl implements _RemoveItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -686,7 +727,7 @@ class _$RemoveItemImpl implements _RemoveItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -709,7 +750,7 @@ class _$RemoveItemImpl implements _RemoveItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -894,7 +935,7 @@ class _$DeleteItemImpl implements _DeleteItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -920,7 +961,7 @@ class _$DeleteItemImpl implements _DeleteItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -943,7 +984,7 @@ class _$DeleteItemImpl implements _DeleteItem {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1121,7 +1162,7 @@ class _$AddDiscountImpl implements _AddDiscount {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -1147,7 +1188,7 @@ class _$AddDiscountImpl implements _AddDiscount {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1170,7 +1211,7 @@ class _$AddDiscountImpl implements _AddDiscount {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1318,7 +1359,7 @@ class _$RemoveDiscountImpl implements _RemoveDiscount {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -1344,7 +1385,7 @@ class _$RemoveDiscountImpl implements _RemoveDiscount {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1367,7 +1408,7 @@ class _$RemoveDiscountImpl implements _RemoveDiscount {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1534,7 +1575,7 @@ class _$AddTaxImpl implements _AddTax {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -1560,7 +1601,7 @@ class _$AddTaxImpl implements _AddTax {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1583,7 +1624,7 @@ class _$AddTaxImpl implements _AddTax {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1760,7 +1801,7 @@ class _$AddServiceChargeImpl implements _AddServiceCharge {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -1786,7 +1827,7 @@ class _$AddServiceChargeImpl implements _AddServiceCharge {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1809,7 +1850,7 @@ class _$AddServiceChargeImpl implements _AddServiceCharge {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -1958,7 +1999,7 @@ class _$RemoveTaxImpl implements _RemoveTax {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -1984,7 +2025,7 @@ class _$RemoveTaxImpl implements _RemoveTax {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2007,7 +2048,7 @@ class _$RemoveTaxImpl implements _RemoveTax {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2148,7 +2189,7 @@ class _$RemoveServiceChargeImpl implements _RemoveServiceCharge {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -2174,7 +2215,7 @@ class _$RemoveServiceChargeImpl implements _RemoveServiceCharge {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2197,7 +2238,7 @@ class _$RemoveServiceChargeImpl implements _RemoveServiceCharge {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2366,7 +2407,7 @@ class _$UpdateOrderTypeImpl implements _UpdateOrderType {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -2392,7 +2433,7 @@ class _$UpdateOrderTypeImpl implements _UpdateOrderType {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2415,7 +2456,7 @@ class _$UpdateOrderTypeImpl implements _UpdateOrderType {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2600,7 +2641,7 @@ class _$UpdateItemNotesImpl implements _UpdateItemNotes {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -2626,7 +2667,7 @@ class _$UpdateItemNotesImpl implements _UpdateItemNotes {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2649,7 +2690,7 @@ class _$UpdateItemNotesImpl implements _UpdateItemNotes {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2848,7 +2889,7 @@ class _$SaveDraftOrderImpl implements _SaveDraftOrder {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -2874,7 +2915,7 @@ class _$SaveDraftOrderImpl implements _SaveDraftOrder {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -2897,7 +2938,7 @@ class _$SaveDraftOrderImpl implements _SaveDraftOrder {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -3076,7 +3117,7 @@ class _$LoadDraftOrderImpl implements _LoadDraftOrder {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -3102,7 +3143,7 @@ class _$LoadDraftOrderImpl implements _LoadDraftOrder {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -3125,7 +3166,7 @@ class _$LoadDraftOrderImpl implements _LoadDraftOrder {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -3303,7 +3344,7 @@ class _$UpdateDeliveryTypeImpl implements _UpdateDeliveryType {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(List<ProductQuantity> items) started,
|
||||||
required TResult Function(Product product, ProductVariant? variant) addItem,
|
required TResult Function(Product product, ProductVariant? variant) addItem,
|
||||||
required TResult Function(Product product, ProductVariant? variant)
|
required TResult Function(Product product, ProductVariant? variant)
|
||||||
removeItem,
|
removeItem,
|
||||||
@@ -3329,7 +3370,7 @@ class _$UpdateDeliveryTypeImpl implements _UpdateDeliveryType {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(List<ProductQuantity> items)? started,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
TResult? Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
TResult? Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult? Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
@@ -3352,7 +3393,7 @@ class _$UpdateDeliveryTypeImpl implements _UpdateDeliveryType {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(List<ProductQuantity> items)? started,
|
||||||
TResult Function(Product product, ProductVariant? variant)? addItem,
|
TResult Function(Product product, ProductVariant? variant)? addItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
TResult Function(Product product, ProductVariant? variant)? removeItem,
|
||||||
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
TResult Function(Product product, ProductVariant? variant)? deleteItem,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ part of 'checkout_bloc.dart';
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class CheckoutEvent with _$CheckoutEvent {
|
class CheckoutEvent with _$CheckoutEvent {
|
||||||
const factory CheckoutEvent.started() = _Started;
|
const factory CheckoutEvent.started(List<ProductQuantity> items) = _Started;
|
||||||
//add item
|
//add item
|
||||||
const factory CheckoutEvent.addItem(
|
const factory CheckoutEvent.addItem(
|
||||||
Product product, ProductVariant? variant) = _AddItem;
|
Product product, ProductVariant? variant) = _AddItem;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_remote_data_source.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
|
|
||||||
import '../../../../data/models/response/product_response_model.dart';
|
|
||||||
|
|
||||||
part 'local_product_bloc.freezed.dart';
|
|
||||||
part 'local_product_event.dart';
|
|
||||||
part 'local_product_state.dart';
|
|
||||||
|
|
||||||
class LocalProductBloc extends Bloc<LocalProductEvent, LocalProductState> {
|
|
||||||
final ProductLocalDatasource productLocalDatasource;
|
|
||||||
LocalProductBloc(
|
|
||||||
this.productLocalDatasource,
|
|
||||||
) : super(const _Initial()) {
|
|
||||||
on<_GetLocalProduct>((event, emit) async {
|
|
||||||
emit(const _Loading());
|
|
||||||
final result = await productLocalDatasource.getProducts();
|
|
||||||
log("Result: ${result.length}");
|
|
||||||
emit(_Loaded(result));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
part of 'local_product_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class LocalProductEvent with _$LocalProductEvent {
|
|
||||||
const factory LocalProductEvent.started() = _Started;
|
|
||||||
const factory LocalProductEvent.getLocalProduct() = _GetLocalProduct;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
part of 'local_product_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class LocalProductState with _$LocalProductState {
|
|
||||||
const factory LocalProductState.initial() = _Initial;
|
|
||||||
const factory LocalProductState.loading() = _Loading;
|
|
||||||
const factory LocalProductState.loaded(List<Product> products) = _Loaded;
|
|
||||||
const factory LocalProductState.error(String message) = _Error;
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
|||||||
|
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
|
|
||||||
import '../../models/order_model.dart';
|
import '../../models/order_model.dart';
|
||||||
import '../../models/product_quantity.dart';
|
import '../../models/product_quantity.dart';
|
||||||
@@ -78,13 +77,13 @@ class OrderBloc extends Bloc<OrderEvent, OrderState> {
|
|||||||
value = false;
|
value = false;
|
||||||
}
|
}
|
||||||
int id = 0;
|
int id = 0;
|
||||||
if (value) {
|
// if (value) {
|
||||||
id = await ProductLocalDatasource.instance
|
// id = await ProductLocalDatasource.instance
|
||||||
.saveOrder(dataInput.copyWith(isSync: 1));
|
// .saveOrder(dataInput.copyWith(isSync: 1));
|
||||||
} else {
|
// } else {
|
||||||
id = await ProductLocalDatasource.instance
|
// id = await ProductLocalDatasource.instance
|
||||||
.saveOrder(dataInput.copyWith(isSync: 1));
|
// .saveOrder(dataInput.copyWith(isSync: 1));
|
||||||
}
|
// }
|
||||||
|
|
||||||
emit(_Loaded(
|
emit(_Loaded(
|
||||||
dataInput,
|
dataInput,
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
|
|||||||
customerName: event.customerName,
|
customerName: event.customerName,
|
||||||
notes: '',
|
notes: '',
|
||||||
orderType: event.orderType.name,
|
orderType: event.orderType.name,
|
||||||
tableId: event.table.id,
|
tableId: event.table?.id ?? "",
|
||||||
tableNumber: event.table.tableName,
|
tableNumber: event.table?.tableName ?? "",
|
||||||
outletId: userData.user?.outletId,
|
outletId: userData.user?.outletId,
|
||||||
customerId: event.customer?.id ?? '',
|
customerId: event.customer?.id ?? '',
|
||||||
orderItems: event.items
|
orderItems: event.items
|
||||||
@@ -131,11 +131,11 @@ class OrderFormBloc extends Bloc<OrderFormEvent, OrderFormState> {
|
|||||||
|
|
||||||
result.fold(
|
result.fold(
|
||||||
(error) => emit(_Error(error)),
|
(error) => emit(_Error(error)),
|
||||||
(success) => emit(_Success(success.data!)),
|
(success) => emit(_SuccessMsg()),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("Error in AddOrderItemsBloc: $e");
|
log("Error in AddOrderItemsBloc: $e");
|
||||||
emit(_Error("Failed to add order items: $e"));
|
emit(_Error("Ada kesalahan. Coba lagi nanti"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ mixin _$OrderFormEvent {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -53,7 +53,7 @@ mixin _$OrderFormEvent {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -76,7 +76,7 @@ mixin _$OrderFormEvent {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -226,7 +226,7 @@ class _$StartedImpl implements _Started {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -255,7 +255,7 @@ class _$StartedImpl implements _Started {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -281,7 +281,7 @@ class _$StartedImpl implements _Started {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -496,7 +496,7 @@ class _$CreateImpl implements _Create {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -525,7 +525,7 @@ class _$CreateImpl implements _Create {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -551,7 +551,7 @@ class _$CreateImpl implements _Create {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -652,7 +652,7 @@ abstract class _$$CreateWithPaymentMethodImplCopyWith<$Res> {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod});
|
PaymentMethod paymentMethod});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +674,7 @@ class __$$CreateWithPaymentMethodImplCopyWithImpl<$Res>
|
|||||||
Object? customerName = null,
|
Object? customerName = null,
|
||||||
Object? customer = freezed,
|
Object? customer = freezed,
|
||||||
Object? orderType = null,
|
Object? orderType = null,
|
||||||
Object? table = null,
|
Object? table = freezed,
|
||||||
Object? paymentMethod = null,
|
Object? paymentMethod = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$CreateWithPaymentMethodImpl(
|
return _then(_$CreateWithPaymentMethodImpl(
|
||||||
@@ -694,10 +694,10 @@ class __$$CreateWithPaymentMethodImplCopyWithImpl<$Res>
|
|||||||
? _value.orderType
|
? _value.orderType
|
||||||
: orderType // ignore: cast_nullable_to_non_nullable
|
: orderType // ignore: cast_nullable_to_non_nullable
|
||||||
as OrderType,
|
as OrderType,
|
||||||
table: null == table
|
table: freezed == table
|
||||||
? _value.table
|
? _value.table
|
||||||
: table // ignore: cast_nullable_to_non_nullable
|
: table // ignore: cast_nullable_to_non_nullable
|
||||||
as TableModel,
|
as TableModel?,
|
||||||
paymentMethod: null == paymentMethod
|
paymentMethod: null == paymentMethod
|
||||||
? _value.paymentMethod
|
? _value.paymentMethod
|
||||||
: paymentMethod // ignore: cast_nullable_to_non_nullable
|
: paymentMethod // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -733,7 +733,7 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
|
|||||||
@override
|
@override
|
||||||
final OrderType orderType;
|
final OrderType orderType;
|
||||||
@override
|
@override
|
||||||
final TableModel table;
|
final TableModel? table;
|
||||||
@override
|
@override
|
||||||
final PaymentMethod paymentMethod;
|
final PaymentMethod paymentMethod;
|
||||||
|
|
||||||
@@ -790,7 +790,7 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -820,7 +820,7 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -847,7 +847,7 @@ class _$CreateWithPaymentMethodImpl implements _CreateWithPaymentMethod {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -922,7 +922,7 @@ abstract class _CreateWithPaymentMethod implements OrderFormEvent {
|
|||||||
required final String customerName,
|
required final String customerName,
|
||||||
required final Customer? customer,
|
required final Customer? customer,
|
||||||
required final OrderType orderType,
|
required final OrderType orderType,
|
||||||
required final TableModel table,
|
required final TableModel? table,
|
||||||
required final PaymentMethod paymentMethod}) =
|
required final PaymentMethod paymentMethod}) =
|
||||||
_$CreateWithPaymentMethodImpl;
|
_$CreateWithPaymentMethodImpl;
|
||||||
|
|
||||||
@@ -930,7 +930,7 @@ abstract class _CreateWithPaymentMethod implements OrderFormEvent {
|
|||||||
String get customerName;
|
String get customerName;
|
||||||
Customer? get customer;
|
Customer? get customer;
|
||||||
OrderType get orderType;
|
OrderType get orderType;
|
||||||
TableModel get table;
|
TableModel? get table;
|
||||||
PaymentMethod get paymentMethod;
|
PaymentMethod get paymentMethod;
|
||||||
|
|
||||||
/// Create a copy of OrderFormEvent
|
/// Create a copy of OrderFormEvent
|
||||||
@@ -1034,7 +1034,7 @@ class _$AddToOrderImpl implements _AddToOrder {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -1063,7 +1063,7 @@ class _$AddToOrderImpl implements _AddToOrder {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1089,7 +1089,7 @@ class _$AddToOrderImpl implements _AddToOrder {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1276,7 +1276,7 @@ class _$RefundImpl implements _Refund {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -1305,7 +1305,7 @@ class _$RefundImpl implements _Refund {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1331,7 +1331,7 @@ class _$RefundImpl implements _Refund {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1520,7 +1520,7 @@ class _$VoidOrderImpl implements _VoidOrder {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -1549,7 +1549,7 @@ class _$VoidOrderImpl implements _VoidOrder {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1575,7 +1575,7 @@ class _$VoidOrderImpl implements _VoidOrder {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1737,7 +1737,7 @@ class _$ToggleItemImpl implements _ToggleItem {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -1766,7 +1766,7 @@ class _$ToggleItemImpl implements _ToggleItem {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1792,7 +1792,7 @@ class _$ToggleItemImpl implements _ToggleItem {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -1951,7 +1951,7 @@ class _$ToggleSelectAllImpl implements _ToggleSelectAll {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)
|
PaymentMethod paymentMethod)
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
required TResult Function(List<ProductQuantity> items, String orderId)
|
required TResult Function(List<ProductQuantity> items, String orderId)
|
||||||
@@ -1980,7 +1980,7 @@ class _$ToggleSelectAllImpl implements _ToggleSelectAll {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult? Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
@@ -2006,7 +2006,7 @@ class _$ToggleSelectAllImpl implements _ToggleSelectAll {
|
|||||||
String customerName,
|
String customerName,
|
||||||
Customer? customer,
|
Customer? customer,
|
||||||
OrderType orderType,
|
OrderType orderType,
|
||||||
TableModel table,
|
TableModel? table,
|
||||||
PaymentMethod paymentMethod)?
|
PaymentMethod paymentMethod)?
|
||||||
createWithPayment,
|
createWithPayment,
|
||||||
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
TResult Function(List<ProductQuantity> items, String orderId)? addToOrder,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class OrderFormEvent with _$OrderFormEvent {
|
|||||||
required String customerName,
|
required String customerName,
|
||||||
required Customer? customer,
|
required Customer? customer,
|
||||||
required OrderType orderType,
|
required OrderType orderType,
|
||||||
required TableModel table,
|
required TableModel? table,
|
||||||
required PaymentMethod paymentMethod,
|
required PaymentMethod paymentMethod,
|
||||||
}) = _CreateWithPaymentMethod;
|
}) = _CreateWithPaymentMethod;
|
||||||
const factory OrderFormEvent.addToOrder({
|
const factory OrderFormEvent.addToOrder({
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_remote_data_source.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_remote_datasource.dart';
|
|
||||||
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/product_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/repositories/product/product_repository.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'product_loader_event.dart';
|
part 'product_loader_event.dart';
|
||||||
@@ -10,102 +10,114 @@ part 'product_loader_state.dart';
|
|||||||
part 'product_loader_bloc.freezed.dart';
|
part 'product_loader_bloc.freezed.dart';
|
||||||
|
|
||||||
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
||||||
final ProductRemoteDatasource _productRemoteDatasource;
|
final ProductRepository _productRepository = ProductRepository.instance;
|
||||||
|
|
||||||
// Debouncing untuk mencegah multiple load more calls
|
|
||||||
Timer? _loadMoreDebounce;
|
Timer? _loadMoreDebounce;
|
||||||
|
Timer? _searchDebounce;
|
||||||
bool _isLoadingMore = false;
|
bool _isLoadingMore = false;
|
||||||
|
|
||||||
ProductLoaderBloc(this._productRemoteDatasource)
|
ProductLoaderBloc() : super(const ProductLoaderState.initial()) {
|
||||||
: super(ProductLoaderState.initial()) {
|
|
||||||
on<_GetProduct>(_onGetProduct);
|
on<_GetProduct>(_onGetProduct);
|
||||||
on<_LoadMore>(_onLoadMore);
|
on<_LoadMore>(_onLoadMore);
|
||||||
on<_Refresh>(_onRefresh);
|
on<_Refresh>(_onRefresh);
|
||||||
|
on<_SearchProduct>(_onSearchProduct);
|
||||||
|
on<_GetDatabaseStats>(_onGetDatabaseStats);
|
||||||
|
on<_ClearCache>(_onClearCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_loadMoreDebounce?.cancel();
|
_loadMoreDebounce?.cancel();
|
||||||
|
_searchDebounce?.cancel();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debounce transformer untuk load more
|
// Pure local product loading
|
||||||
// EventTransformer<T> _debounceTransformer<T>() {
|
|
||||||
// return (events, mapper) {
|
|
||||||
// return events
|
|
||||||
// .debounceTime(const Duration(milliseconds: 300))
|
|
||||||
// .asyncExpand(mapper);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
Future<void> _onGetProduct(
|
Future<void> _onGetProduct(
|
||||||
_GetProduct event,
|
_GetProduct event,
|
||||||
Emitter<ProductLoaderState> emit,
|
Emitter<ProductLoaderState> emit,
|
||||||
) async {
|
) async {
|
||||||
emit(const _Loading());
|
emit(const ProductLoaderState.loading());
|
||||||
_isLoadingMore = false; // Reset loading state
|
_isLoadingMore = false;
|
||||||
|
|
||||||
final result = await _productRemoteDatasource.getProducts(
|
log('📱 Loading local products - categoryId: ${event.categoryId}');
|
||||||
|
|
||||||
|
// Check if local database is ready
|
||||||
|
final isReady = await _productRepository.isLocalDatabaseReady();
|
||||||
|
if (!isReady) {
|
||||||
|
emit(const ProductLoaderState.error(
|
||||||
|
'Database lokal belum siap. Silakan lakukan sinkronisasi data terlebih dahulu.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await _productRepository.getProducts(
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
categoryId: event.categoryId,
|
categoryId: event.categoryId,
|
||||||
|
search: event.search,
|
||||||
);
|
);
|
||||||
|
|
||||||
await result.fold(
|
await result.fold(
|
||||||
(failure) async => emit(_Error(failure)),
|
(failure) async {
|
||||||
|
log('❌ Error loading local products: $failure');
|
||||||
|
emit(ProductLoaderState.error(failure));
|
||||||
|
},
|
||||||
(response) async {
|
(response) async {
|
||||||
final products = response.data?.products ?? [];
|
final products = response.data?.products ?? [];
|
||||||
final hasReachedMax = products.length < 10;
|
final totalPages = response.data?.totalPages ?? 1;
|
||||||
|
final hasReachedMax = products.length < 10 || 1 >= totalPages;
|
||||||
|
|
||||||
emit(_Loaded(
|
log('✅ Local products loaded: ${products.length}, hasReachedMax: $hasReachedMax, totalPages: $totalPages');
|
||||||
|
|
||||||
|
emit(ProductLoaderState.loaded(
|
||||||
products: products,
|
products: products,
|
||||||
hasReachedMax: hasReachedMax,
|
hasReachedMax: hasReachedMax,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
isLoadingMore: false,
|
isLoadingMore: false,
|
||||||
|
categoryId: event.categoryId,
|
||||||
|
searchQuery: event.search,
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load more with enhanced debouncing
|
// Pure local load more
|
||||||
Future<void> _onLoadMore(
|
Future<void> _onLoadMore(
|
||||||
_LoadMore event,
|
_LoadMore event,
|
||||||
Emitter<ProductLoaderState> emit,
|
Emitter<ProductLoaderState> emit,
|
||||||
) async {
|
) async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
// Enhanced validation
|
|
||||||
if (currentState is! _Loaded ||
|
if (currentState is! _Loaded ||
|
||||||
currentState.hasReachedMax ||
|
currentState.hasReachedMax ||
|
||||||
_isLoadingMore ||
|
_isLoadingMore ||
|
||||||
currentState.isLoadingMore) {
|
currentState.isLoadingMore) {
|
||||||
|
log('⏹️ Load more blocked - state: ${currentState.runtimeType}, isLoadingMore: $_isLoadingMore');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoadingMore = true;
|
_isLoadingMore = true;
|
||||||
|
|
||||||
// Emit loading more state
|
|
||||||
emit(currentState.copyWith(isLoadingMore: true));
|
emit(currentState.copyWith(isLoadingMore: true));
|
||||||
|
|
||||||
final nextPage = currentState.currentPage + 1;
|
final nextPage = currentState.currentPage + 1;
|
||||||
|
log('📄 Loading more local products - page: $nextPage');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = await _productRemoteDatasource.getProducts(
|
final result = await _productRepository.getProducts(
|
||||||
page: nextPage,
|
page: nextPage,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
categoryId: event.categoryId,
|
categoryId: currentState.categoryId,
|
||||||
|
search: currentState.searchQuery,
|
||||||
);
|
);
|
||||||
|
|
||||||
await result.fold(
|
await result.fold(
|
||||||
(failure) async {
|
(failure) async {
|
||||||
// On error, revert loading state but don't show error
|
log('❌ Error loading more local products: $failure');
|
||||||
// Just silently fail and allow retry
|
|
||||||
emit(currentState.copyWith(isLoadingMore: false));
|
emit(currentState.copyWith(isLoadingMore: false));
|
||||||
_isLoadingMore = false;
|
|
||||||
},
|
},
|
||||||
(response) async {
|
(response) async {
|
||||||
final newProducts = response.data?.products ?? [];
|
final newProducts = response.data?.products ?? [];
|
||||||
|
final totalPages = response.data?.totalPages ?? 1;
|
||||||
|
|
||||||
// Prevent duplicate products
|
// Prevent duplicate products
|
||||||
final currentProductIds =
|
final currentProductIds =
|
||||||
@@ -117,32 +129,130 @@ class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
|||||||
final allProducts = List<Product>.from(currentState.products)
|
final allProducts = List<Product>.from(currentState.products)
|
||||||
..addAll(filteredNewProducts);
|
..addAll(filteredNewProducts);
|
||||||
|
|
||||||
final hasReachedMax = newProducts.length < 10;
|
final hasReachedMax =
|
||||||
|
newProducts.length < 10 || nextPage >= totalPages;
|
||||||
|
|
||||||
emit(_Loaded(
|
log('✅ More local products loaded: ${filteredNewProducts.length} new, total: ${allProducts.length}');
|
||||||
|
|
||||||
|
emit(ProductLoaderState.loaded(
|
||||||
products: allProducts,
|
products: allProducts,
|
||||||
hasReachedMax: hasReachedMax,
|
hasReachedMax: hasReachedMax,
|
||||||
currentPage: nextPage,
|
currentPage: nextPage,
|
||||||
isLoadingMore: false,
|
isLoadingMore: false,
|
||||||
|
categoryId: currentState.categoryId,
|
||||||
|
searchQuery: currentState.searchQuery,
|
||||||
));
|
));
|
||||||
|
|
||||||
_isLoadingMore = false;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Handle unexpected errors
|
log('❌ Exception loading more local products: $e');
|
||||||
emit(currentState.copyWith(isLoadingMore: false));
|
emit(currentState.copyWith(isLoadingMore: false));
|
||||||
|
} finally {
|
||||||
_isLoadingMore = false;
|
_isLoadingMore = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh data
|
// Pure local refresh
|
||||||
Future<void> _onRefresh(
|
Future<void> _onRefresh(
|
||||||
_Refresh event,
|
_Refresh event,
|
||||||
Emitter<ProductLoaderState> emit,
|
Emitter<ProductLoaderState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
final currentState = state;
|
||||||
|
String? categoryId;
|
||||||
|
String? searchQuery;
|
||||||
|
|
||||||
|
if (currentState is _Loaded) {
|
||||||
|
categoryId = currentState.categoryId;
|
||||||
|
searchQuery = currentState.searchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
_isLoadingMore = false;
|
_isLoadingMore = false;
|
||||||
_loadMoreDebounce?.cancel();
|
_loadMoreDebounce?.cancel();
|
||||||
add(const _GetProduct());
|
_searchDebounce?.cancel();
|
||||||
|
|
||||||
|
log('🔄 Refreshing local products');
|
||||||
|
|
||||||
|
// Clear local cache
|
||||||
|
_productRepository.clearCache();
|
||||||
|
|
||||||
|
add(ProductLoaderEvent.getProduct(
|
||||||
|
categoryId: categoryId,
|
||||||
|
search: searchQuery,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast local search (no debouncing needed for local data)
|
||||||
|
Future<void> _onSearchProduct(
|
||||||
|
_SearchProduct event,
|
||||||
|
Emitter<ProductLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
// Cancel previous search
|
||||||
|
_searchDebounce?.cancel();
|
||||||
|
|
||||||
|
// Minimal debounce for local search (much faster)
|
||||||
|
_searchDebounce = Timer(Duration(milliseconds: 150), () async {
|
||||||
|
emit(const ProductLoaderState.loading());
|
||||||
|
_isLoadingMore = false;
|
||||||
|
|
||||||
|
log('🔍 Local search: "${event.query}"');
|
||||||
|
|
||||||
|
final result = await _productRepository.getProducts(
|
||||||
|
page: 1,
|
||||||
|
limit: 20, // More results for search
|
||||||
|
categoryId: event.categoryId,
|
||||||
|
search: event.query,
|
||||||
|
);
|
||||||
|
|
||||||
|
await result.fold(
|
||||||
|
(failure) async {
|
||||||
|
log('❌ Local search error: $failure');
|
||||||
|
emit(ProductLoaderState.error(failure));
|
||||||
|
},
|
||||||
|
(response) async {
|
||||||
|
final products = response.data?.products ?? [];
|
||||||
|
final totalPages = response.data?.totalPages ?? 1;
|
||||||
|
final hasReachedMax = products.length < 20 || 1 >= totalPages;
|
||||||
|
|
||||||
|
log('✅ Local search results: ${products.length} products found');
|
||||||
|
|
||||||
|
emit(ProductLoaderState.loaded(
|
||||||
|
products: products,
|
||||||
|
hasReachedMax: hasReachedMax,
|
||||||
|
currentPage: 1,
|
||||||
|
isLoadingMore: false,
|
||||||
|
categoryId: event.categoryId,
|
||||||
|
searchQuery: event.query,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get local database statistics
|
||||||
|
Future<void> _onGetDatabaseStats(
|
||||||
|
_GetDatabaseStats event,
|
||||||
|
Emitter<ProductLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final stats = await _productRepository.getDatabaseStats();
|
||||||
|
log('📊 Local database stats retrieved: $stats');
|
||||||
|
|
||||||
|
// You can emit a special state here if needed for UI updates
|
||||||
|
// For now, just log the stats
|
||||||
|
} catch (e) {
|
||||||
|
log('❌ Error getting local database stats: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear local cache
|
||||||
|
Future<void> _onClearCache(
|
||||||
|
_ClearCache event,
|
||||||
|
Emitter<ProductLoaderState> emit,
|
||||||
|
) async {
|
||||||
|
log('🧹 Manually clearing local cache');
|
||||||
|
_productRepository.clearCache();
|
||||||
|
|
||||||
|
// Refresh current data after cache clear
|
||||||
|
add(const ProductLoaderEvent.refresh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,25 @@ part of 'product_loader_bloc.dart';
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class ProductLoaderEvent with _$ProductLoaderEvent {
|
class ProductLoaderEvent with _$ProductLoaderEvent {
|
||||||
const factory ProductLoaderEvent.getProduct(
|
const factory ProductLoaderEvent.getProduct({
|
||||||
{String? categoryId, String? search}) = _GetProduct;
|
String? categoryId,
|
||||||
const factory ProductLoaderEvent.loadMore(
|
String? search, // Added search parameter
|
||||||
{String? categoryId, String? search}) = _LoadMore;
|
bool? forceRefresh, // Kept for compatibility but ignored
|
||||||
|
}) = _GetProduct;
|
||||||
|
|
||||||
|
const factory ProductLoaderEvent.loadMore({
|
||||||
|
String? categoryId,
|
||||||
|
String? search,
|
||||||
|
}) = _LoadMore;
|
||||||
|
|
||||||
const factory ProductLoaderEvent.refresh() = _Refresh;
|
const factory ProductLoaderEvent.refresh() = _Refresh;
|
||||||
|
|
||||||
|
const factory ProductLoaderEvent.searchProduct({
|
||||||
|
String? query,
|
||||||
|
String? categoryId,
|
||||||
|
}) = _SearchProduct;
|
||||||
|
|
||||||
|
const factory ProductLoaderEvent.getDatabaseStats() = _GetDatabaseStats;
|
||||||
|
|
||||||
|
const factory ProductLoaderEvent.clearCache() = _ClearCache;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class ProductLoaderState with _$ProductLoaderState {
|
|||||||
required bool hasReachedMax,
|
required bool hasReachedMax,
|
||||||
required int currentPage,
|
required int currentPage,
|
||||||
required bool isLoadingMore,
|
required bool isLoadingMore,
|
||||||
|
String? categoryId,
|
||||||
|
String? searchQuery,
|
||||||
}) = _Loaded;
|
}) = _Loaded;
|
||||||
const factory ProductLoaderState.error(String message) = _Error;
|
const factory ProductLoaderState.error(String message) = _Error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
import 'package:enaklo_pos/data/models/response/table_model.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
part 'status_table_event.dart';
|
|
||||||
part 'status_table_state.dart';
|
|
||||||
part 'status_table_bloc.freezed.dart';
|
|
||||||
|
|
||||||
class StatusTableBloc extends Bloc<StatusTableEvent, StatusTableState> {
|
|
||||||
final ProductLocalDatasource datasource;
|
|
||||||
StatusTableBloc(this.datasource) : super(StatusTableState.initial()) {
|
|
||||||
on<_StatusTable>((event, emit) async {
|
|
||||||
emit(_Loading());
|
|
||||||
await datasource.updateStatusTable(event.table);
|
|
||||||
emit(_Success());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,725 +0,0 @@
|
|||||||
// 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 'status_table_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 _$StatusTableEvent {
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() started,
|
|
||||||
required TResult Function(TableModel table) statusTabel,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? started,
|
|
||||||
TResult? Function(TableModel table)? statusTabel,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? started,
|
|
||||||
TResult Function(TableModel table)? statusTabel,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Started value) started,
|
|
||||||
required TResult Function(_StatusTable value) statusTabel,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Started value)? started,
|
|
||||||
TResult? Function(_StatusTable value)? statusTabel,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Started value)? started,
|
|
||||||
TResult Function(_StatusTable value)? statusTabel,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $StatusTableEventCopyWith<$Res> {
|
|
||||||
factory $StatusTableEventCopyWith(
|
|
||||||
StatusTableEvent value, $Res Function(StatusTableEvent) then) =
|
|
||||||
_$StatusTableEventCopyWithImpl<$Res, StatusTableEvent>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$StatusTableEventCopyWithImpl<$Res, $Val extends StatusTableEvent>
|
|
||||||
implements $StatusTableEventCopyWith<$Res> {
|
|
||||||
_$StatusTableEventCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$StartedImplCopyWith<$Res> {
|
|
||||||
factory _$$StartedImplCopyWith(
|
|
||||||
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
|
|
||||||
__$$StartedImplCopyWithImpl<$Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$StartedImplCopyWithImpl<$Res>
|
|
||||||
extends _$StatusTableEventCopyWithImpl<$Res, _$StartedImpl>
|
|
||||||
implements _$$StartedImplCopyWith<$Res> {
|
|
||||||
__$$StartedImplCopyWithImpl(
|
|
||||||
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$StartedImpl implements _Started {
|
|
||||||
const _$StartedImpl();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'StatusTableEvent.started()';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType && other is _$StartedImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => runtimeType.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() started,
|
|
||||||
required TResult Function(TableModel table) statusTabel,
|
|
||||||
}) {
|
|
||||||
return started();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? started,
|
|
||||||
TResult? Function(TableModel table)? statusTabel,
|
|
||||||
}) {
|
|
||||||
return started?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? started,
|
|
||||||
TResult Function(TableModel table)? statusTabel,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (started != null) {
|
|
||||||
return started();
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Started value) started,
|
|
||||||
required TResult Function(_StatusTable value) statusTabel,
|
|
||||||
}) {
|
|
||||||
return started(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Started value)? started,
|
|
||||||
TResult? Function(_StatusTable value)? statusTabel,
|
|
||||||
}) {
|
|
||||||
return started?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Started value)? started,
|
|
||||||
TResult Function(_StatusTable value)? statusTabel,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (started != null) {
|
|
||||||
return started(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Started implements StatusTableEvent {
|
|
||||||
const factory _Started() = _$StartedImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$StatusTableImplCopyWith<$Res> {
|
|
||||||
factory _$$StatusTableImplCopyWith(
|
|
||||||
_$StatusTableImpl value, $Res Function(_$StatusTableImpl) then) =
|
|
||||||
__$$StatusTableImplCopyWithImpl<$Res>;
|
|
||||||
@useResult
|
|
||||||
$Res call({TableModel table});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$StatusTableImplCopyWithImpl<$Res>
|
|
||||||
extends _$StatusTableEventCopyWithImpl<$Res, _$StatusTableImpl>
|
|
||||||
implements _$$StatusTableImplCopyWith<$Res> {
|
|
||||||
__$$StatusTableImplCopyWithImpl(
|
|
||||||
_$StatusTableImpl _value, $Res Function(_$StatusTableImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? table = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$StatusTableImpl(
|
|
||||||
null == table
|
|
||||||
? _value.table
|
|
||||||
: table // ignore: cast_nullable_to_non_nullable
|
|
||||||
as TableModel,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$StatusTableImpl implements _StatusTable {
|
|
||||||
const _$StatusTableImpl(this.table);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final TableModel table;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'StatusTableEvent.statusTabel(table: $table)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$StatusTableImpl &&
|
|
||||||
(identical(other.table, table) || other.table == table));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, table);
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$StatusTableImplCopyWith<_$StatusTableImpl> get copyWith =>
|
|
||||||
__$$StatusTableImplCopyWithImpl<_$StatusTableImpl>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() started,
|
|
||||||
required TResult Function(TableModel table) statusTabel,
|
|
||||||
}) {
|
|
||||||
return statusTabel(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? started,
|
|
||||||
TResult? Function(TableModel table)? statusTabel,
|
|
||||||
}) {
|
|
||||||
return statusTabel?.call(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? started,
|
|
||||||
TResult Function(TableModel table)? statusTabel,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (statusTabel != null) {
|
|
||||||
return statusTabel(table);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Started value) started,
|
|
||||||
required TResult Function(_StatusTable value) statusTabel,
|
|
||||||
}) {
|
|
||||||
return statusTabel(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Started value)? started,
|
|
||||||
TResult? Function(_StatusTable value)? statusTabel,
|
|
||||||
}) {
|
|
||||||
return statusTabel?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Started value)? started,
|
|
||||||
TResult Function(_StatusTable value)? statusTabel,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (statusTabel != null) {
|
|
||||||
return statusTabel(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _StatusTable implements StatusTableEvent {
|
|
||||||
const factory _StatusTable(final TableModel table) = _$StatusTableImpl;
|
|
||||||
|
|
||||||
TableModel get table;
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$StatusTableImplCopyWith<_$StatusTableImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$StatusTableState {
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() initial,
|
|
||||||
required TResult Function() loading,
|
|
||||||
required TResult Function() success,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? initial,
|
|
||||||
TResult? Function()? loading,
|
|
||||||
TResult? Function()? success,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? initial,
|
|
||||||
TResult Function()? loading,
|
|
||||||
TResult Function()? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Initial value) initial,
|
|
||||||
required TResult Function(_Loading value) loading,
|
|
||||||
required TResult Function(_Success value) success,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Initial value)? initial,
|
|
||||||
TResult? Function(_Loading value)? loading,
|
|
||||||
TResult? Function(_Success value)? success,
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Initial value)? initial,
|
|
||||||
TResult Function(_Loading value)? loading,
|
|
||||||
TResult Function(_Success value)? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $StatusTableStateCopyWith<$Res> {
|
|
||||||
factory $StatusTableStateCopyWith(
|
|
||||||
StatusTableState value, $Res Function(StatusTableState) then) =
|
|
||||||
_$StatusTableStateCopyWithImpl<$Res, StatusTableState>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$StatusTableStateCopyWithImpl<$Res, $Val extends StatusTableState>
|
|
||||||
implements $StatusTableStateCopyWith<$Res> {
|
|
||||||
_$StatusTableStateCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$InitialImplCopyWith<$Res> {
|
|
||||||
factory _$$InitialImplCopyWith(
|
|
||||||
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
|
|
||||||
__$$InitialImplCopyWithImpl<$Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$InitialImplCopyWithImpl<$Res>
|
|
||||||
extends _$StatusTableStateCopyWithImpl<$Res, _$InitialImpl>
|
|
||||||
implements _$$InitialImplCopyWith<$Res> {
|
|
||||||
__$$InitialImplCopyWithImpl(
|
|
||||||
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$InitialImpl implements _Initial {
|
|
||||||
const _$InitialImpl();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'StatusTableState.initial()';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType && other is _$InitialImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => runtimeType.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() initial,
|
|
||||||
required TResult Function() loading,
|
|
||||||
required TResult Function() success,
|
|
||||||
}) {
|
|
||||||
return initial();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? initial,
|
|
||||||
TResult? Function()? loading,
|
|
||||||
TResult? Function()? success,
|
|
||||||
}) {
|
|
||||||
return initial?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? initial,
|
|
||||||
TResult Function()? loading,
|
|
||||||
TResult Function()? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (initial != null) {
|
|
||||||
return initial();
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Initial value) initial,
|
|
||||||
required TResult Function(_Loading value) loading,
|
|
||||||
required TResult Function(_Success value) success,
|
|
||||||
}) {
|
|
||||||
return initial(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Initial value)? initial,
|
|
||||||
TResult? Function(_Loading value)? loading,
|
|
||||||
TResult? Function(_Success value)? success,
|
|
||||||
}) {
|
|
||||||
return initial?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Initial value)? initial,
|
|
||||||
TResult Function(_Loading value)? loading,
|
|
||||||
TResult Function(_Success value)? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (initial != null) {
|
|
||||||
return initial(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Initial implements StatusTableState {
|
|
||||||
const factory _Initial() = _$InitialImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$LoadingImplCopyWith<$Res> {
|
|
||||||
factory _$$LoadingImplCopyWith(
|
|
||||||
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
|
|
||||||
__$$LoadingImplCopyWithImpl<$Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$LoadingImplCopyWithImpl<$Res>
|
|
||||||
extends _$StatusTableStateCopyWithImpl<$Res, _$LoadingImpl>
|
|
||||||
implements _$$LoadingImplCopyWith<$Res> {
|
|
||||||
__$$LoadingImplCopyWithImpl(
|
|
||||||
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$LoadingImpl implements _Loading {
|
|
||||||
const _$LoadingImpl();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'StatusTableState.loading()';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => runtimeType.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() initial,
|
|
||||||
required TResult Function() loading,
|
|
||||||
required TResult Function() success,
|
|
||||||
}) {
|
|
||||||
return loading();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? initial,
|
|
||||||
TResult? Function()? loading,
|
|
||||||
TResult? Function()? success,
|
|
||||||
}) {
|
|
||||||
return loading?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? initial,
|
|
||||||
TResult Function()? loading,
|
|
||||||
TResult Function()? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (loading != null) {
|
|
||||||
return loading();
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Initial value) initial,
|
|
||||||
required TResult Function(_Loading value) loading,
|
|
||||||
required TResult Function(_Success value) success,
|
|
||||||
}) {
|
|
||||||
return loading(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Initial value)? initial,
|
|
||||||
TResult? Function(_Loading value)? loading,
|
|
||||||
TResult? Function(_Success value)? success,
|
|
||||||
}) {
|
|
||||||
return loading?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Initial value)? initial,
|
|
||||||
TResult Function(_Loading value)? loading,
|
|
||||||
TResult Function(_Success value)? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (loading != null) {
|
|
||||||
return loading(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Loading implements StatusTableState {
|
|
||||||
const factory _Loading() = _$LoadingImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$SuccessImplCopyWith<$Res> {
|
|
||||||
factory _$$SuccessImplCopyWith(
|
|
||||||
_$SuccessImpl value, $Res Function(_$SuccessImpl) then) =
|
|
||||||
__$$SuccessImplCopyWithImpl<$Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$SuccessImplCopyWithImpl<$Res>
|
|
||||||
extends _$StatusTableStateCopyWithImpl<$Res, _$SuccessImpl>
|
|
||||||
implements _$$SuccessImplCopyWith<$Res> {
|
|
||||||
__$$SuccessImplCopyWithImpl(
|
|
||||||
_$SuccessImpl _value, $Res Function(_$SuccessImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of StatusTableState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$SuccessImpl implements _Success {
|
|
||||||
const _$SuccessImpl();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'StatusTableState.success()';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType && other is _$SuccessImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => runtimeType.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() initial,
|
|
||||||
required TResult Function() loading,
|
|
||||||
required TResult Function() success,
|
|
||||||
}) {
|
|
||||||
return success();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? initial,
|
|
||||||
TResult? Function()? loading,
|
|
||||||
TResult? Function()? success,
|
|
||||||
}) {
|
|
||||||
return success?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? initial,
|
|
||||||
TResult Function()? loading,
|
|
||||||
TResult Function()? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (success != null) {
|
|
||||||
return success();
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Initial value) initial,
|
|
||||||
required TResult Function(_Loading value) loading,
|
|
||||||
required TResult Function(_Success value) success,
|
|
||||||
}) {
|
|
||||||
return success(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Initial value)? initial,
|
|
||||||
TResult? Function(_Loading value)? loading,
|
|
||||||
TResult? Function(_Success value)? success,
|
|
||||||
}) {
|
|
||||||
return success?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Initial value)? initial,
|
|
||||||
TResult Function(_Loading value)? loading,
|
|
||||||
TResult Function(_Success value)? success,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (success != null) {
|
|
||||||
return success(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Success implements StatusTableState {
|
|
||||||
const factory _Success() = _$SuccessImpl;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
part of 'status_table_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class StatusTableEvent with _$StatusTableEvent {
|
|
||||||
const factory StatusTableEvent.started() = _Started;
|
|
||||||
const factory StatusTableEvent.statusTabel(
|
|
||||||
TableModel table,
|
|
||||||
) = _StatusTable;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
part of 'status_table_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class StatusTableState with _$StatusTableState {
|
|
||||||
const factory StatusTableState.initial() = _Initial;
|
|
||||||
const factory StatusTableState.loading() = _Loading;
|
|
||||||
const factory StatusTableState.success() = _Success;
|
|
||||||
}
|
|
||||||
@@ -289,7 +289,7 @@ class _PaymentAddOrderDialogState extends State<PaymentAddOrderDialog> {
|
|||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
state.maybeWhen(
|
state.maybeWhen(
|
||||||
orElse: () {},
|
orElse: () {},
|
||||||
success: (data) {
|
successMsg: () {
|
||||||
context.pop();
|
context.pop();
|
||||||
context.pushReplacement(
|
context.pushReplacement(
|
||||||
SuccessSaveOrderPage(
|
SuccessSaveOrderPage(
|
||||||
|
|||||||
@@ -2,24 +2,22 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:enaklo_pos/data/datasources/printer/printer_local_datasource.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart';
|
// import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart';
|
||||||
import 'package:widgets_to_image/widgets_to_image.dart';
|
import 'package:widgets_to_image/widgets_to_image.dart';
|
||||||
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
||||||
|
|
||||||
import '../../../core/components/spaces.dart';
|
import '../../../core/components/spaces.dart';
|
||||||
import '../../../core/constants/colors.dart';
|
import '../../../core/constants/colors.dart';
|
||||||
import '../bloc/order/order_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
|
|
||||||
class PaymentQrisDialog extends StatefulWidget {
|
class PaymentQrisDialog extends StatefulWidget {
|
||||||
final List<ProductQuantity> items;
|
final List<ProductQuantity> items;
|
||||||
@@ -124,36 +122,36 @@ class _PaymentQrisDialogState extends State<PaymentQrisDialog> {
|
|||||||
));
|
));
|
||||||
});
|
});
|
||||||
}, success: (message) async {
|
}, success: (message) async {
|
||||||
context.read<OrderBloc>().add(OrderEvent.order(
|
// context.read<OrderBloc>().add(OrderEvent.order(
|
||||||
widget.items,
|
// widget.items,
|
||||||
widget.discount,
|
// widget.discount,
|
||||||
widget.discountAmount,
|
// widget.discountAmount,
|
||||||
widget.tax,
|
// widget.tax,
|
||||||
widget.serviceCharge,
|
// widget.serviceCharge,
|
||||||
widget.paymentAmount,
|
// widget.paymentAmount,
|
||||||
widget.customerName,
|
// widget.customerName,
|
||||||
widget.tableNumber,
|
// widget.tableNumber,
|
||||||
'completed',
|
// 'completed',
|
||||||
'paid',
|
// 'paid',
|
||||||
'Qris',
|
// 'Qris',
|
||||||
widget.price,
|
// widget.price,
|
||||||
OrderType.dineIn));
|
// OrderType.dineIn));
|
||||||
await showDialog(
|
// await showDialog(
|
||||||
context: context,
|
// context: context,
|
||||||
barrierDismissible: false,
|
// barrierDismissible: false,
|
||||||
builder: (context) => SuccessPaymentDialog(
|
// builder: (context) => SuccessPaymentDialog(
|
||||||
isTablePaymentPage: widget.isTablePaymentPage,
|
// isTablePaymentPage: widget.isTablePaymentPage,
|
||||||
data: widget.items,
|
// data: widget.items,
|
||||||
totalQty: widget.totalQty,
|
// totalQty: widget.totalQty,
|
||||||
totalPrice: widget.price,
|
// totalPrice: widget.price,
|
||||||
totalTax: widget.tax,
|
// totalTax: widget.tax,
|
||||||
totalDiscount: widget.discountAmount,
|
// totalDiscount: widget.discountAmount,
|
||||||
subTotal: widget.subTotal,
|
// subTotal: widget.subTotal,
|
||||||
normalPrice: widget.price,
|
// normalPrice: widget.price,
|
||||||
totalService: widget.serviceCharge,
|
// totalService: widget.serviceCharge,
|
||||||
draftName: widget.customerName,
|
// draftName: widget.customerName,
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: BlocBuilder<QrisBloc, QrisState>(
|
child: BlocBuilder<QrisBloc, QrisState>(
|
||||||
@@ -236,7 +234,7 @@ class _PaymentQrisDialogState extends State<PaymentQrisDialog> {
|
|||||||
widget.price, bytes!, int.parse(sizeReceipt));
|
widget.price, bytes!, int.parse(sizeReceipt));
|
||||||
|
|
||||||
// Get the receipt printer to print QRIS
|
// Get the receipt printer to print QRIS
|
||||||
final receiptPrinter = await ProductLocalDatasource
|
final receiptPrinter = await PrinterLocalDatasource
|
||||||
.instance
|
.instance
|
||||||
.getPrinterByCode('receipt');
|
.getPrinterByCode('receipt');
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class OrderRequestModel {
|
|||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
"outlet_id": outletId,
|
"outlet_id": outletId,
|
||||||
"table_number": tableNumber,
|
"table_number": tableNumber,
|
||||||
"table_id": tableId,
|
|
||||||
"order_type": orderType,
|
"order_type": orderType,
|
||||||
"notes": notes,
|
"notes": notes,
|
||||||
"order_items": orderItems == null
|
"order_items": orderItems == null
|
||||||
@@ -58,6 +57,10 @@ class OrderRequestModel {
|
|||||||
data["customer_id"] = customerId;
|
data["customer_id"] = customerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tableId != null && tableId != "") {
|
||||||
|
data["table_id"] = tableId;
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ import 'package:enaklo_pos/presentation/home/bloc/order/order_bloc.dart';
|
|||||||
import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
|
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/widgets/save_order_dialog.dart';
|
// import 'package:enaklo_pos/presentation/home/widgets/save_order_dialog.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart';
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
||||||
|
|
||||||
@@ -355,7 +355,10 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
(previousValue, element) =>
|
(previousValue, element) =>
|
||||||
previousValue +
|
previousValue +
|
||||||
(element.product.price! *
|
(element.product.price! *
|
||||||
element.quantity),
|
element.quantity) +
|
||||||
|
(element.variant
|
||||||
|
?.priceModifier ??
|
||||||
|
0),
|
||||||
));
|
));
|
||||||
return Text(
|
return Text(
|
||||||
price.currencyFormatRp,
|
price.currencyFormatRp,
|
||||||
@@ -500,7 +503,9 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
(previousValue, element) =>
|
(previousValue, element) =>
|
||||||
previousValue +
|
previousValue +
|
||||||
(element.product.price! *
|
(element.product.price! *
|
||||||
element.quantity),
|
element.quantity) +
|
||||||
|
(element.variant?.priceModifier ??
|
||||||
|
0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1065,12 +1070,15 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.read<OrderFormBloc>().add(
|
context.read<OrderFormBloc>().add(
|
||||||
OrderFormEvent.create(
|
OrderFormEvent
|
||||||
|
.createWithPayment(
|
||||||
items: items,
|
items: items,
|
||||||
customerName:
|
customerName:
|
||||||
customerController
|
customerController
|
||||||
.text,
|
.text,
|
||||||
orderType: orderType,
|
orderType: orderType,
|
||||||
|
paymentMethod:
|
||||||
|
selectedPaymentMethod!,
|
||||||
table: widget.table,
|
table: widget.table,
|
||||||
customer:
|
customer:
|
||||||
selectedCustomer,
|
selectedCustomer,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:enaklo_pos/presentation/customer/pages/customer_page.dart';
|
import 'package:enaklo_pos/presentation/customer/pages/customer_page.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/pages/setting_page.dart';
|
import 'package:enaklo_pos/presentation/setting/pages/setting_page.dart';
|
||||||
import 'package:enaklo_pos/presentation/table/pages/table_page.dart';
|
import 'package:enaklo_pos/presentation/table/pages/table_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -15,7 +15,6 @@ import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
|||||||
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
|
||||||
import 'package:enaklo_pos/presentation/auth/login_page.dart';
|
import 'package:enaklo_pos/presentation/auth/login_page.dart';
|
||||||
import 'package:enaklo_pos/presentation/report/pages/report_page.dart';
|
import 'package:enaklo_pos/presentation/report/pages/report_page.dart';
|
||||||
import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart';
|
|
||||||
|
|
||||||
import '../../../core/assets/assets.gen.dart';
|
import '../../../core/assets/assets.gen.dart';
|
||||||
import '../../auth/bloc/logout/logout_bloc.dart';
|
import '../../auth/bloc/logout/logout_bloc.dart';
|
||||||
@@ -26,10 +25,13 @@ import 'home_page.dart';
|
|||||||
class DashboardPage extends StatefulWidget {
|
class DashboardPage extends StatefulWidget {
|
||||||
final int? index;
|
final int? index;
|
||||||
final TableModel? table;
|
final TableModel? table;
|
||||||
|
final List<ProductQuantity>? items;
|
||||||
|
|
||||||
const DashboardPage({
|
const DashboardPage({
|
||||||
super.key,
|
super.key,
|
||||||
this.index = 0,
|
this.index = 0,
|
||||||
this.table,
|
this.table,
|
||||||
|
this.items,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -56,8 +58,11 @@ class _DashboardPageState extends State<DashboardPage> {
|
|||||||
HomePage(
|
HomePage(
|
||||||
isTable: false,
|
isTable: false,
|
||||||
table: widget.table,
|
table: widget.table,
|
||||||
|
items: widget.items ?? [],
|
||||||
|
),
|
||||||
|
TablePage(
|
||||||
|
items: widget.items ?? [],
|
||||||
),
|
),
|
||||||
const TablePage(),
|
|
||||||
const ReportPage(),
|
const ReportPage(),
|
||||||
const CustomerPage(),
|
const CustomerPage(),
|
||||||
const SettingPage(),
|
const SettingPage(),
|
||||||
@@ -148,10 +153,6 @@ class _DashboardPageState extends State<DashboardPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
online: () {
|
online: () {
|
||||||
log("🌐 Dashboard: Internet connection detected, triggering sync");
|
|
||||||
context.read<SyncOrderBloc>().add(
|
|
||||||
const SyncOrderEvent.syncOrder(),
|
|
||||||
);
|
|
||||||
return Container(
|
return Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
margin:
|
margin:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
import 'package:enaklo_pos/core/constants/colors.dart';
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
|
import 'package:enaklo_pos/data/models/response/category_response_model.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/category_loader/category_loader_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
|
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -37,14 +36,41 @@ class _CategoryTabBarState extends State<CategoryTabBar>
|
|||||||
context.read<ProductLoaderBloc>().add(
|
context.read<ProductLoaderBloc>().add(
|
||||||
ProductLoaderEvent.getProduct(categoryId: selectedCategoryId),
|
ProductLoaderEvent.getProduct(categoryId: selectedCategoryId),
|
||||||
);
|
);
|
||||||
context
|
|
||||||
.read<CategoryLoaderBloc>()
|
|
||||||
.add(CategoryLoaderEvent.setCategoryId(selectedCategoryId ?? ""));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CategoryTabBar oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
// ✅ Update TabController when categories length changes
|
||||||
|
if (oldWidget.categories.length != widget.categories.length) {
|
||||||
|
_tabController.dispose();
|
||||||
|
_tabController = TabController(
|
||||||
|
length: widget.categories.length,
|
||||||
|
vsync: this,
|
||||||
|
initialIndex: 0, // Reset to first tab
|
||||||
|
);
|
||||||
|
|
||||||
|
_tabController.addListener(() {
|
||||||
|
if (_tabController.indexIsChanging) {
|
||||||
|
if (_tabController.index == 0) {
|
||||||
|
context.read<ProductLoaderBloc>().add(
|
||||||
|
ProductLoaderEvent.getProduct(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
selectedCategoryId = widget.categories[_tabController.index].id;
|
||||||
|
context.read<ProductLoaderBloc>().add(
|
||||||
|
ProductLoaderEvent.getProduct(categoryId: selectedCategoryId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
@@ -53,48 +79,45 @@ class _CategoryTabBarState extends State<CategoryTabBar>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DefaultTabController(
|
return Column(
|
||||||
length: widget.categories.length,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Material(
|
||||||
children: [
|
elevation: 0,
|
||||||
Material(
|
color: Colors.white,
|
||||||
elevation: 0,
|
borderOnForeground: false,
|
||||||
color: Colors.white,
|
child: Padding(
|
||||||
borderOnForeground: false,
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Padding(
|
child: TabBar(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
child: TabBar(
|
|
||||||
controller: _tabController,
|
|
||||||
isScrollable: true,
|
|
||||||
tabAlignment: TabAlignment.start,
|
|
||||||
labelColor: AppColors.primary,
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
dividerColor: AppColors.primary,
|
|
||||||
unselectedLabelColor: AppColors.primary,
|
|
||||||
indicatorSize: TabBarIndicatorSize.label,
|
|
||||||
indicatorWeight: 4,
|
|
||||||
indicatorColor: AppColors.primary,
|
|
||||||
tabs: widget.categories
|
|
||||||
.map((category) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: Tab(text: category.name),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
// ✅ ini bagian penting
|
|
||||||
child: TabBarView(
|
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: widget.tabViews,
|
isScrollable: true,
|
||||||
|
tabAlignment: TabAlignment.start,
|
||||||
|
labelColor: AppColors.primary,
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
dividerColor: AppColors.primary,
|
||||||
|
unselectedLabelColor: AppColors.primary,
|
||||||
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
indicatorWeight: 4,
|
||||||
|
indicatorColor: AppColors.primary,
|
||||||
|
tabs: widget.categories
|
||||||
|
.map((category) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Tab(text: category.name),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
Expanded(
|
||||||
|
// ✅ ini bagian penting
|
||||||
|
child: TabBarView(
|
||||||
|
controller: _tabController,
|
||||||
|
children: widget.tabViews,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class ConfirmPaymentTitle extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: context.deviceHeight * 0.1,
|
height: context.deviceHeight * 0.123,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.white,
|
color: AppColors.white,
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ class HomeRightTitle extends StatelessWidget {
|
|||||||
if (table == null) {
|
if (table == null) {
|
||||||
context.push(DashboardPage(
|
context.push(DashboardPage(
|
||||||
index: 1,
|
index: 1,
|
||||||
|
items: items,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
||||||
import 'package:enaklo_pos/presentation/home/dialog/variant_dialog.dart';
|
import 'package:enaklo_pos/presentation/home/dialog/variant_dialog.dart';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:enaklo_pos/core/constants/variables.dart';
|
import 'package:enaklo_pos/core/constants/variables.dart';
|
||||||
@@ -53,25 +54,47 @@ class ProductCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||||
child: CachedNetworkImage(
|
child: data.imageUrl == ""
|
||||||
imageUrl: (data.imageUrl ?? "").contains('http')
|
? Container(
|
||||||
? data.imageUrl!
|
width: double.infinity,
|
||||||
: '${Variables.baseUrl}/${data.imageUrl}',
|
height: 120,
|
||||||
fit: BoxFit.cover,
|
decoration: BoxDecoration(
|
||||||
width: double.infinity,
|
color: AppColors.disabled.withOpacity(0.4),
|
||||||
height: 120,
|
),
|
||||||
errorWidget: (context, url, error) => Container(
|
child: const Icon(
|
||||||
width: double.infinity,
|
Icons.image,
|
||||||
height: 120,
|
color: AppColors.grey,
|
||||||
decoration: BoxDecoration(
|
),
|
||||||
color: AppColors.disabled.withOpacity(0.4),
|
)
|
||||||
),
|
: CachedNetworkImage(
|
||||||
child: const Icon(
|
imageUrl: (data.imageUrl ?? "").contains('http')
|
||||||
Icons.image,
|
? data.imageUrl!
|
||||||
color: AppColors.grey,
|
: '${Variables.baseUrl}/${data.imageUrl}',
|
||||||
),
|
fit: BoxFit.cover,
|
||||||
),
|
width: double.infinity,
|
||||||
),
|
height: 120,
|
||||||
|
memCacheHeight: 120,
|
||||||
|
memCacheWidth: 120,
|
||||||
|
errorWidget: (context, url, error) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(
|
||||||
|
error,
|
||||||
|
StackTrace.current,
|
||||||
|
reason:
|
||||||
|
'Failed to load image from: $url, productId: ${data.id}, productName: ${data.name}, dataUrl: ${data.imageUrl}',
|
||||||
|
fatal: false,
|
||||||
|
);
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 120,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.disabled.withOpacity(0.4),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.image,
|
||||||
|
color: AppColors.grey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:enaklo_pos/core/extensions/string_ext.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
|
||||||
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
|
||||||
import 'package:enaklo_pos/data/models/response/table_model.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
|
||||||
|
|
||||||
import '../../../core/assets/assets.gen.dart';
|
|
||||||
import '../../../core/components/buttons.dart';
|
|
||||||
import '../../../core/components/spaces.dart';
|
|
||||||
import '../../table/blocs/get_table/get_table_bloc.dart';
|
|
||||||
import '../bloc/checkout/checkout_bloc.dart';
|
|
||||||
import '../models/order_type.dart';
|
|
||||||
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
|
||||||
|
|
||||||
class SaveOrderDialog extends StatefulWidget {
|
|
||||||
const SaveOrderDialog({
|
|
||||||
super.key,
|
|
||||||
required this.data,
|
|
||||||
required this.totalQty,
|
|
||||||
required this.totalPrice,
|
|
||||||
required this.totalTax,
|
|
||||||
required this.totalDiscount,
|
|
||||||
required this.subTotal,
|
|
||||||
required this.normalPrice,
|
|
||||||
required this.table,
|
|
||||||
required this.draftName,
|
|
||||||
});
|
|
||||||
final List<ProductQuantity> data;
|
|
||||||
final int totalQty;
|
|
||||||
final int totalPrice;
|
|
||||||
final int totalTax;
|
|
||||||
final int totalDiscount;
|
|
||||||
final int subTotal;
|
|
||||||
final int normalPrice;
|
|
||||||
final TableModel table;
|
|
||||||
final String draftName;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<SaveOrderDialog> createState() => _SaveOrderDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SaveOrderDialogState extends State<SaveOrderDialog> {
|
|
||||||
// List<ProductQuantity> data = [];
|
|
||||||
// int totalQty = 0;
|
|
||||||
// int totalPrice = 0;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
content: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Center(child: Assets.icons.success.svg()),
|
|
||||||
const SpaceHeight(16.0),
|
|
||||||
const Center(
|
|
||||||
child: Text(
|
|
||||||
'Order Berhasil Disimpan',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SpaceHeight(20.0),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Button.outlined(
|
|
||||||
onPressed: () {
|
|
||||||
context
|
|
||||||
.read<CheckoutBloc>()
|
|
||||||
.add(const CheckoutEvent.started());
|
|
||||||
context
|
|
||||||
.read<GetTableBloc>()
|
|
||||||
.add(const GetTableEvent.getTables());
|
|
||||||
context.popToRoot();
|
|
||||||
},
|
|
||||||
label: 'Kembali',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SpaceWidth(8.0),
|
|
||||||
Flexible(
|
|
||||||
child: BlocBuilder<CheckoutBloc, CheckoutState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final orderType = state.maybeWhen(
|
|
||||||
orElse: () => OrderType.dineIn,
|
|
||||||
loaded: (
|
|
||||||
items,
|
|
||||||
discountModel,
|
|
||||||
discount,
|
|
||||||
discountAmount,
|
|
||||||
tax,
|
|
||||||
serviceCharge,
|
|
||||||
totalQuantity,
|
|
||||||
totalPrice,
|
|
||||||
draftName,
|
|
||||||
orderType,
|
|
||||||
deliveryType,
|
|
||||||
) =>
|
|
||||||
orderType,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Button.filled(
|
|
||||||
onPressed: () async {
|
|
||||||
final checkerPrinter = await ProductLocalDatasource
|
|
||||||
.instance
|
|
||||||
.getPrinterByCode('checker');
|
|
||||||
final kitchenPrinter = await ProductLocalDatasource
|
|
||||||
.instance
|
|
||||||
.getPrinterByCode('kitchen');
|
|
||||||
final barPrinter = await ProductLocalDatasource
|
|
||||||
.instance
|
|
||||||
.getPrinterByCode('bar');
|
|
||||||
|
|
||||||
log("Checker printer: ${checkerPrinter?.toMap()}");
|
|
||||||
log("Kitchen printer: ${kitchenPrinter?.toMap()}");
|
|
||||||
log("Bar printer: ${barPrinter?.toMap()}");
|
|
||||||
|
|
||||||
// Checker printer
|
|
||||||
if (checkerPrinter != null) {
|
|
||||||
try {
|
|
||||||
final printValue = await PrintDataoutputs.instance
|
|
||||||
.printChecker(
|
|
||||||
widget.data,
|
|
||||||
widget.table.tableName!,
|
|
||||||
widget.draftName,
|
|
||||||
'kasir',
|
|
||||||
checkerPrinter.paper.toIntegerFromText,
|
|
||||||
orderType.value);
|
|
||||||
|
|
||||||
await PrinterService().printWithPrinter(
|
|
||||||
checkerPrinter, printValue, context);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing checker: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content:
|
|
||||||
Text('Error printing checker: $e')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kitchen printer
|
|
||||||
if (kitchenPrinter != null) {
|
|
||||||
try {
|
|
||||||
final printValue =
|
|
||||||
await PrintDataoutputs.instance.printKitchen(
|
|
||||||
widget.data,
|
|
||||||
widget.table.tableName!,
|
|
||||||
widget.draftName,
|
|
||||||
'kasir',
|
|
||||||
kitchenPrinter.paper.toIntegerFromText,
|
|
||||||
orderType.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
await PrinterService().printWithPrinter(
|
|
||||||
kitchenPrinter, printValue, context);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing kitchen order: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'Error printing kitchen order: $e')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bar printer
|
|
||||||
if (barPrinter != null) {
|
|
||||||
try {
|
|
||||||
final printValue =
|
|
||||||
await PrintDataoutputs.instance.printBar(
|
|
||||||
widget.data,
|
|
||||||
widget.table.tableName!,
|
|
||||||
widget.draftName,
|
|
||||||
'kasir',
|
|
||||||
barPrinter.paper.toIntegerFromText,
|
|
||||||
orderType.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
await PrinterService().printWithPrinter(
|
|
||||||
barPrinter, printValue, context);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing bar order: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content:
|
|
||||||
Text('Error printing bar order: $e')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: 'Print Checker',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
|
||||||
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
|
||||||
import 'package:enaklo_pos/core/extensions/string_ext.dart';
|
|
||||||
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
|
||||||
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
|
|
||||||
import 'package:enaklo_pos/core/utils/printer_service.dart';
|
|
||||||
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
|
|
||||||
|
|
||||||
import '../../../core/assets/assets.gen.dart';
|
|
||||||
import '../../../core/components/buttons.dart';
|
|
||||||
import '../../../core/components/spaces.dart';
|
|
||||||
import '../../table/blocs/get_table/get_table_bloc.dart';
|
|
||||||
import '../bloc/checkout/checkout_bloc.dart';
|
|
||||||
import '../bloc/order/order_bloc.dart';
|
|
||||||
|
|
||||||
class SuccessPaymentDialog extends StatefulWidget {
|
|
||||||
const SuccessPaymentDialog({
|
|
||||||
Key? key,
|
|
||||||
required this.data,
|
|
||||||
required this.totalQty,
|
|
||||||
required this.totalPrice,
|
|
||||||
required this.totalTax,
|
|
||||||
required this.totalDiscount,
|
|
||||||
required this.subTotal,
|
|
||||||
required this.normalPrice,
|
|
||||||
required this.totalService,
|
|
||||||
required this.draftName,
|
|
||||||
this.isTablePaymentPage = false,
|
|
||||||
}) : super(key: key);
|
|
||||||
final List<ProductQuantity> data;
|
|
||||||
final int totalQty;
|
|
||||||
final int totalPrice;
|
|
||||||
final int totalTax;
|
|
||||||
final int totalDiscount;
|
|
||||||
final int subTotal;
|
|
||||||
final int normalPrice;
|
|
||||||
final int totalService;
|
|
||||||
final String draftName;
|
|
||||||
final bool? isTablePaymentPage;
|
|
||||||
@override
|
|
||||||
State<SuccessPaymentDialog> createState() => _SuccessPaymentDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SuccessPaymentDialogState extends State<SuccessPaymentDialog> {
|
|
||||||
// List<ProductQuantity> data = [];
|
|
||||||
// int totalQty = 0;
|
|
||||||
// int totalPrice = 0;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
content: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Center(child: Assets.icons.success.svg()),
|
|
||||||
const SpaceHeight(16.0),
|
|
||||||
const Center(
|
|
||||||
child: Text(
|
|
||||||
'Pembayaran telah sukses dilakukan',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SpaceHeight(20.0),
|
|
||||||
const Text('METODE BAYAR'),
|
|
||||||
const SpaceHeight(5.0),
|
|
||||||
BlocBuilder<OrderBloc, OrderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final paymentMethod = state.maybeWhen(
|
|
||||||
orElse: () => 'Cash',
|
|
||||||
loaded: (model, orderId) => model.paymentMethod,
|
|
||||||
);
|
|
||||||
return Text(
|
|
||||||
paymentMethod,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SpaceHeight(10.0),
|
|
||||||
const Divider(),
|
|
||||||
const SpaceHeight(8.0),
|
|
||||||
const Text('TOTAL TAGIHAN'),
|
|
||||||
const SpaceHeight(5.0),
|
|
||||||
BlocBuilder<OrderBloc, OrderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
state.maybeWhen(
|
|
||||||
orElse: () => 0,
|
|
||||||
loaded: (model, orderId) => model.total,
|
|
||||||
);
|
|
||||||
return Text(
|
|
||||||
widget.totalPrice.currencyFormatRp,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SpaceHeight(10.0),
|
|
||||||
const Divider(),
|
|
||||||
const SpaceHeight(8.0),
|
|
||||||
const Text('NOMINAL BAYAR'),
|
|
||||||
const SpaceHeight(5.0),
|
|
||||||
BlocBuilder<OrderBloc, OrderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final paymentAmount = state.maybeWhen(
|
|
||||||
orElse: () => 0,
|
|
||||||
loaded: (model, orderId) => model.paymentAmount,
|
|
||||||
);
|
|
||||||
return Text(
|
|
||||||
paymentAmount.ceil().currencyFormatRp,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
const SpaceHeight(8.0),
|
|
||||||
const Text('KEMBALIAN'),
|
|
||||||
const SpaceHeight(5.0),
|
|
||||||
BlocBuilder<OrderBloc, OrderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final paymentAmount = state.maybeWhen(
|
|
||||||
orElse: () => 0,
|
|
||||||
loaded: (model, orderId) => model.paymentAmount,
|
|
||||||
);
|
|
||||||
final total = state.maybeWhen(
|
|
||||||
orElse: () => 0,
|
|
||||||
loaded: (model, orderId) => model.total,
|
|
||||||
);
|
|
||||||
final diff = paymentAmount - total;
|
|
||||||
log("DIFF: $diff paymentAmount: $paymentAmount total: $total");
|
|
||||||
return Text(
|
|
||||||
diff.ceil().currencyFormatRp,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SpaceHeight(10.0),
|
|
||||||
const Divider(),
|
|
||||||
const SpaceHeight(8.0),
|
|
||||||
const Text('WAKTU PEMBAYARAN'),
|
|
||||||
const SpaceHeight(5.0),
|
|
||||||
Text(
|
|
||||||
DateFormat('dd MMMM yyyy, HH:mm').format(DateTime.now()),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SpaceHeight(20.0),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Button.outlined(
|
|
||||||
onPressed: () {
|
|
||||||
// For table payment page, just close the dialog
|
|
||||||
// The cleanup and navigation is handled by the payment page
|
|
||||||
if (widget.isTablePaymentPage == true) {
|
|
||||||
Navigator.of(context).pop(); // Close dialog only
|
|
||||||
} else {
|
|
||||||
// For regular payment flow, reset and go to root
|
|
||||||
context
|
|
||||||
.read<CheckoutBloc>()
|
|
||||||
.add(const CheckoutEvent.started());
|
|
||||||
context
|
|
||||||
.read<GetTableBloc>()
|
|
||||||
.add(const GetTableEvent.getTables());
|
|
||||||
context.popToRoot();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: 'Kembali',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SpaceWidth(8.0),
|
|
||||||
Flexible(
|
|
||||||
child: BlocBuilder<OrderBloc, OrderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final paymentAmount = state.maybeWhen(
|
|
||||||
orElse: () => 0,
|
|
||||||
loaded: (model, orderId) => model.paymentAmount,
|
|
||||||
);
|
|
||||||
|
|
||||||
final kembalian = paymentAmount - widget.totalPrice;
|
|
||||||
return BlocBuilder<CheckoutBloc, CheckoutState>(
|
|
||||||
builder: (context, checkoutState) {
|
|
||||||
final orderType = checkoutState.maybeWhen(
|
|
||||||
orElse: () => OrderType.dineIn,
|
|
||||||
loaded: (
|
|
||||||
items,
|
|
||||||
discountModel,
|
|
||||||
discount,
|
|
||||||
discountAmount,
|
|
||||||
tax,
|
|
||||||
serviceCharge,
|
|
||||||
totalQuantity,
|
|
||||||
totalPrice,
|
|
||||||
draftName,
|
|
||||||
orderType,
|
|
||||||
deliveryType,
|
|
||||||
) =>
|
|
||||||
orderType,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Button.filled(
|
|
||||||
onPressed: () async {
|
|
||||||
final receiptPrinter =
|
|
||||||
await ProductLocalDatasource.instance
|
|
||||||
.getPrinterByCode('receipt');
|
|
||||||
final kitchenPrinter =
|
|
||||||
await ProductLocalDatasource.instance
|
|
||||||
.getPrinterByCode('kitchen');
|
|
||||||
final barPrinter = await ProductLocalDatasource
|
|
||||||
.instance
|
|
||||||
.getPrinterByCode('bar');
|
|
||||||
|
|
||||||
// Receipt Printer
|
|
||||||
if (receiptPrinter != null) {
|
|
||||||
try {
|
|
||||||
final settingsLocalDatasource =
|
|
||||||
SettingsLocalDatasource();
|
|
||||||
final taxModel =
|
|
||||||
await settingsLocalDatasource.getTax();
|
|
||||||
final serviceChargeValue =
|
|
||||||
await settingsLocalDatasource
|
|
||||||
.getServiceCharge();
|
|
||||||
|
|
||||||
// Get the actual payment method from OrderBloc
|
|
||||||
final paymentMethod = state.maybeWhen(
|
|
||||||
orElse: () => 'Cash',
|
|
||||||
loaded: (model, orderId) =>
|
|
||||||
model.paymentMethod,
|
|
||||||
);
|
|
||||||
|
|
||||||
final printValue = await PrintDataoutputs
|
|
||||||
.instance
|
|
||||||
.printOrderV3(
|
|
||||||
widget.data,
|
|
||||||
widget.totalQty,
|
|
||||||
widget.totalPrice,
|
|
||||||
paymentMethod,
|
|
||||||
paymentAmount,
|
|
||||||
kembalian,
|
|
||||||
widget.subTotal,
|
|
||||||
widget.totalDiscount,
|
|
||||||
widget.totalTax,
|
|
||||||
widget.totalService,
|
|
||||||
'kasir',
|
|
||||||
widget.draftName,
|
|
||||||
receiptPrinter.paper.toIntegerFromText,
|
|
||||||
taxPercentage: taxModel.value,
|
|
||||||
serviceChargePercentage: serviceChargeValue,
|
|
||||||
);
|
|
||||||
|
|
||||||
await PrinterService().printWithPrinter(
|
|
||||||
receiptPrinter, printValue, context);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing receipt: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content:
|
|
||||||
Text('Error printing receipt: $e')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kitchen Printer
|
|
||||||
if (kitchenPrinter != null &&
|
|
||||||
widget.isTablePaymentPage == false) {
|
|
||||||
try {
|
|
||||||
final printValue = await PrintDataoutputs
|
|
||||||
.instance
|
|
||||||
.printKitchen(
|
|
||||||
widget.data,
|
|
||||||
'',
|
|
||||||
widget.draftName,
|
|
||||||
'kasir',
|
|
||||||
kitchenPrinter.paper.toIntegerFromText,
|
|
||||||
orderType.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
await PrinterService().printWithPrinter(
|
|
||||||
kitchenPrinter, printValue, context);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing kitchen order: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'Error printing kitchen order: $e')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bar printer
|
|
||||||
if (barPrinter != null &&
|
|
||||||
widget.isTablePaymentPage == false) {
|
|
||||||
try {
|
|
||||||
final printValue =
|
|
||||||
await PrintDataoutputs.instance.printBar(
|
|
||||||
widget.data,
|
|
||||||
'',
|
|
||||||
widget.draftName,
|
|
||||||
'kasir',
|
|
||||||
barPrinter.paper.toIntegerFromText,
|
|
||||||
orderType.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
await PrinterService().printWithPrinter(
|
|
||||||
barPrinter, printValue, context);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error printing bar order: $e");
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'Error printing bar order: $e')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: 'Print',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_method
|
|||||||
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
|
||||||
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
|
import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart';
|
||||||
import 'package:enaklo_pos/presentation/success/pages/success_payment_page.dart';
|
import 'package:enaklo_pos/presentation/success/pages/success_payment_page.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/success/pages/success_split_bill_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@@ -370,23 +371,43 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
state.maybeWhen(
|
state.maybeWhen(
|
||||||
orElse: () {},
|
orElse: () {},
|
||||||
success: (data) {
|
success: (data) {
|
||||||
context.pushReplacement(SuccessPaymentPage(
|
if (widget.isSplit) {
|
||||||
productQuantity: widget.order.orderItems
|
context.pushReplacement(SuccessSplitBillPage(
|
||||||
?.map(
|
productQuantity: getOrderItemPending()
|
||||||
(item) => ProductQuantity(
|
.map(
|
||||||
product: Product(
|
(item) => ProductQuantity(
|
||||||
name: item.productName,
|
product: Product(
|
||||||
price: item.unitPrice,
|
name: item.productName,
|
||||||
),
|
price: item.unitPrice,
|
||||||
quantity: item.quantity ?? 0,
|
|
||||||
),
|
),
|
||||||
)
|
quantity: item.quantity ?? 0,
|
||||||
.toList() ??
|
),
|
||||||
[],
|
)
|
||||||
payment: data,
|
.toList(),
|
||||||
paymentMethod: selectedPaymentMethod?.name ?? '',
|
payment: data,
|
||||||
nominalBayar: totalPriceController.text.toIntegerFromText,
|
paymentMethod: selectedPaymentMethod?.name ?? '',
|
||||||
));
|
nominalBayar:
|
||||||
|
totalPriceController.text.toIntegerFromText,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
context.pushReplacement(SuccessPaymentPage(
|
||||||
|
productQuantity: getOrderItemPending()
|
||||||
|
.map(
|
||||||
|
(item) => ProductQuantity(
|
||||||
|
product: Product(
|
||||||
|
name: item.productName,
|
||||||
|
price: item.unitPrice,
|
||||||
|
),
|
||||||
|
quantity: item.quantity ?? 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
payment: data,
|
||||||
|
paymentMethod: selectedPaymentMethod?.name ?? '',
|
||||||
|
nominalBayar:
|
||||||
|
totalPriceController.text.toIntegerFromText,
|
||||||
|
));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: (message) {
|
error: (message) {
|
||||||
AppFlushbar.showError(context, message);
|
AppFlushbar.showError(context, message);
|
||||||
@@ -443,6 +464,7 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
final itemPending = widget.order.orderItems
|
final itemPending = widget.order.orderItems
|
||||||
?.where((item) => item.status == "pending")
|
?.where((item) => item.status == "pending")
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (widget.isSplit == false) {
|
if (widget.isSplit == false) {
|
||||||
final request = PaymentRequestModel(
|
final request = PaymentRequestModel(
|
||||||
amount: widget.order.totalAmount ?? 0,
|
amount: widget.order.totalAmount ?? 0,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'package:enaklo_pos/core/components/spaces.dart';
|
import 'package:enaklo_pos/core/components/spaces.dart';
|
||||||
import 'package:enaklo_pos/core/constants/colors.dart';
|
import 'package:enaklo_pos/core/constants/colors.dart';
|
||||||
|
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
|
||||||
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
import 'package:enaklo_pos/core/extensions/int_ext.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class RefundSuccessDialog extends StatelessWidget {
|
class RefundSuccessDialog extends StatelessWidget {
|
||||||
@@ -102,8 +104,7 @@ class RefundSuccessDialog extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
context.push(DashboardPage());
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.primary,
|
backgroundColor: AppColors.primary,
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'category_report_event.dart';
|
||||||
|
part 'category_report_state.dart';
|
||||||
|
part 'category_report_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class CategoryReportBloc
|
||||||
|
extends Bloc<CategoryReportEvent, CategoryReportState> {
|
||||||
|
final AnalyticRemoteDatasource _datasource;
|
||||||
|
CategoryReportBloc(this._datasource) : super(CategoryReportState.initial()) {
|
||||||
|
on<_Get>((event, emit) async {
|
||||||
|
emit(_Loading());
|
||||||
|
|
||||||
|
final result = await _datasource.getCategory(
|
||||||
|
dateFrom: event.startDate,
|
||||||
|
dateTo: event.endDate,
|
||||||
|
);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(l) => emit(_Error(l)),
|
||||||
|
(r) => emit(_Loaded(r.data!)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+205
-210
@@ -3,7 +3,7 @@
|
|||||||
// ignore_for_file: type=lint
|
// 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
|
// 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 'sync_order_bloc.dart';
|
part of 'category_report_bloc.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// FreezedGenerator
|
// FreezedGenerator
|
||||||
@@ -15,133 +15,187 @@ 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');
|
'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
|
/// @nodoc
|
||||||
mixin _$SyncOrderEvent {
|
mixin _$CategoryReportEvent {
|
||||||
|
DateTime get startDate => throw _privateConstructorUsedError;
|
||||||
|
DateTime get endDate => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(DateTime startDate, DateTime endDate) get,
|
||||||
required TResult Function() syncOrder,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult? Function()? syncOrder,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult Function()? syncOrder,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Get value) get,
|
||||||
required TResult Function(_SyncOrder value) syncOrder,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Get value)? get,
|
||||||
TResult? Function(_SyncOrder value)? syncOrder,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Get value)? get,
|
||||||
TResult Function(_SyncOrder value)? syncOrder,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of CategoryReportEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$CategoryReportEventCopyWith<CategoryReportEvent> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class $SyncOrderEventCopyWith<$Res> {
|
abstract class $CategoryReportEventCopyWith<$Res> {
|
||||||
factory $SyncOrderEventCopyWith(
|
factory $CategoryReportEventCopyWith(
|
||||||
SyncOrderEvent value, $Res Function(SyncOrderEvent) then) =
|
CategoryReportEvent value, $Res Function(CategoryReportEvent) then) =
|
||||||
_$SyncOrderEventCopyWithImpl<$Res, SyncOrderEvent>;
|
_$CategoryReportEventCopyWithImpl<$Res, CategoryReportEvent>;
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class _$SyncOrderEventCopyWithImpl<$Res, $Val extends SyncOrderEvent>
|
class _$CategoryReportEventCopyWithImpl<$Res, $Val extends CategoryReportEvent>
|
||||||
implements $SyncOrderEventCopyWith<$Res> {
|
implements $CategoryReportEventCopyWith<$Res> {
|
||||||
_$SyncOrderEventCopyWithImpl(this._value, this._then);
|
_$CategoryReportEventCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Val _value;
|
final $Val _value;
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Res Function($Val) _then;
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
/// Create a copy of SyncOrderEvent
|
/// Create a copy of CategoryReportEvent
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? startDate = null,
|
||||||
|
Object? endDate = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
startDate: null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
endDate: null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class _$$StartedImplCopyWith<$Res> {
|
abstract class _$$GetImplCopyWith<$Res>
|
||||||
factory _$$StartedImplCopyWith(
|
implements $CategoryReportEventCopyWith<$Res> {
|
||||||
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
|
factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) =
|
||||||
__$$StartedImplCopyWithImpl<$Res>;
|
__$$GetImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$StartedImplCopyWithImpl<$Res>
|
class __$$GetImplCopyWithImpl<$Res>
|
||||||
extends _$SyncOrderEventCopyWithImpl<$Res, _$StartedImpl>
|
extends _$CategoryReportEventCopyWithImpl<$Res, _$GetImpl>
|
||||||
implements _$$StartedImplCopyWith<$Res> {
|
implements _$$GetImplCopyWith<$Res> {
|
||||||
__$$StartedImplCopyWithImpl(
|
__$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then)
|
||||||
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
|
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of SyncOrderEvent
|
/// Create a copy of CategoryReportEvent
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? startDate = null,
|
||||||
|
Object? endDate = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$GetImpl(
|
||||||
|
startDate: null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
endDate: null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$StartedImpl implements _Started {
|
class _$GetImpl implements _Get {
|
||||||
const _$StartedImpl();
|
const _$GetImpl({required this.startDate, required this.endDate});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DateTime startDate;
|
||||||
|
@override
|
||||||
|
final DateTime endDate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SyncOrderEvent.started()';
|
return 'CategoryReportEvent.get(startDate: $startDate, endDate: $endDate)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType && other is _$StartedImpl);
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$GetImpl &&
|
||||||
|
(identical(other.startDate, startDate) ||
|
||||||
|
other.startDate == startDate) &&
|
||||||
|
(identical(other.endDate, endDate) || other.endDate == endDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode => Object.hash(runtimeType, startDate, endDate);
|
||||||
|
|
||||||
|
/// Create a copy of CategoryReportEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
|
||||||
|
__$$GetImplCopyWithImpl<_$GetImpl>(this, _$identity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(DateTime startDate, DateTime endDate) get,
|
||||||
required TResult Function() syncOrder,
|
|
||||||
}) {
|
}) {
|
||||||
return started();
|
return get(startDate, endDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult? Function()? syncOrder,
|
|
||||||
}) {
|
}) {
|
||||||
return started?.call();
|
return get?.call(startDate, endDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult Function()? syncOrder,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (get != null) {
|
||||||
return started();
|
return get(startDate, endDate);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
@@ -149,151 +203,57 @@ class _$StartedImpl implements _Started {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Get value) get,
|
||||||
required TResult Function(_SyncOrder value) syncOrder,
|
|
||||||
}) {
|
}) {
|
||||||
return started(this);
|
return get(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Get value)? get,
|
||||||
TResult? Function(_SyncOrder value)? syncOrder,
|
|
||||||
}) {
|
}) {
|
||||||
return started?.call(this);
|
return get?.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Get value)? get,
|
||||||
TResult Function(_SyncOrder value)? syncOrder,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (get != null) {
|
||||||
return started(this);
|
return get(this);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Started implements SyncOrderEvent {
|
abstract class _Get implements CategoryReportEvent {
|
||||||
const factory _Started() = _$StartedImpl;
|
const factory _Get(
|
||||||
}
|
{required final DateTime startDate,
|
||||||
|
required final DateTime endDate}) = _$GetImpl;
|
||||||
|
|
||||||
/// @nodoc
|
@override
|
||||||
abstract class _$$SyncOrderImplCopyWith<$Res> {
|
DateTime get startDate;
|
||||||
factory _$$SyncOrderImplCopyWith(
|
@override
|
||||||
_$SyncOrderImpl value, $Res Function(_$SyncOrderImpl) then) =
|
DateTime get endDate;
|
||||||
__$$SyncOrderImplCopyWithImpl<$Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
/// Create a copy of CategoryReportEvent
|
||||||
class __$$SyncOrderImplCopyWithImpl<$Res>
|
|
||||||
extends _$SyncOrderEventCopyWithImpl<$Res, _$SyncOrderImpl>
|
|
||||||
implements _$$SyncOrderImplCopyWith<$Res> {
|
|
||||||
__$$SyncOrderImplCopyWithImpl(
|
|
||||||
_$SyncOrderImpl _value, $Res Function(_$SyncOrderImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of SyncOrderEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
mixin _$CategoryReportState {
|
||||||
class _$SyncOrderImpl implements _SyncOrder {
|
|
||||||
const _$SyncOrderImpl();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'SyncOrderEvent.syncOrder()';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType && other is _$SyncOrderImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => runtimeType.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() started,
|
|
||||||
required TResult Function() syncOrder,
|
|
||||||
}) {
|
|
||||||
return syncOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? started,
|
|
||||||
TResult? Function()? syncOrder,
|
|
||||||
}) {
|
|
||||||
return syncOrder?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? started,
|
|
||||||
TResult Function()? syncOrder,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (syncOrder != null) {
|
|
||||||
return syncOrder();
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Started value) started,
|
|
||||||
required TResult Function(_SyncOrder value) syncOrder,
|
|
||||||
}) {
|
|
||||||
return syncOrder(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Started value)? started,
|
|
||||||
TResult? Function(_SyncOrder value)? syncOrder,
|
|
||||||
}) {
|
|
||||||
return syncOrder?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Started value)? started,
|
|
||||||
TResult Function(_SyncOrder value)? syncOrder,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (syncOrder != null) {
|
|
||||||
return syncOrder(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _SyncOrder implements SyncOrderEvent {
|
|
||||||
const factory _SyncOrder() = _$SyncOrderImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$SyncOrderState {
|
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function() loaded,
|
required TResult Function(CategoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@@ -301,7 +261,7 @@ mixin _$SyncOrderState {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function()? loaded,
|
TResult? Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@@ -309,7 +269,7 @@ mixin _$SyncOrderState {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function()? loaded,
|
TResult Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
@@ -342,23 +302,23 @@ mixin _$SyncOrderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class $SyncOrderStateCopyWith<$Res> {
|
abstract class $CategoryReportStateCopyWith<$Res> {
|
||||||
factory $SyncOrderStateCopyWith(
|
factory $CategoryReportStateCopyWith(
|
||||||
SyncOrderState value, $Res Function(SyncOrderState) then) =
|
CategoryReportState value, $Res Function(CategoryReportState) then) =
|
||||||
_$SyncOrderStateCopyWithImpl<$Res, SyncOrderState>;
|
_$CategoryReportStateCopyWithImpl<$Res, CategoryReportState>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class _$SyncOrderStateCopyWithImpl<$Res, $Val extends SyncOrderState>
|
class _$CategoryReportStateCopyWithImpl<$Res, $Val extends CategoryReportState>
|
||||||
implements $SyncOrderStateCopyWith<$Res> {
|
implements $CategoryReportStateCopyWith<$Res> {
|
||||||
_$SyncOrderStateCopyWithImpl(this._value, this._then);
|
_$CategoryReportStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Val _value;
|
final $Val _value;
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Res Function($Val) _then;
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,13 +331,13 @@ abstract class _$$InitialImplCopyWith<$Res> {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$InitialImplCopyWithImpl<$Res>
|
class __$$InitialImplCopyWithImpl<$Res>
|
||||||
extends _$SyncOrderStateCopyWithImpl<$Res, _$InitialImpl>
|
extends _$CategoryReportStateCopyWithImpl<$Res, _$InitialImpl>
|
||||||
implements _$$InitialImplCopyWith<$Res> {
|
implements _$$InitialImplCopyWith<$Res> {
|
||||||
__$$InitialImplCopyWithImpl(
|
__$$InitialImplCopyWithImpl(
|
||||||
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +348,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SyncOrderState.initial()';
|
return 'CategoryReportState.initial()';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -405,7 +365,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function() loaded,
|
required TResult Function(CategoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return initial();
|
return initial();
|
||||||
@@ -416,7 +376,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function()? loaded,
|
TResult? Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return initial?.call();
|
return initial?.call();
|
||||||
@@ -427,7 +387,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function()? loaded,
|
TResult Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@@ -475,7 +435,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Initial implements SyncOrderState {
|
abstract class _Initial implements CategoryReportState {
|
||||||
const factory _Initial() = _$InitialImpl;
|
const factory _Initial() = _$InitialImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,13 +448,13 @@ abstract class _$$LoadingImplCopyWith<$Res> {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$LoadingImplCopyWithImpl<$Res>
|
class __$$LoadingImplCopyWithImpl<$Res>
|
||||||
extends _$SyncOrderStateCopyWithImpl<$Res, _$LoadingImpl>
|
extends _$CategoryReportStateCopyWithImpl<$Res, _$LoadingImpl>
|
||||||
implements _$$LoadingImplCopyWith<$Res> {
|
implements _$$LoadingImplCopyWith<$Res> {
|
||||||
__$$LoadingImplCopyWithImpl(
|
__$$LoadingImplCopyWithImpl(
|
||||||
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
|
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,7 +465,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SyncOrderState.loading()';
|
return 'CategoryReportState.loading()';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -522,7 +482,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function() loaded,
|
required TResult Function(CategoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return loading();
|
return loading();
|
||||||
@@ -533,7 +493,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function()? loaded,
|
TResult? Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return loading?.call();
|
return loading?.call();
|
||||||
@@ -544,7 +504,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function()? loaded,
|
TResult Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@@ -592,7 +552,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Loading implements SyncOrderState {
|
abstract class _Loading implements CategoryReportState {
|
||||||
const factory _Loading() = _$LoadingImpl;
|
const factory _Loading() = _$LoadingImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,48 +561,75 @@ abstract class _$$LoadedImplCopyWith<$Res> {
|
|||||||
factory _$$LoadedImplCopyWith(
|
factory _$$LoadedImplCopyWith(
|
||||||
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
||||||
__$$LoadedImplCopyWithImpl<$Res>;
|
__$$LoadedImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({CategoryAnalyticData data});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$LoadedImplCopyWithImpl<$Res>
|
class __$$LoadedImplCopyWithImpl<$Res>
|
||||||
extends _$SyncOrderStateCopyWithImpl<$Res, _$LoadedImpl>
|
extends _$CategoryReportStateCopyWithImpl<$Res, _$LoadedImpl>
|
||||||
implements _$$LoadedImplCopyWith<$Res> {
|
implements _$$LoadedImplCopyWith<$Res> {
|
||||||
__$$LoadedImplCopyWithImpl(
|
__$$LoadedImplCopyWithImpl(
|
||||||
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
|
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? data = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$LoadedImpl(
|
||||||
|
null == data
|
||||||
|
? _value.data
|
||||||
|
: data // ignore: cast_nullable_to_non_nullable
|
||||||
|
as CategoryAnalyticData,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$LoadedImpl implements _Loaded {
|
class _$LoadedImpl implements _Loaded {
|
||||||
const _$LoadedImpl();
|
const _$LoadedImpl(this.data);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final CategoryAnalyticData data;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SyncOrderState.loaded()';
|
return 'CategoryReportState.loaded(data: $data)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType && other is _$LoadedImpl);
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$LoadedImpl &&
|
||||||
|
(identical(other.data, data) || other.data == data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode => Object.hash(runtimeType, data);
|
||||||
|
|
||||||
|
/// Create a copy of CategoryReportState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
|
||||||
|
__$$LoadedImplCopyWithImpl<_$LoadedImpl>(this, _$identity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function() loaded,
|
required TResult Function(CategoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return loaded();
|
return loaded(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -650,10 +637,10 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function()? loaded,
|
TResult? Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return loaded?.call();
|
return loaded?.call(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -661,12 +648,12 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function()? loaded,
|
TResult Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (loaded != null) {
|
if (loaded != null) {
|
||||||
return loaded();
|
return loaded(data);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
@@ -709,8 +696,16 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Loaded implements SyncOrderState {
|
abstract class _Loaded implements CategoryReportState {
|
||||||
const factory _Loaded() = _$LoadedImpl;
|
const factory _Loaded(final CategoryAnalyticData data) = _$LoadedImpl;
|
||||||
|
|
||||||
|
CategoryAnalyticData get data;
|
||||||
|
|
||||||
|
/// Create a copy of CategoryReportState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -724,13 +719,13 @@ abstract class _$$ErrorImplCopyWith<$Res> {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$ErrorImplCopyWithImpl<$Res>
|
class __$$ErrorImplCopyWithImpl<$Res>
|
||||||
extends _$SyncOrderStateCopyWithImpl<$Res, _$ErrorImpl>
|
extends _$CategoryReportStateCopyWithImpl<$Res, _$ErrorImpl>
|
||||||
implements _$$ErrorImplCopyWith<$Res> {
|
implements _$$ErrorImplCopyWith<$Res> {
|
||||||
__$$ErrorImplCopyWithImpl(
|
__$$ErrorImplCopyWithImpl(
|
||||||
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
|
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
@@ -756,7 +751,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SyncOrderState.error(message: $message)';
|
return 'CategoryReportState.error(message: $message)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -770,7 +765,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType, message);
|
int get hashCode => Object.hash(runtimeType, message);
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// 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)
|
||||||
@override
|
@override
|
||||||
@@ -783,7 +778,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function() loaded,
|
required TResult Function(CategoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return error(message);
|
return error(message);
|
||||||
@@ -794,7 +789,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function()? loaded,
|
TResult? Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return error?.call(message);
|
return error?.call(message);
|
||||||
@@ -805,7 +800,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function()? loaded,
|
TResult Function(CategoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@@ -853,12 +848,12 @@ class _$ErrorImpl implements _Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Error implements SyncOrderState {
|
abstract class _Error implements CategoryReportState {
|
||||||
const factory _Error(final String message) = _$ErrorImpl;
|
const factory _Error(final String message) = _$ErrorImpl;
|
||||||
|
|
||||||
String get message;
|
String get message;
|
||||||
|
|
||||||
/// Create a copy of SyncOrderState
|
/// Create a copy of CategoryReportState
|
||||||
/// 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)
|
||||||
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
|
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
part of 'category_report_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class CategoryReportEvent with _$CategoryReportEvent {
|
||||||
|
const factory CategoryReportEvent.get({
|
||||||
|
required DateTime startDate,
|
||||||
|
required DateTime endDate,
|
||||||
|
}) = _Get;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
part of 'category_report_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class CategoryReportState with _$CategoryReportState {
|
||||||
|
const factory CategoryReportState.initial() = _Initial;
|
||||||
|
const factory CategoryReportState.loading() = _Loading;
|
||||||
|
const factory CategoryReportState.loaded(CategoryAnalyticData data) = _Loaded;
|
||||||
|
const factory CategoryReportState.error(String message) = _Error;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/inventory_analytic_response_model.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'inventory_report_event.dart';
|
||||||
|
part 'inventory_report_state.dart';
|
||||||
|
part 'inventory_report_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class InventoryReportBloc
|
||||||
|
extends Bloc<InventoryReportEvent, InventoryReportState> {
|
||||||
|
final AnalyticRemoteDatasource _datasource;
|
||||||
|
InventoryReportBloc(this._datasource)
|
||||||
|
: super(InventoryReportState.initial()) {
|
||||||
|
on<_Get>((event, emit) async {
|
||||||
|
emit(_Loading());
|
||||||
|
|
||||||
|
final result = await _datasource.getInventory(
|
||||||
|
dateFrom: event.startDate, dateTo: event.endDate);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(f) => emit(_Error(f)),
|
||||||
|
(r) => emit(
|
||||||
|
_Loaded(r.data!),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+182
-226
@@ -3,7 +3,7 @@
|
|||||||
// ignore_for_file: type=lint
|
// 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
|
// 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 'local_product_bloc.dart';
|
part of 'inventory_report_bloc.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// FreezedGenerator
|
// FreezedGenerator
|
||||||
@@ -15,133 +15,188 @@ 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');
|
'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
|
/// @nodoc
|
||||||
mixin _$LocalProductEvent {
|
mixin _$InventoryReportEvent {
|
||||||
|
DateTime get startDate => throw _privateConstructorUsedError;
|
||||||
|
DateTime get endDate => throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(DateTime startDate, DateTime endDate) get,
|
||||||
required TResult Function() getLocalProduct,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult? Function()? getLocalProduct,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult Function()? getLocalProduct,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Get value) get,
|
||||||
required TResult Function(_GetLocalProduct value) getLocalProduct,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Get value)? get,
|
||||||
TResult? Function(_GetLocalProduct value)? getLocalProduct,
|
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Get value)? get,
|
||||||
TResult Function(_GetLocalProduct value)? getLocalProduct,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryReportEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$InventoryReportEventCopyWith<InventoryReportEvent> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class $LocalProductEventCopyWith<$Res> {
|
abstract class $InventoryReportEventCopyWith<$Res> {
|
||||||
factory $LocalProductEventCopyWith(
|
factory $InventoryReportEventCopyWith(InventoryReportEvent value,
|
||||||
LocalProductEvent value, $Res Function(LocalProductEvent) then) =
|
$Res Function(InventoryReportEvent) then) =
|
||||||
_$LocalProductEventCopyWithImpl<$Res, LocalProductEvent>;
|
_$InventoryReportEventCopyWithImpl<$Res, InventoryReportEvent>;
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class _$LocalProductEventCopyWithImpl<$Res, $Val extends LocalProductEvent>
|
class _$InventoryReportEventCopyWithImpl<$Res,
|
||||||
implements $LocalProductEventCopyWith<$Res> {
|
$Val extends InventoryReportEvent>
|
||||||
_$LocalProductEventCopyWithImpl(this._value, this._then);
|
implements $InventoryReportEventCopyWith<$Res> {
|
||||||
|
_$InventoryReportEventCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Val _value;
|
final $Val _value;
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Res Function($Val) _then;
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
/// Create a copy of LocalProductEvent
|
/// Create a copy of InventoryReportEvent
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? startDate = null,
|
||||||
|
Object? endDate = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
startDate: null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
endDate: null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class _$$StartedImplCopyWith<$Res> {
|
abstract class _$$GetImplCopyWith<$Res>
|
||||||
factory _$$StartedImplCopyWith(
|
implements $InventoryReportEventCopyWith<$Res> {
|
||||||
_$StartedImpl value, $Res Function(_$StartedImpl) then) =
|
factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) =
|
||||||
__$$StartedImplCopyWithImpl<$Res>;
|
__$$GetImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$StartedImplCopyWithImpl<$Res>
|
class __$$GetImplCopyWithImpl<$Res>
|
||||||
extends _$LocalProductEventCopyWithImpl<$Res, _$StartedImpl>
|
extends _$InventoryReportEventCopyWithImpl<$Res, _$GetImpl>
|
||||||
implements _$$StartedImplCopyWith<$Res> {
|
implements _$$GetImplCopyWith<$Res> {
|
||||||
__$$StartedImplCopyWithImpl(
|
__$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then)
|
||||||
_$StartedImpl _value, $Res Function(_$StartedImpl) _then)
|
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of LocalProductEvent
|
/// Create a copy of InventoryReportEvent
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? startDate = null,
|
||||||
|
Object? endDate = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$GetImpl(
|
||||||
|
startDate: null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
endDate: null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$StartedImpl implements _Started {
|
class _$GetImpl implements _Get {
|
||||||
const _$StartedImpl();
|
const _$GetImpl({required this.startDate, required this.endDate});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DateTime startDate;
|
||||||
|
@override
|
||||||
|
final DateTime endDate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocalProductEvent.started()';
|
return 'InventoryReportEvent.get(startDate: $startDate, endDate: $endDate)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType && other is _$StartedImpl);
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$GetImpl &&
|
||||||
|
(identical(other.startDate, startDate) ||
|
||||||
|
other.startDate == startDate) &&
|
||||||
|
(identical(other.endDate, endDate) || other.endDate == endDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode => Object.hash(runtimeType, startDate, endDate);
|
||||||
|
|
||||||
|
/// Create a copy of InventoryReportEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
|
||||||
|
__$$GetImplCopyWithImpl<_$GetImpl>(this, _$identity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() started,
|
required TResult Function(DateTime startDate, DateTime endDate) get,
|
||||||
required TResult Function() getLocalProduct,
|
|
||||||
}) {
|
}) {
|
||||||
return started();
|
return get(startDate, endDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? started,
|
TResult? Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult? Function()? getLocalProduct,
|
|
||||||
}) {
|
}) {
|
||||||
return started?.call();
|
return get?.call(startDate, endDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? started,
|
TResult Function(DateTime startDate, DateTime endDate)? get,
|
||||||
TResult Function()? getLocalProduct,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (get != null) {
|
||||||
return started();
|
return get(startDate, endDate);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
@@ -149,151 +204,57 @@ class _$StartedImpl implements _Started {
|
|||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult map<TResult extends Object?>({
|
TResult map<TResult extends Object?>({
|
||||||
required TResult Function(_Started value) started,
|
required TResult Function(_Get value) get,
|
||||||
required TResult Function(_GetLocalProduct value) getLocalProduct,
|
|
||||||
}) {
|
}) {
|
||||||
return started(this);
|
return get(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
TResult? Function(_Started value)? started,
|
TResult? Function(_Get value)? get,
|
||||||
TResult? Function(_GetLocalProduct value)? getLocalProduct,
|
|
||||||
}) {
|
}) {
|
||||||
return started?.call(this);
|
return get?.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult maybeMap<TResult extends Object?>({
|
TResult maybeMap<TResult extends Object?>({
|
||||||
TResult Function(_Started value)? started,
|
TResult Function(_Get value)? get,
|
||||||
TResult Function(_GetLocalProduct value)? getLocalProduct,
|
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (started != null) {
|
if (get != null) {
|
||||||
return started(this);
|
return get(this);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Started implements LocalProductEvent {
|
abstract class _Get implements InventoryReportEvent {
|
||||||
const factory _Started() = _$StartedImpl;
|
const factory _Get(
|
||||||
}
|
{required final DateTime startDate,
|
||||||
|
required final DateTime endDate}) = _$GetImpl;
|
||||||
|
|
||||||
/// @nodoc
|
@override
|
||||||
abstract class _$$GetLocalProductImplCopyWith<$Res> {
|
DateTime get startDate;
|
||||||
factory _$$GetLocalProductImplCopyWith(_$GetLocalProductImpl value,
|
@override
|
||||||
$Res Function(_$GetLocalProductImpl) then) =
|
DateTime get endDate;
|
||||||
__$$GetLocalProductImplCopyWithImpl<$Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
/// Create a copy of InventoryReportEvent
|
||||||
class __$$GetLocalProductImplCopyWithImpl<$Res>
|
|
||||||
extends _$LocalProductEventCopyWithImpl<$Res, _$GetLocalProductImpl>
|
|
||||||
implements _$$GetLocalProductImplCopyWith<$Res> {
|
|
||||||
__$$GetLocalProductImplCopyWithImpl(
|
|
||||||
_$GetLocalProductImpl _value, $Res Function(_$GetLocalProductImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of LocalProductEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
mixin _$InventoryReportState {
|
||||||
class _$GetLocalProductImpl implements _GetLocalProduct {
|
|
||||||
const _$GetLocalProductImpl();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'LocalProductEvent.getLocalProduct()';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType && other is _$GetLocalProductImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => runtimeType.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function() started,
|
|
||||||
required TResult Function() getLocalProduct,
|
|
||||||
}) {
|
|
||||||
return getLocalProduct();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function()? started,
|
|
||||||
TResult? Function()? getLocalProduct,
|
|
||||||
}) {
|
|
||||||
return getLocalProduct?.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function()? started,
|
|
||||||
TResult Function()? getLocalProduct,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (getLocalProduct != null) {
|
|
||||||
return getLocalProduct();
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Started value) started,
|
|
||||||
required TResult Function(_GetLocalProduct value) getLocalProduct,
|
|
||||||
}) {
|
|
||||||
return getLocalProduct(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Started value)? started,
|
|
||||||
TResult? Function(_GetLocalProduct value)? getLocalProduct,
|
|
||||||
}) {
|
|
||||||
return getLocalProduct?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Started value)? started,
|
|
||||||
TResult Function(_GetLocalProduct value)? getLocalProduct,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (getLocalProduct != null) {
|
|
||||||
return getLocalProduct(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _GetLocalProduct implements LocalProductEvent {
|
|
||||||
const factory _GetLocalProduct() = _$GetLocalProductImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$LocalProductState {
|
|
||||||
@optionalTypeArgs
|
@optionalTypeArgs
|
||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(InventoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@@ -301,7 +262,7 @@ mixin _$LocalProductState {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) =>
|
}) =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
@@ -309,7 +270,7 @@ mixin _$LocalProductState {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) =>
|
}) =>
|
||||||
@@ -342,23 +303,24 @@ mixin _$LocalProductState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
abstract class $LocalProductStateCopyWith<$Res> {
|
abstract class $InventoryReportStateCopyWith<$Res> {
|
||||||
factory $LocalProductStateCopyWith(
|
factory $InventoryReportStateCopyWith(InventoryReportState value,
|
||||||
LocalProductState value, $Res Function(LocalProductState) then) =
|
$Res Function(InventoryReportState) then) =
|
||||||
_$LocalProductStateCopyWithImpl<$Res, LocalProductState>;
|
_$InventoryReportStateCopyWithImpl<$Res, InventoryReportState>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class _$LocalProductStateCopyWithImpl<$Res, $Val extends LocalProductState>
|
class _$InventoryReportStateCopyWithImpl<$Res,
|
||||||
implements $LocalProductStateCopyWith<$Res> {
|
$Val extends InventoryReportState>
|
||||||
_$LocalProductStateCopyWithImpl(this._value, this._then);
|
implements $InventoryReportStateCopyWith<$Res> {
|
||||||
|
_$InventoryReportStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Val _value;
|
final $Val _value;
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final $Res Function($Val) _then;
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,13 +333,13 @@ abstract class _$$InitialImplCopyWith<$Res> {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$InitialImplCopyWithImpl<$Res>
|
class __$$InitialImplCopyWithImpl<$Res>
|
||||||
extends _$LocalProductStateCopyWithImpl<$Res, _$InitialImpl>
|
extends _$InventoryReportStateCopyWithImpl<$Res, _$InitialImpl>
|
||||||
implements _$$InitialImplCopyWith<$Res> {
|
implements _$$InitialImplCopyWith<$Res> {
|
||||||
__$$InitialImplCopyWithImpl(
|
__$$InitialImplCopyWithImpl(
|
||||||
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +350,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocalProductState.initial()';
|
return 'InventoryReportState.initial()';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -405,7 +367,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(InventoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return initial();
|
return initial();
|
||||||
@@ -416,7 +378,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return initial?.call();
|
return initial?.call();
|
||||||
@@ -427,7 +389,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@@ -475,7 +437,7 @@ class _$InitialImpl implements _Initial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Initial implements LocalProductState {
|
abstract class _Initial implements InventoryReportState {
|
||||||
const factory _Initial() = _$InitialImpl;
|
const factory _Initial() = _$InitialImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,13 +450,13 @@ abstract class _$$LoadingImplCopyWith<$Res> {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$LoadingImplCopyWithImpl<$Res>
|
class __$$LoadingImplCopyWithImpl<$Res>
|
||||||
extends _$LocalProductStateCopyWithImpl<$Res, _$LoadingImpl>
|
extends _$InventoryReportStateCopyWithImpl<$Res, _$LoadingImpl>
|
||||||
implements _$$LoadingImplCopyWith<$Res> {
|
implements _$$LoadingImplCopyWith<$Res> {
|
||||||
__$$LoadingImplCopyWithImpl(
|
__$$LoadingImplCopyWithImpl(
|
||||||
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
|
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,7 +467,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocalProductState.loading()';
|
return 'InventoryReportState.loading()';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -522,7 +484,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(InventoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return loading();
|
return loading();
|
||||||
@@ -533,7 +495,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return loading?.call();
|
return loading?.call();
|
||||||
@@ -544,7 +506,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@@ -592,7 +554,7 @@ class _$LoadingImpl implements _Loading {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Loading implements LocalProductState {
|
abstract class _Loading implements InventoryReportState {
|
||||||
const factory _Loading() = _$LoadingImpl;
|
const factory _Loading() = _$LoadingImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,29 +564,29 @@ abstract class _$$LoadedImplCopyWith<$Res> {
|
|||||||
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
|
||||||
__$$LoadedImplCopyWithImpl<$Res>;
|
__$$LoadedImplCopyWithImpl<$Res>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({List<Product> products});
|
$Res call({InventoryAnalyticData data});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$LoadedImplCopyWithImpl<$Res>
|
class __$$LoadedImplCopyWithImpl<$Res>
|
||||||
extends _$LocalProductStateCopyWithImpl<$Res, _$LoadedImpl>
|
extends _$InventoryReportStateCopyWithImpl<$Res, _$LoadedImpl>
|
||||||
implements _$$LoadedImplCopyWith<$Res> {
|
implements _$$LoadedImplCopyWith<$Res> {
|
||||||
__$$LoadedImplCopyWithImpl(
|
__$$LoadedImplCopyWithImpl(
|
||||||
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
|
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? products = null,
|
Object? data = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$LoadedImpl(
|
return _then(_$LoadedImpl(
|
||||||
null == products
|
null == data
|
||||||
? _value._products
|
? _value.data
|
||||||
: products // ignore: cast_nullable_to_non_nullable
|
: data // ignore: cast_nullable_to_non_nullable
|
||||||
as List<Product>,
|
as InventoryAnalyticData,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -632,19 +594,14 @@ class __$$LoadedImplCopyWithImpl<$Res>
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
||||||
class _$LoadedImpl implements _Loaded {
|
class _$LoadedImpl implements _Loaded {
|
||||||
const _$LoadedImpl(final List<Product> products) : _products = products;
|
const _$LoadedImpl(this.data);
|
||||||
|
|
||||||
final List<Product> _products;
|
|
||||||
@override
|
@override
|
||||||
List<Product> get products {
|
final InventoryAnalyticData data;
|
||||||
if (_products is EqualUnmodifiableListView) return _products;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_products);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocalProductState.loaded(products: $products)';
|
return 'InventoryReportState.loaded(data: $data)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -652,14 +609,13 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$LoadedImpl &&
|
other is _$LoadedImpl &&
|
||||||
const DeepCollectionEquality().equals(other._products, _products));
|
(identical(other.data, data) || other.data == data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(runtimeType, data);
|
||||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_products));
|
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// 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)
|
||||||
@override
|
@override
|
||||||
@@ -672,10 +628,10 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(InventoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return loaded(products);
|
return loaded(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -683,10 +639,10 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return loaded?.call(products);
|
return loaded?.call(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -694,12 +650,12 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
if (loaded != null) {
|
if (loaded != null) {
|
||||||
return loaded(products);
|
return loaded(data);
|
||||||
}
|
}
|
||||||
return orElse();
|
return orElse();
|
||||||
}
|
}
|
||||||
@@ -742,12 +698,12 @@ class _$LoadedImpl implements _Loaded {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Loaded implements LocalProductState {
|
abstract class _Loaded implements InventoryReportState {
|
||||||
const factory _Loaded(final List<Product> products) = _$LoadedImpl;
|
const factory _Loaded(final InventoryAnalyticData data) = _$LoadedImpl;
|
||||||
|
|
||||||
List<Product> get products;
|
InventoryAnalyticData get data;
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// 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)
|
||||||
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
|
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
|
||||||
@@ -765,13 +721,13 @@ abstract class _$$ErrorImplCopyWith<$Res> {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
class __$$ErrorImplCopyWithImpl<$Res>
|
class __$$ErrorImplCopyWithImpl<$Res>
|
||||||
extends _$LocalProductStateCopyWithImpl<$Res, _$ErrorImpl>
|
extends _$InventoryReportStateCopyWithImpl<$Res, _$ErrorImpl>
|
||||||
implements _$$ErrorImplCopyWith<$Res> {
|
implements _$$ErrorImplCopyWith<$Res> {
|
||||||
__$$ErrorImplCopyWithImpl(
|
__$$ErrorImplCopyWithImpl(
|
||||||
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
|
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
|
||||||
: super(_value, _then);
|
: super(_value, _then);
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
@@ -797,7 +753,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocalProductState.error(message: $message)';
|
return 'InventoryReportState.error(message: $message)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -811,7 +767,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType, message);
|
int get hashCode => Object.hash(runtimeType, message);
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// 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)
|
||||||
@override
|
@override
|
||||||
@@ -824,7 +780,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult when<TResult extends Object?>({
|
TResult when<TResult extends Object?>({
|
||||||
required TResult Function() initial,
|
required TResult Function() initial,
|
||||||
required TResult Function() loading,
|
required TResult Function() loading,
|
||||||
required TResult Function(List<Product> products) loaded,
|
required TResult Function(InventoryAnalyticData data) loaded,
|
||||||
required TResult Function(String message) error,
|
required TResult Function(String message) error,
|
||||||
}) {
|
}) {
|
||||||
return error(message);
|
return error(message);
|
||||||
@@ -835,7 +791,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult? whenOrNull<TResult extends Object?>({
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
TResult? Function()? initial,
|
TResult? Function()? initial,
|
||||||
TResult? Function()? loading,
|
TResult? Function()? loading,
|
||||||
TResult? Function(List<Product> products)? loaded,
|
TResult? Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult? Function(String message)? error,
|
TResult? Function(String message)? error,
|
||||||
}) {
|
}) {
|
||||||
return error?.call(message);
|
return error?.call(message);
|
||||||
@@ -846,7 +802,7 @@ class _$ErrorImpl implements _Error {
|
|||||||
TResult maybeWhen<TResult extends Object?>({
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
TResult Function()? initial,
|
TResult Function()? initial,
|
||||||
TResult Function()? loading,
|
TResult Function()? loading,
|
||||||
TResult Function(List<Product> products)? loaded,
|
TResult Function(InventoryAnalyticData data)? loaded,
|
||||||
TResult Function(String message)? error,
|
TResult Function(String message)? error,
|
||||||
required TResult orElse(),
|
required TResult orElse(),
|
||||||
}) {
|
}) {
|
||||||
@@ -894,12 +850,12 @@ class _$ErrorImpl implements _Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Error implements LocalProductState {
|
abstract class _Error implements InventoryReportState {
|
||||||
const factory _Error(final String message) = _$ErrorImpl;
|
const factory _Error(final String message) = _$ErrorImpl;
|
||||||
|
|
||||||
String get message;
|
String get message;
|
||||||
|
|
||||||
/// Create a copy of LocalProductState
|
/// Create a copy of InventoryReportState
|
||||||
/// 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)
|
||||||
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
|
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
part of 'inventory_report_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryReportEvent with _$InventoryReportEvent {
|
||||||
|
const factory InventoryReportEvent.get({
|
||||||
|
required DateTime startDate,
|
||||||
|
required DateTime endDate,
|
||||||
|
}) = _Get;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
part of 'inventory_report_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryReportState with _$InventoryReportState {
|
||||||
|
const factory InventoryReportState.initial() = _Initial;
|
||||||
|
const factory InventoryReportState.loading() = _Loading;
|
||||||
|
const factory InventoryReportState.loaded(InventoryAnalyticData data) =
|
||||||
|
_Loaded;
|
||||||
|
const factory InventoryReportState.error(String message) = _Error;
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ class PaymentMethodReportBloc
|
|||||||
(l) => emit(_Error(l)),
|
(l) => emit(_Error(l)),
|
||||||
(r) => emit(
|
(r) => emit(
|
||||||
_Loaded(
|
_Loaded(
|
||||||
r.data,
|
r.data!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {
|
|||||||
dateFrom: event.startDate,
|
dateFrom: event.startDate,
|
||||||
dateTo: event.endDate,
|
dateTo: event.endDate,
|
||||||
);
|
);
|
||||||
result.fold((l) => emit(_Error(l)), (r) => emit(_Success(r.data)));
|
result.fold((l) => emit(_Error(l)), (r) => emit(_Success(r.data!)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/datasources/outlet/outlet_local_datasource.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/product_analytic_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/data/models/response/profit_loss_response_model.dart';
|
||||||
|
import 'package:enaklo_pos/presentation/home/models/outlet_model.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'report_event.dart';
|
||||||
|
part 'report_state.dart';
|
||||||
|
part 'report_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class ReportBloc extends Bloc<ReportEvent, ReportState> {
|
||||||
|
final AnalyticRemoteDatasource _datasource;
|
||||||
|
ReportBloc(this._datasource) : super(ReportState.initial()) {
|
||||||
|
on<_Get>((event, emit) async {
|
||||||
|
emit(_Loading());
|
||||||
|
|
||||||
|
final outlet = await OutletLocalDatasource().get();
|
||||||
|
|
||||||
|
final category = await _datasource.getCategory(
|
||||||
|
dateFrom: event.startDate, dateTo: event.endDate);
|
||||||
|
|
||||||
|
final product = await _datasource.getProduct(
|
||||||
|
dateFrom: event.startDate, dateTo: event.endDate);
|
||||||
|
|
||||||
|
final paymentMethod = await _datasource.getPaymentMethod(
|
||||||
|
dateFrom: event.startDate, dateTo: event.endDate);
|
||||||
|
|
||||||
|
final profitLoss = await _datasource.getProfitLoss(
|
||||||
|
dateFrom: event.startDate, dateTo: event.endDate);
|
||||||
|
|
||||||
|
if (category.isLeft() ||
|
||||||
|
product.isLeft() ||
|
||||||
|
paymentMethod.isLeft() ||
|
||||||
|
profitLoss.isLeft()) {
|
||||||
|
emit(_Error());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(_Loaded(
|
||||||
|
outlet,
|
||||||
|
category
|
||||||
|
.getOrElse(
|
||||||
|
() => CategoryAnalyticResponseModel(success: false, data: null))
|
||||||
|
.data,
|
||||||
|
profitLoss
|
||||||
|
.getOrElse(
|
||||||
|
() => ProfitLossResponseModel(success: false, data: null))
|
||||||
|
.data,
|
||||||
|
paymentMethod
|
||||||
|
.getOrElse(() =>
|
||||||
|
PaymentMethodAnalyticResponseModel(success: false, data: null))
|
||||||
|
.data,
|
||||||
|
product
|
||||||
|
.getOrElse(
|
||||||
|
() => ProductAnalyticResponseModel(success: false, data: null))
|
||||||
|
.data,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+437
-409
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user