265 lines
8.5 KiB
Dart
265 lines
8.5 KiB
Dart
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<void> 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<List<LocalReceipt>> 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<List<LocalReceipt>> getUnsubmittedReceipts() async {
|
|
final allReceipts = await getReceipts();
|
|
return allReceipts.where((receipt) => !receipt.isSubmitted).toList();
|
|
}
|
|
|
|
/// Get submitted receipts from local storage
|
|
static Future<List<LocalReceipt>> getSubmittedReceipts() async {
|
|
final allReceipts = await getReceipts();
|
|
return allReceipts.where((receipt) => receipt.isSubmitted).toList();
|
|
}
|
|
|
|
/// Update a receipt's submission status
|
|
static Future<void> 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<void> 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<void> clearAllReceipts() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.remove(_receiptsKey);
|
|
}
|
|
|
|
/// Submit a single receipt to FireFly III
|
|
static Future<bool> 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<Map<String, dynamic>> submitAllUnsubmittedReceipts(
|
|
String baseUrl,
|
|
String accessToken,
|
|
) async {
|
|
// Cek koneksi server terlebih dahulu
|
|
final isServerAvailable = await FireflyApiService.testConnection(
|
|
baseUrl: baseUrl,
|
|
);
|
|
|
|
if (!isServerAvailable) {
|
|
// Jika server tidak tersedia, hanya kembalikan status tanpa mencoba submit
|
|
final unsubmittedReceipts = await getUnsubmittedReceipts();
|
|
final results = <String, bool>{};
|
|
|
|
for (final receipt in unsubmittedReceipts) {
|
|
results[receipt.id] = false;
|
|
}
|
|
|
|
return {
|
|
'results': results,
|
|
'successCount': 0,
|
|
'failureCount': unsubmittedReceipts.length,
|
|
'totalCount': unsubmittedReceipts.length,
|
|
'serverAvailable': false,
|
|
'message': 'Server tidak tersedia, tidak ada transaksi yang dikirim',
|
|
};
|
|
}
|
|
|
|
final unsubmittedReceipts = await getUnsubmittedReceipts();
|
|
final results = <String, bool>{};
|
|
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,
|
|
'serverAvailable': true,
|
|
};
|
|
}
|
|
|
|
/// Private method to handle actual submission to FireFly III
|
|
static Future<String?> _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),
|
|
);
|
|
|
|
// Jika transaksi berhasil dikirim, simpan transactionId dan URL
|
|
if (transactionId != null) {
|
|
// Update receipt dengan transactionId dan URL
|
|
final updatedReceipt = receipt.copyWith(
|
|
fireflyTransactionId: transactionId,
|
|
fireflyTransactionUrl: '$baseUrl/transactions/show/$transactionId',
|
|
);
|
|
|
|
// Simpan kembali receipt yang diperbarui ke storage
|
|
await saveReceipt(updatedReceipt);
|
|
}
|
|
|
|
return transactionId;
|
|
} catch (e) {
|
|
print('Error submitting receipt to FireFly III: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Generate transaction description
|
|
static String _generateTransactionDescription(List<ReceiptItem> 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(', ');
|
|
}
|
|
}
|