checkpoint
parent
28e99e4e2f
commit
c2e6f6b945
|
@ -41,3 +41,6 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
# Deprecated files
|
||||||
|
/deprecated/
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Instruksi untuk menambahkan logo toko ke aplikasi:
|
|
||||||
|
|
||||||
1. Tambahkan file gambar logo ke folder `assets/images/` dengan nama `store_logo.png`
|
|
||||||
|
|
||||||
2. Tambahkan path ke file `pubspec.yaml` agar file gambar tersebut tersedia di aplikasi:
|
|
||||||
```yaml
|
|
||||||
flutter:
|
|
||||||
assets:
|
|
||||||
- assets/images/
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Untuk menyimpan path logo ke shared preferences, panggil fungsi:
|
|
||||||
```dart
|
|
||||||
import 'package:cashumit/utils/store_logo_utils.dart';
|
|
||||||
|
|
||||||
// Simpan path logo
|
|
||||||
await saveStoreLogoPath('assets/images/store_logo.png');
|
|
||||||
|
|
||||||
// Hapus path logo jika diperlukan
|
|
||||||
await removeStoreLogoPath();
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Untuk menguji pencetakan logo, pastikan printer thermal mendukung pencetakan gambar.
|
|
Binary file not shown.
Before Width: | Height: | Size: 96 B After Width: | Height: | Size: 91 B |
|
@ -3,6 +3,8 @@ import 'package:cashumit/screens/transaction_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cashumit/screens/receipt_screen.dart';
|
import 'package:cashumit/screens/receipt_screen.dart';
|
||||||
import 'package:cashumit/utils/store_logo_utils.dart';
|
import 'package:cashumit/utils/store_logo_utils.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:cashumit/providers/receipt_provider.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
// Ensure WidgetsFlutterBinding is initialized for async operations
|
// Ensure WidgetsFlutterBinding is initialized for async operations
|
||||||
|
@ -11,7 +13,14 @@ void main() async {
|
||||||
// Initialize the store logo from asset
|
// Initialize the store logo from asset
|
||||||
await copyAndSaveStoreLogoFromAsset('assets/images/store_logo.png');
|
await copyAndSaveStoreLogoFromAsset('assets/images/store_logo.png');
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(
|
||||||
|
MultiProvider(
|
||||||
|
providers: [
|
||||||
|
ChangeNotifierProvider(create: (_) => ReceiptProvider()),
|
||||||
|
],
|
||||||
|
child: const MyApp(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
|
|
@ -22,6 +22,13 @@ class _StoreDisclaimerState extends State<StoreDisclaimer> {
|
||||||
_loadDisclaimerText();
|
_loadDisclaimerText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant StoreDisclaimer oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
// Memuat ulang teks ketika widget diupdate
|
||||||
|
_loadDisclaimerText();
|
||||||
|
}
|
||||||
|
|
||||||
/// Memuat teks disclaimer dari shared preferences
|
/// Memuat teks disclaimer dari shared preferences
|
||||||
Future<void> _loadDisclaimerText() async {
|
Future<void> _loadDisclaimerText() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
|
@ -14,9 +14,9 @@ class ThankYouPantun extends StatefulWidget {
|
||||||
|
|
||||||
class _ThankYouPantunState extends State<ThankYouPantun> {
|
class _ThankYouPantunState extends State<ThankYouPantun> {
|
||||||
String _thankYouText = '*** TERIMA KASIH ***';
|
String _thankYouText = '*** TERIMA KASIH ***';
|
||||||
String _pantunText = 'Belanja di toko kami, hemat dan nyaman,\n'
|
String _pantunText = 'Belanja di toko kami, hemat dan nyaman,\\n'
|
||||||
'Dengan penuh semangat, kami siap melayani,\n'
|
'Dengan penuh semangat, kami siap melayani,\\n'
|
||||||
'Harapan kami, Anda selalu puas,\n'
|
'Harapan kami, Anda selalu puas,\\n'
|
||||||
'Sampai jumpa lagi, selamat tinggal.';
|
'Sampai jumpa lagi, selamat tinggal.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -25,6 +25,13 @@ class _ThankYouPantunState extends State<ThankYouPantun> {
|
||||||
_loadThankYouText();
|
_loadThankYouText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant ThankYouPantun oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
// Memuat ulang teks ketika widget diupdate
|
||||||
|
_loadThankYouText();
|
||||||
|
}
|
||||||
|
|
||||||
/// Memuat teks terima kasih dan pantun dari shared preferences
|
/// Memuat teks terima kasih dan pantun dari shared preferences
|
||||||
Future<void> _loadThankYouText() async {
|
Future<void> _loadThankYouText() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
@ -34,9 +41,9 @@ class _ThankYouPantunState extends State<ThankYouPantun> {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_thankYouText = savedThankYou ?? '*** TERIMA KASIH ***';
|
_thankYouText = savedThankYou ?? '*** TERIMA KASIH ***';
|
||||||
_pantunText = savedPantun ?? 'Belanja di toko kami, hemat dan nyaman,\n'
|
_pantunText = savedPantun ?? 'Belanja di toko kami, hemat dan nyaman,\\n'
|
||||||
'Dengan penuh semangat, kami siap melayani,\n'
|
'Dengan penuh semangat, kami siap melayani,\\n'
|
||||||
'Harapan kami, Anda selalu puas,\n'
|
'Harapan kami, Anda selalu puas,\\n'
|
||||||
'Sampai jumpa lagi, selamat tinggal.';
|
'Sampai jumpa lagi, selamat tinggal.';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
80
pubspec.lock
80
pubspec.lock
|
@ -5,10 +5,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.7"
|
version: "3.6.1"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -25,14 +25,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.9"
|
version: "2.2.9"
|
||||||
bidi:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: bidi
|
|
||||||
sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.13"
|
|
||||||
bluetooth_print:
|
bluetooth_print:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -96,6 +88,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.6"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -165,6 +165,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_esc_pos_utils:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_esc_pos_utils
|
||||||
|
sha256: dd0dbd9738c07ea9a6b078f5db18feb2d5d970b4de0ce34427d66665f639eaec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.5"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -199,6 +207,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
gbk_codec:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gbk_codec
|
||||||
|
sha256: "3af5311fc9393115e3650ae6023862adf998051a804a08fb804f042724999f61"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -207,6 +223,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.3.0"
|
||||||
|
hex:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: hex
|
||||||
|
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.6"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -232,13 +264,13 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.4"
|
version: "3.3.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -383,6 +415,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
open_file:
|
open_file:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -515,10 +555,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: pdf
|
name: pdf
|
||||||
sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416"
|
sha256: "10659b915e65832b106f6d1d213e09b789cc1f24bf282ee911e49db35b96be4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.11.3"
|
version: "3.8.4"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -543,14 +583,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
posix:
|
provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: posix
|
name: provider
|
||||||
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
|
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.1.5+1"
|
||||||
qr:
|
qr:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -49,6 +49,7 @@ dependencies:
|
||||||
flutter_speed_dial: ^7.0.0
|
flutter_speed_dial: ^7.0.0
|
||||||
image_picker: ^1.0.0
|
image_picker: ^1.0.0
|
||||||
webview_flutter: ^4.10.0
|
webview_flutter: ^4.10.0
|
||||||
|
provider: ^6.1.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
/// Script untuk menguji integrasi dengan Firefly III API
|
|
||||||
/// Menjalankan beberapa operasi dasar:
|
|
||||||
/// 1. Menguji koneksi ke instance Firefly III
|
|
||||||
/// 2. Menguji autentikasi dengan token
|
|
||||||
/// 3. Mengambil daftar akun
|
|
||||||
/// 4. Mengirim transaksi dummy
|
|
||||||
|
|
||||||
void main() async {
|
|
||||||
// Konfigurasi - Ganti dengan nilai yang sesuai untuk instance Firefly III Anda
|
|
||||||
const String baseUrl = 'http://192.168.1.100:8080'; // Contoh URL
|
|
||||||
const String accessToken = 'your_access_token_here'; // Contoh token
|
|
||||||
const String sourceAccountId = '1'; // ID akun sumber (revenue)
|
|
||||||
const String destinationAccountId = '2'; // ID akun tujuan (asset)
|
|
||||||
|
|
||||||
print('=== MENGUJI INTEGRASI FIREFLY III ===\n');
|
|
||||||
|
|
||||||
// 1. Menguji koneksi
|
|
||||||
print('1. Menguji koneksi ke Firefly III...');
|
|
||||||
try {
|
|
||||||
final connectivityResponse = await http.get(Uri.parse('$baseUrl/api/v1/about'));
|
|
||||||
if (connectivityResponse.statusCode == 200) {
|
|
||||||
print(' ✓ Koneksi berhasil');
|
|
||||||
} else {
|
|
||||||
print(' ✗ Koneksi gagal (Status code: ${connectivityResponse.statusCode})');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(' ✗ Koneksi gagal dengan error: $e');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Menguji autentikasi
|
|
||||||
print('\n2. Menguji autentikasi dengan token...');
|
|
||||||
try {
|
|
||||||
final authResponse = await http.get(
|
|
||||||
Uri.parse('$baseUrl/api/v1/about/user'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer $accessToken',
|
|
||||||
'Accept': 'application/json',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (authResponse.statusCode == 200) {
|
|
||||||
print(' ✓ Autentikasi berhasil');
|
|
||||||
} else {
|
|
||||||
print(' ✗ Autentikasi gagal (Status code: ${authResponse.statusCode})');
|
|
||||||
print(' Response body: ${authResponse.body}');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(' ✗ Autentikasi gagal dengan error: $e');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Mengambil daftar akun
|
|
||||||
print('\n3. Mengambil daftar akun...');
|
|
||||||
try {
|
|
||||||
final accountsResponse = await http.get(
|
|
||||||
Uri.parse('$baseUrl/api/v1/accounts'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer $accessToken',
|
|
||||||
'Accept': 'application/json',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (accountsResponse.statusCode == 200) {
|
|
||||||
final accountsData = json.decode(accountsResponse.body);
|
|
||||||
if (accountsData is Map<String, dynamic> && accountsData.containsKey('data')) {
|
|
||||||
final accounts = accountsData['data'] as List;
|
|
||||||
print(' ✓ Berhasil mengambil ${accounts.length} akun');
|
|
||||||
|
|
||||||
// Tampilkan 3 akun pertama
|
|
||||||
print(' Contoh akun:');
|
|
||||||
for (int i = 0; i < accounts.length && i < 3; i++) {
|
|
||||||
final account = accounts[i];
|
|
||||||
if (account is Map<String, dynamic> && account.containsKey('id') && account.containsKey('attributes')) {
|
|
||||||
final attributes = account['attributes'];
|
|
||||||
if (attributes is Map<String, dynamic> && attributes.containsKey('name') && attributes.containsKey('type')) {
|
|
||||||
print(' - ID: ${account['id']}, Nama: ${attributes['name']}, Tipe: ${attributes['type']}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print(' ✗ Format respons akun tidak sesuai harapan');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print(' ✗ Gagal mengambil akun (Status code: ${accountsResponse.statusCode})');
|
|
||||||
print(' Response body: ${accountsResponse.body}');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(' ✗ Gagal mengambil akun dengan error: $e');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Mengirim transaksi dummy
|
|
||||||
print('\n4. Mengirim transaksi dummy...');
|
|
||||||
try {
|
|
||||||
final transactionPayload = jsonEncode({
|
|
||||||
"transactions": [
|
|
||||||
{
|
|
||||||
"type": "deposit",
|
|
||||||
"date": "2025-08-21",
|
|
||||||
"amount": "50.00",
|
|
||||||
"description": "Transaksi Dummy via Test Script",
|
|
||||||
"source_id": sourceAccountId,
|
|
||||||
"destination_id": destinationAccountId,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
final transactionResponse = await http.post(
|
|
||||||
Uri.parse('$baseUrl/api/v1/transactions'),
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer $accessToken',
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: transactionPayload,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (transactionResponse.statusCode == 200 || transactionResponse.statusCode == 201) {
|
|
||||||
print(' ✓ Transaksi berhasil dikirim');
|
|
||||||
|
|
||||||
// Coba parse transaction ID
|
|
||||||
try {
|
|
||||||
final transactionData = json.decode(transactionResponse.body);
|
|
||||||
if (transactionData is Map<String, dynamic> &&
|
|
||||||
transactionData.containsKey('data') &&
|
|
||||||
transactionData['data'] is List &&
|
|
||||||
transactionData['data'].isNotEmpty) {
|
|
||||||
|
|
||||||
final firstTransaction = transactionData['data'][0];
|
|
||||||
if (firstTransaction is Map<String, dynamic> && firstTransaction.containsKey('transaction_id')) {
|
|
||||||
print(' Transaction ID: ${firstTransaction['transaction_id']}');
|
|
||||||
} else if (firstTransaction is Map<String, dynamic> && firstTransaction.containsKey('id')) {
|
|
||||||
print(' Transaction ID: ${firstTransaction['id']}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(' Tidak dapat mem-parsing transaction ID: $e');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print(' ✗ Gagal mengirim transaksi (Status code: ${transactionResponse.statusCode})');
|
|
||||||
print(' Response body: ${transactionResponse.body}');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(' ✗ Gagal mengirim transaksi dengan error: $e');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
print('\n=== PENGUJIAN SELESAI ===');
|
|
||||||
print('Semua pengujian berhasil! Integrasi dengan Firefly III berfungsi dengan baik.');
|
|
||||||
}
|
|
Loading…
Reference in New Issue