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 uploadToServer(); } class DefaultExportService implements ExportService { final AppDatabase database; DefaultExportService({required this.database}); @override Future 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) { // Should prob log this one tho } } catch (_) { // if this fails who cares } } Future> _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, purchasedAt: row.purchasedAt?.toIso8601String(), removedAt: row.removedAt?.toIso8601String(), ), ) .toList(), ), ); } return result; } Map _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, 'purchasedAt': item.purchasedAt, 'removedAt': item.removedAt, 'isRemoved': item.isRemoved, 'lineTotalInCents': item.lineTotalInCents, }, ) .toList(), 'itemCount': tab.items.fold( 0, (sum, item) => sum + (item.isRemoved ? 0 : item.quantity), ), 'totalInCents': tab.items.fold( 0, (sum, item) => sum + item.lineTotalInCents, ), }, ) .toList(), }; } } class _ClosedTabItemExport { final String productId; final String productName; final int quantity; final int unitPriceInCents; final String? purchasedAt; final String? removedAt; _ClosedTabItemExport({ required this.productId, required this.productName, required this.quantity, required this.unitPriceInCents, required this.purchasedAt, required this.removedAt, }); bool get isRemoved => removedAt != null; int get lineTotalInCents => isRemoved ? 0 : quantity * 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, }); }