feat: add export service
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import '../database/app_database.dart';
|
import '../database/app_database.dart';
|
||||||
import '../services/default_product_seeder.dart';
|
import '../services/default_product_seeder.dart';
|
||||||
|
import '../services/export_service.dart';
|
||||||
import '../viewmodels/bar_screen_view_model.dart';
|
import '../viewmodels/bar_screen_view_model.dart';
|
||||||
import '../viewmodels/product_list_view_model.dart';
|
import '../viewmodels/product_list_view_model.dart';
|
||||||
|
|
||||||
@@ -28,6 +29,8 @@ class _AppBootstrapState extends State<AppBootstrap> {
|
|||||||
|
|
||||||
context.read<ProductListViewModel>().ensureLoaded();
|
context.read<ProductListViewModel>().ensureLoaded();
|
||||||
context.read<BarScreenViewModel>().ensureLoaded();
|
context.read<BarScreenViewModel>().ensureLoaded();
|
||||||
|
|
||||||
|
DefaultExportService(database: database).uploadToServer();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../database/app_database.dart';
|
||||||
|
import '../utils/app_config.dart';
|
||||||
|
|
||||||
|
abstract class ExportService {
|
||||||
|
Future<void> uploadToServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultExportService implements ExportService {
|
||||||
|
final AppDatabase database;
|
||||||
|
|
||||||
|
DefaultExportService({required this.database});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> uploadToServer() async {
|
||||||
|
try {
|
||||||
|
final closedTabs = await _getAllClosedTabs();
|
||||||
|
final exportData = _buildExportPayload(closedTabs);
|
||||||
|
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse(kExportServerUrl),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(exportData),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||||
|
// Silently fail - this is a non-critical background operation
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Silently fail - this is a non-critical background operation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<_ClosedTabExport>> _getAllClosedTabs() async {
|
||||||
|
final tabQuery = database.select(database.closedTabs)
|
||||||
|
..orderBy([(tab) => OrderingTerm.desc(tab.closedAt)]);
|
||||||
|
|
||||||
|
final tabRows = await tabQuery.get();
|
||||||
|
|
||||||
|
final result = <_ClosedTabExport>[];
|
||||||
|
|
||||||
|
for (final tabRow in tabRows) {
|
||||||
|
final itemsQuery = database.select(database.closedTabItems)
|
||||||
|
..where((item) => item.closedTabId.equals(tabRow.id));
|
||||||
|
|
||||||
|
final itemRows = await itemsQuery.get();
|
||||||
|
|
||||||
|
result.add(_ClosedTabExport(
|
||||||
|
id: tabRow.id,
|
||||||
|
originalTabId: tabRow.originalTabId,
|
||||||
|
customerName: tabRow.customerName,
|
||||||
|
closedAt: tabRow.closedAt.toIso8601String(),
|
||||||
|
paymentMethod: tabRow.paymentMethod,
|
||||||
|
items: itemRows.map((row) => _ClosedTabItemExport(
|
||||||
|
productId: row.productId,
|
||||||
|
productName: row.productName,
|
||||||
|
quantity: row.quantity,
|
||||||
|
unitPriceInCents: row.unitPriceInCents,
|
||||||
|
)).toList(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _buildExportPayload(List<_ClosedTabExport> tabs) {
|
||||||
|
return {
|
||||||
|
'exportedAt': DateTime.now().toIso8601String(),
|
||||||
|
'appVersion': '1.0.7',
|
||||||
|
'closedTabs': tabs.map((tab) => {
|
||||||
|
'id': tab.id,
|
||||||
|
'originalTabId': tab.originalTabId,
|
||||||
|
'customerName': tab.customerName,
|
||||||
|
'closedAt': tab.closedAt,
|
||||||
|
'paymentMethod': tab.paymentMethod,
|
||||||
|
'items': tab.items.map((item) => {
|
||||||
|
'productId': item.productId,
|
||||||
|
'productName': item.productName,
|
||||||
|
'quantity': item.quantity,
|
||||||
|
'unitPriceInCents': item.unitPriceInCents,
|
||||||
|
'lineTotalInCents': item.quantity * item.unitPriceInCents,
|
||||||
|
}).toList(),
|
||||||
|
'itemCount': tab.items.fold(0, (sum, item) => sum + item.quantity),
|
||||||
|
'totalInCents': tab.items.fold(0, (sum, item) => sum + item.quantity * item.unitPriceInCents),
|
||||||
|
}).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ClosedTabItemExport {
|
||||||
|
final String productId;
|
||||||
|
final String productName;
|
||||||
|
final int quantity;
|
||||||
|
final int unitPriceInCents;
|
||||||
|
|
||||||
|
_ClosedTabItemExport({
|
||||||
|
required this.productId,
|
||||||
|
required this.productName,
|
||||||
|
required this.quantity,
|
||||||
|
required this.unitPriceInCents,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ClosedTabExport {
|
||||||
|
final String id;
|
||||||
|
final String originalTabId;
|
||||||
|
final String customerName;
|
||||||
|
final String closedAt;
|
||||||
|
final String paymentMethod;
|
||||||
|
final List<_ClosedTabItemExport> items;
|
||||||
|
|
||||||
|
_ClosedTabExport({
|
||||||
|
required this.id,
|
||||||
|
required this.originalTabId,
|
||||||
|
required this.customerName,
|
||||||
|
required this.closedAt,
|
||||||
|
required this.paymentMethod,
|
||||||
|
required this.items,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
const kUpdateServerUrl = 'https://updater.brammie15.dev';
|
const kUpdateServerUrl = 'https://updater.brammie15.dev';
|
||||||
|
const kExportServerUrl = 'https://your-server.com/api/backup';
|
||||||
Reference in New Issue
Block a user