cashumit/lib/services/firefly_api_service.dart

202 lines
6.3 KiB
Dart

// lib/services/firefly_api_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/firefly_account.dart';
class FireflyApiService {
/// Mengambil daftar akun dari Firefly III API.
///
/// [baseUrl] adalah URL dasar instance Firefly III (e.g., https://firefly.yourdomain.com).
/// [accessToken] adalah Personal Access Token pengguna.
/// [type] adalah filter opsional untuk tipe akun (e.g., 'asset', 'revenue').
static Future<List<FireflyAccount>> fetchAccounts({
required String baseUrl,
required String accessToken,
String? type,
}) async {
final uri = type != null
? Uri.parse('$baseUrl/api/v1/accounts?type=$type')
: Uri.parse('$baseUrl/api/v1/accounts');
final response = await http.get(
uri,
headers: {
'Authorization': 'Bearer $accessToken',
'Accept': 'application/json',
},
);
if (response.statusCode == 200) {
final dynamic responseBody = json.decode(response.body);
if (responseBody is Map<String, dynamic> &&
responseBody.containsKey('data')) {
final List accountsJson = responseBody['data'] as List;
final List<FireflyAccount> accounts = accountsJson
.map((accountJson) => FireflyAccount.fromJson(accountJson))
.toList();
return accounts;
} else {
throw Exception('Format respons API tidak sesuai');
}
} else {
// Handle error - misalnya, lempar exception
throw Exception('Gagal mengambil akun: ${response.statusCode}');
}
}
/// Mengirim transaksi dummy ke Firefly III API.
///
/// [baseUrl] adalah URL dasar instance Firefly III.
/// [accessToken] adalah Personal Access Token pengguna.
/// [sourceId] dan [destinationId] adalah ID akun yang valid.
/// [type] adalah tipe transaksi (default 'deposit').
static Future<String?> submitDummyTransaction({
required String baseUrl,
required String accessToken,
required String sourceId,
required String destinationId,
String type = 'deposit',
String description = 'Pengeluaran Dummy via Flutter App',
String date = '2025-08-19', // Gunakan format ISO 8601
String amount = '50.00',
}) async {
final uri = Uri.parse('$baseUrl/api/v1/transactions');
final payload = jsonEncode({
"transactions": [
{
"type": type,
"date": date,
"amount": amount,
"description": description,
"source_id": sourceId,
"destination_id": destinationId,
}
]
});
try {
final response = await http.post(
uri,
headers: {
'Authorization': 'Bearer $accessToken',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: payload,
);
if (response.statusCode == 200 || response.statusCode == 201) {
// Parse response to get transaction ID
try {
final dynamic responseBody = json.decode(response.body);
if (responseBody is Map<String, dynamic> &&
responseBody.containsKey('data')) {
final dynamic data = responseBody['data'];
// Handle case where data is a single object with an 'id' field
if (data is Map<String, dynamic> && data.containsKey('id')) {
final transactionId = data['id'].toString();
return transactionId;
}
// Handle case where data is a list with at least one object
if (data is List && data.isNotEmpty) {
final firstItem = data[0];
if (firstItem is Map<String, dynamic>) {
if (firstItem.containsKey('id')) {
final transactionId = firstItem['id'].toString();
return transactionId;
} else if (firstItem.containsKey('transaction_id')) {
final transactionId = firstItem['transaction_id'].toString();
return transactionId;
}
}
}
}
} catch (e) {
// Don't crash if parsing fails, just return success
}
// If we can't parse the ID, return a default success indicator
return "success";
} else {
return null;
}
} catch (e) {
return null;
}
}
/// Mengunggah lampiran langsung ke transaksi di Firefly III.
///
/// [baseUrl] adalah URL dasar instance Firefly III.
/// [accessToken] adalah Personal Access Token pengguna.
/// [transactionId] adalah ID transaksi yang akan dilampiri.
/// [fileBytes] adalah byte data dari file lampiran.
static Future<bool> uploadAttachment({
required String baseUrl,
required String accessToken,
required String transactionId,
required List<int> fileBytes,
}) async {
final uri = Uri.parse('$baseUrl/api/v1/attachments/$transactionId/upload');
try {
// Kirim file sebagai raw binary data dengan Content-Type: application/octet-stream
final response = await http.post(
uri,
headers: {
'Authorization': 'Bearer $accessToken',
'Accept': 'application/json',
'Content-Type': 'application/octet-stream',
},
body: fileBytes, // Kirim byte array langsung sebagai body
);
if (response.statusCode == 200 || response.statusCode == 204) {
return true;
} else {
print('Upload attachment failed with status: ${response.statusCode}');
print('Response body: ${response.body}');
return false;
}
} catch (e) {
print('Upload attachment failed with exception: $e');
return false;
}
}
/// Menguji koneksi ke Firefly III.
static Future<bool> testConnection({required String baseUrl}) async {
try {
final response = await http.get(Uri.parse(baseUrl));
return response.statusCode == 200;
} catch (e) {
return false;
}
}
/// Menguji autentikasi dengan token.
static Future<bool> testAuthentication(
{required String baseUrl, required String accessToken}) async {
try {
final response = await http.get(
Uri.parse('$baseUrl/api/v1/about/user'),
headers: {
'Authorization': 'Bearer $accessToken',
'Accept': 'application/json',
},
);
return response.statusCode == 200;
} catch (e) {
return false;
}
}
}