// 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> 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 && responseBody.containsKey('data')) { final List accountsJson = responseBody['data'] as List; final List 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 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 && responseBody.containsKey('data')) { final dynamic data = responseBody['data']; // Handle case where data is a single object with an 'id' field if (data is Map && 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) { 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 uploadAttachment({ required String baseUrl, required String accessToken, required String transactionId, required List 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 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 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; } } }