Files
kooltab/lib/services/export_service.dart
T
2026-10-08 04:12:36 +02:00

90 lines
2.7 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../models/closed_tab.dart';
import '../models/closed_tab_item.dart';
import '../utils/app_config.dart';
import 'bar_tab_service.dart';
abstract class ExportService {
Future<void> uploadToServer();
}
/// Uploads the closed-tab history to the backup server. Best effort: a
/// failed backup is logged but never interrupts the bar.
class DefaultExportService implements ExportService {
final BarTabService barTabService;
final http.Client _client;
DefaultExportService({required this.barTabService, http.Client? client})
: _client = client ?? http.Client();
@override
Future<void> uploadToServer() async {
try {
final closedTabs = await barTabService.getClosedTabs();
final appVersion = (await PackageInfo.fromPlatform()).version;
final response = await _client.post(
Uri.parse(kExportServerUrl),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(_buildPayload(closedTabs, appVersion: appVersion)),
);
if (response.statusCode != 200 && response.statusCode != 201) {
unawaited(
Sentry.captureMessage(
'Backup upload failed with HTTP ${response.statusCode}',
level: SentryLevel.warning,
),
);
}
} on Exception catch (e) {
// Mostly offline tablets; not worth an error report.
debugPrint('ExportService: backup upload failed: $e');
}
}
Map<String, dynamic> _buildPayload(
List<ClosedTab> tabs, {
required String appVersion,
}) {
return {
'exportedAt': DateTime.now().toIso8601String(),
'appVersion': appVersion,
'closedTabs': tabs.map(_tabToJson).toList(),
};
}
Map<String, dynamic> _tabToJson(ClosedTab tab) {
return {
'id': tab.id,
'originalTabId': tab.originalTabId,
'customerName': tab.customerName,
'closedAt': tab.closedAt.toIso8601String(),
'paymentMethod': tab.paymentMethod.value,
'items': tab.items.map(_itemToJson).toList(),
'itemCount': tab.itemCount,
'totalInCents': tab.totalInCents,
};
}
Map<String, dynamic> _itemToJson(ClosedTabItem item) {
return {
'productId': item.productId,
'productName': item.productName,
'quantity': item.quantity,
'unitPriceInCents': item.unitPriceInCents,
'purchasedAt': item.purchasedAt?.toIso8601String(),
'removedAt': item.removedAt?.toIso8601String(),
'isRemoved': item.isRemoved,
'lineTotalInCents': item.lineTotalInCents,
};
}
}