import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; import 'package:cashumit/models/local_receipt.dart'; import 'package:cashumit/models/receipt_item.dart'; import 'package:cashumit/services/firefly_api_service.dart'; class LocalReceiptService { static const String _receiptsKey = 'local_receipts'; /// Save a receipt to local storage static Future saveReceipt(LocalReceipt receipt) async { final prefs = await SharedPreferences.getInstance(); final receipts = await getReceipts(); // Check if receipt with the same ID already exists final existingIndex = receipts.indexWhere((r) => r.id == receipt.id); if (existingIndex != -1) { // Replace existing receipt receipts[existingIndex] = receipt; } else { // Add new receipt receipts.add(receipt); } // Convert receipts to JSON strings final receiptJsonList = receipts.map((receipt) => json.encode(receipt.toJson())).toList(); await prefs.setStringList(_receiptsKey, receiptJsonList); } /// Get all receipts from local storage static Future> getReceipts() async { final prefs = await SharedPreferences.getInstance(); final receiptJsonList = prefs.getStringList(_receiptsKey) ?? []; return receiptJsonList .map((jsonString) => LocalReceipt.fromJson(json.decode(jsonString))) .toList(); } /// Get unsubmitted receipts from local storage static Future> getUnsubmittedReceipts() async { final allReceipts = await getReceipts(); return allReceipts.where((receipt) => !receipt.isSubmitted).toList(); } /// Get submitted receipts from local storage static Future> getSubmittedReceipts() async { final allReceipts = await getReceipts(); return allReceipts.where((receipt) => receipt.isSubmitted).toList(); } /// Update a receipt's submission status static Future updateReceiptSubmissionStatus(String receiptId, {bool? isSubmitted, String? submissionError}) async { final prefs = await SharedPreferences.getInstance(); final receipts = await getReceipts(); final receiptIndex = receipts.indexWhere((r) => r.id == receiptId); if (receiptIndex != -1) { final receipt = receipts[receiptIndex]; receipts[receiptIndex] = receipt.copyWith( isSubmitted: isSubmitted ?? receipt.isSubmitted, submissionError: submissionError ?? receipt.submissionError, submittedAt: isSubmitted == true ? DateTime.now() : receipt.submittedAt, ); // Convert receipts to JSON strings final receiptJsonList = receipts.map((receipt) => json.encode(receipt.toJson())).toList(); await prefs.setStringList(_receiptsKey, receiptJsonList); } } /// Remove a receipt from local storage static Future removeReceipt(String receiptId) async { final prefs = await SharedPreferences.getInstance(); final receipts = await getReceipts(); final filteredReceipts = receipts.where((receipt) => receipt.id != receiptId).toList(); // Convert receipts to JSON strings final receiptJsonList = filteredReceipts .map((receipt) => json.encode(receipt.toJson())) .toList(); await prefs.setStringList(_receiptsKey, receiptJsonList); } /// Clear all receipts from local storage static Future clearAllReceipts() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove(_receiptsKey); } /// Submit a single receipt to FireFly III static Future submitReceiptToServer( LocalReceipt receipt, String baseUrl, String accessToken, ) async { try { // Check if the receipt is already submitted if (receipt.isSubmitted) { return true; // Already submitted } // Validate required fields if (receipt.sourceAccountId == null || receipt.destinationAccountId == null) { await updateReceiptSubmissionStatus(receipt.id, isSubmitted: false, submissionError: 'Missing account information'); return false; } // Submit to FireFly III final transactionId = await _submitToFireFly( receipt: receipt, baseUrl: baseUrl, accessToken: accessToken, ); if (transactionId != null) { // Update receipt status to submitted await updateReceiptSubmissionStatus(receipt.id, isSubmitted: true, submissionError: null); return true; } else { await updateReceiptSubmissionStatus(receipt.id, isSubmitted: false, submissionError: 'Failed to submit to server'); return false; } } catch (e) { await updateReceiptSubmissionStatus(receipt.id, isSubmitted: false, submissionError: e.toString()); return false; } } /// Submit all unsubmitted receipts to FireFly III static Future> submitAllUnsubmittedReceipts( String baseUrl, String accessToken, ) async { final unsubmittedReceipts = await getUnsubmittedReceipts(); final results = {}; int successCount = 0; int failureCount = 0; for (final receipt in unsubmittedReceipts) { try { final success = await submitReceiptToServer(receipt, baseUrl, accessToken); results[receipt.id] = success; if (success) { successCount++; } else { failureCount++; } } catch (e) { results[receipt.id] = false; failureCount++; // Update the receipt with the error await updateReceiptSubmissionStatus(receipt.id, isSubmitted: false, submissionError: e.toString()); } } return { 'results': results, 'successCount': successCount, 'failureCount': failureCount, 'totalCount': unsubmittedReceipts.length, }; } /// Private method to handle actual submission to FireFly III static Future _submitToFireFly({ required LocalReceipt receipt, required String baseUrl, required String accessToken, }) async { try { final transactionId = await FireflyApiService.submitDummyTransaction( baseUrl: baseUrl, accessToken: accessToken, sourceId: receipt.sourceAccountId!, destinationId: receipt.destinationAccountId!, type: 'deposit', // Assuming deposit for receipts description: receipt.transactionDescription ?? _generateTransactionDescription(receipt.items), date: '${receipt.transactionDate.year}-${receipt.transactionDate.month.toString().padLeft(2, '0')}-${receipt.transactionDate.day.toString().padLeft(2, '0')}', amount: receipt.total.toStringAsFixed(2), ); return transactionId; } catch (e) { print('Error submitting receipt to FireFly III: $e'); return null; } } /// Generate transaction description static String _generateTransactionDescription(List items) { if (items.isEmpty) { return 'Transaksi Struk Belanja'; } // Take the first 5 item descriptions final itemNames = items.take(5).map((item) => item.description).toList(); // If there are more than 5 items, append ', dll' to the last item if (items.length > 5) { itemNames[4] += ', dll'; } // Join the item names with ', ' return itemNames.join(', '); } }