manual edit cleaning and fix AI code
							parent
							
								
									b88c301d7d
								
							
						
					
					
						commit
						b3cb5e3e05
					
				| 
						 | 
				
			
			@ -13,38 +13,35 @@ class EscPosPrintService {
 | 
			
		|||
  static Future<List<int>> generateEscPosBytes({
 | 
			
		||||
    required List<ReceiptItem> items,
 | 
			
		||||
    required DateTime transactionDate,
 | 
			
		||||
    required String cashierId,
 | 
			
		||||
    required String transactionId,
 | 
			
		||||
  }) async {
 | 
			
		||||
    print('Memulai generateEscPosBytes...');
 | 
			
		||||
    print('Jumlah item: ${items.length}');
 | 
			
		||||
    print('Tanggal transaksi: $transactionDate');
 | 
			
		||||
    print('ID kasir: $cashierId');
 | 
			
		||||
    print('ID transaksi: $transactionId');
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Load store info from shared preferences
 | 
			
		||||
    final prefs = await SharedPreferences.getInstance();
 | 
			
		||||
    final storeName = prefs.getString('store_name') ?? 'TOKO SEMBAKO MURAH';
 | 
			
		||||
    final storeAddress = prefs.getString('store_address') ?? 'Jl. Merdeka No. 123';
 | 
			
		||||
    final storeAddress =
 | 
			
		||||
        prefs.getString('store_address') ?? 'Jl. Merdeka No. 123';
 | 
			
		||||
    final adminName = prefs.getString('admin_name') ?? 'Budi Santoso';
 | 
			
		||||
    final adminPhone = prefs.getString('admin_phone') ?? '08123456789';
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    print('Nama toko: $storeName');
 | 
			
		||||
    print('Alamat toko: $storeAddress');
 | 
			
		||||
    print('Nama admin: $adminName');
 | 
			
		||||
    print('Telepon admin: $adminPhone');
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Format tanggal
 | 
			
		||||
    final dateFormatter = DateFormat('dd/MM/yyyy HH:mm');
 | 
			
		||||
    final formattedDate = dateFormatter.format(transactionDate);
 | 
			
		||||
    print('Tanggal yang diformat: $formattedDate');
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Format angka ke rupiah
 | 
			
		||||
    String formatRupiah(double amount) {
 | 
			
		||||
      final formatter = NumberFormat("#,##0", "id_ID");
 | 
			
		||||
      return "Rp ${formatter.format(amount)}";
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Load capability profile with timeout
 | 
			
		||||
    CapabilityProfile profile;
 | 
			
		||||
    try {
 | 
			
		||||
| 
						 | 
				
			
			@ -54,9 +51,9 @@ class EscPosPrintService {
 | 
			
		|||
      // Gunakan profile default jika gagal
 | 
			
		||||
      profile = await CapabilityProfile.load();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    final generator = Generator(PaperSize.mm58, profile);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Mulai dengan inisialisasi printer
 | 
			
		||||
    List<int> bytes = [];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +72,7 @@ class EscPosPrintService {
 | 
			
		|||
        print('Error loading or processing store logo: $e');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Tambahkan nama toko sebagai header
 | 
			
		||||
    bytes += generator.text(storeName,
 | 
			
		||||
        styles: PosStyles(
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +81,7 @@ class EscPosPrintService {
 | 
			
		|||
          width: PosTextSize.size1,
 | 
			
		||||
          align: PosAlign.center,
 | 
			
		||||
        ));
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      bytes += generator.text(storeAddress,
 | 
			
		||||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
| 
						 | 
				
			
			@ -94,25 +91,13 @@ class EscPosPrintService {
 | 
			
		|||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
      bytes += generator.text('$formattedDate',
 | 
			
		||||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      bytes += generator.feed(1);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Garis pemisah
 | 
			
		||||
      bytes += generator.text('================================',
 | 
			
		||||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
      
 | 
			
		||||
      // Informasi transaksi
 | 
			
		||||
      bytes += generator.text('Kasir: $cashierId',
 | 
			
		||||
          styles: PosStyles(bold: true));
 | 
			
		||||
      bytes += generator.text('ID Transaksi: $transactionId',
 | 
			
		||||
          styles: PosStyles());
 | 
			
		||||
      
 | 
			
		||||
      bytes += generator.feed(1);
 | 
			
		||||
      
 | 
			
		||||
      // Garis pemisah
 | 
			
		||||
      bytes += generator.text('================================',
 | 
			
		||||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Tabel item
 | 
			
		||||
      bytes += generator.row([
 | 
			
		||||
        PosColumn(
 | 
			
		||||
| 
						 | 
				
			
			@ -126,14 +111,15 @@ class EscPosPrintService {
 | 
			
		|||
          styles: PosStyles(bold: true, align: PosAlign.right),
 | 
			
		||||
        ),
 | 
			
		||||
      ]);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Item list dengan penanganan error
 | 
			
		||||
      print('Memulai iterasi item...');
 | 
			
		||||
      for (int i = 0; i < items.length; i++) {
 | 
			
		||||
        try {
 | 
			
		||||
          var item = items[i];
 | 
			
		||||
          print('Item $i: ${item.description}, qty: ${item.quantity}, price: ${item.price}, total: ${item.total}');
 | 
			
		||||
          
 | 
			
		||||
          print(
 | 
			
		||||
              'Item $i: ${item.description}, qty: ${item.quantity}, price: ${item.price}, total: ${item.total}');
 | 
			
		||||
 | 
			
		||||
          // Untuk item dengan detail kuantitas dan harga
 | 
			
		||||
          bytes += generator.row([
 | 
			
		||||
            PosColumn(
 | 
			
		||||
| 
						 | 
				
			
			@ -142,12 +128,15 @@ class EscPosPrintService {
 | 
			
		|||
              styles: PosStyles(align: PosAlign.left),
 | 
			
		||||
            ),
 | 
			
		||||
          ]);
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          bytes += generator.row([
 | 
			
		||||
            PosColumn(
 | 
			
		||||
              text: '${item.quantity} x ${formatRupiah(item.price)}',
 | 
			
		||||
              width: 7,
 | 
			
		||||
              styles: PosStyles(align: PosAlign.left, height: PosTextSize.size1, width: PosTextSize.size1),
 | 
			
		||||
              styles: PosStyles(
 | 
			
		||||
                  align: PosAlign.left,
 | 
			
		||||
                  height: PosTextSize.size1,
 | 
			
		||||
                  width: PosTextSize.size1),
 | 
			
		||||
            ),
 | 
			
		||||
            PosColumn(
 | 
			
		||||
              text: formatRupiah(item.total),
 | 
			
		||||
| 
						 | 
				
			
			@ -162,11 +151,11 @@ class EscPosPrintService {
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
      print('Selesai iterasi item');
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Garis pemisah sebelum total
 | 
			
		||||
      bytes += generator.text('--------------------------------',
 | 
			
		||||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Total
 | 
			
		||||
      double totalAmount = 0.0;
 | 
			
		||||
      try {
 | 
			
		||||
| 
						 | 
				
			
			@ -175,9 +164,9 @@ class EscPosPrintService {
 | 
			
		|||
        print('Error saat menghitung total: $e');
 | 
			
		||||
        totalAmount = 0.0;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      print('Total amount: $totalAmount');
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      bytes += generator.row([
 | 
			
		||||
        PosColumn(
 | 
			
		||||
          text: 'TOTAL',
 | 
			
		||||
| 
						 | 
				
			
			@ -190,7 +179,7 @@ class EscPosPrintService {
 | 
			
		|||
          styles: PosStyles(bold: true, align: PosAlign.right),
 | 
			
		||||
        ),
 | 
			
		||||
      ]);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Garis pemisah setelah total
 | 
			
		||||
      bytes += generator.text('================================',
 | 
			
		||||
          styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
| 
						 | 
				
			
			@ -198,29 +187,31 @@ class EscPosPrintService {
 | 
			
		|||
      // Memuat teks kustom dari shared preferences
 | 
			
		||||
      String customDisclaimer;
 | 
			
		||||
      try {
 | 
			
		||||
        customDisclaimer = prefs.getString('store_disclaimer_text') ?? 
 | 
			
		||||
            'Barang yang sudah dibeli tidak dapat dikembalikan/ditukar. ' 
 | 
			
		||||
            'Harap periksa kembali struk belanja Anda sebelum meninggalkan toko.';
 | 
			
		||||
        customDisclaimer = prefs.getString('store_disclaimer_text') ??
 | 
			
		||||
            'Barang yang sudah dibeli tidak dapat dikembalikan/ditukar. '
 | 
			
		||||
                'Harap periksa kembali struk belanja Anda sebelum meninggalkan toko.';
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        print('Error saat memuat disclaimer: $e');
 | 
			
		||||
        customDisclaimer = 'Barang yang sudah dibeli tidak dapat dikembalikan/ditukar.';
 | 
			
		||||
        customDisclaimer =
 | 
			
		||||
            'Barang yang sudah dibeli tidak dapat dikembalikan/ditukar.';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      String customThankYou;
 | 
			
		||||
      try {
 | 
			
		||||
        customThankYou = prefs.getString('thank_you_text') ?? '*** TERIMA KASIH ***';
 | 
			
		||||
        customThankYou =
 | 
			
		||||
            prefs.getString('thank_you_text') ?? '*** TERIMA KASIH ***';
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        print('Error saat memuat thank you text: $e');
 | 
			
		||||
        customThankYou = '*** TERIMA KASIH ***';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      String customPantun;
 | 
			
		||||
      try {
 | 
			
		||||
        customPantun = prefs.getString('pantun_text') ?? 
 | 
			
		||||
        customPantun = prefs.getString('pantun_text') ??
 | 
			
		||||
            'Belanja di toko kami, hemat dan nyaman\n'
 | 
			
		||||
            'Dengan penuh semangat, kami siap melayani\n'
 | 
			
		||||
            'Harapan kami, Anda selalu puas\n'
 | 
			
		||||
            'Sampai jumpa lagi, selamat tinggal.';
 | 
			
		||||
                'Dengan penuh semangat, kami siap melayani\n'
 | 
			
		||||
                'Harapan kami, Anda selalu puas\n'
 | 
			
		||||
                'Sampai jumpa lagi, selamat tinggal.';
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        print('Error saat memuat pantun: $e');
 | 
			
		||||
        customPantun = '';
 | 
			
		||||
| 
						 | 
				
			
			@ -228,7 +219,7 @@ class EscPosPrintService {
 | 
			
		|||
 | 
			
		||||
      // Menambahkan disclaimer
 | 
			
		||||
      bytes += generator.feed(1);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Memecah disclaimer menjadi beberapa baris jika terlalu panjang
 | 
			
		||||
      try {
 | 
			
		||||
        final disclaimerLines = customDisclaimer.split(' ');
 | 
			
		||||
| 
						 | 
				
			
			@ -248,8 +239,8 @@ class EscPosPrintService {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        for (final line in wrappedDisclaimer) {
 | 
			
		||||
          bytes += generator.text(line,
 | 
			
		||||
              styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
          bytes +=
 | 
			
		||||
              generator.text(line, styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        print('Error saat memproses disclaimer: $e');
 | 
			
		||||
| 
						 | 
				
			
			@ -269,8 +260,8 @@ class EscPosPrintService {
 | 
			
		|||
          bytes += generator.feed(1);
 | 
			
		||||
          final pantunLines = customPantun.split('\n');
 | 
			
		||||
          for (final line in pantunLines) {
 | 
			
		||||
            bytes += generator.text(line,
 | 
			
		||||
                styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
            bytes +=
 | 
			
		||||
                generator.text(line, styles: PosStyles(align: PosAlign.center));
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          print('Error saat menambahkan pantun: $e');
 | 
			
		||||
| 
						 | 
				
			
			@ -286,9 +277,9 @@ class EscPosPrintService {
 | 
			
		|||
      bytes += generator.feed(2);
 | 
			
		||||
      bytes += generator.cut();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    print('Jumlah byte yang dihasilkan: ${bytes.length}');
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    return bytes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -299,38 +290,30 @@ class EscPosPrintService {
 | 
			
		|||
    required BuildContext context,
 | 
			
		||||
    required dynamic bluetoothService, // Kita akan sesuaikan tipe ini nanti
 | 
			
		||||
  }) async {
 | 
			
		||||
    // Definisikan cashierId dan transactionId di sini karena tidak berubah
 | 
			
		||||
    final String cashierId = 'KSR001';
 | 
			
		||||
    final String transactionId = 'TXN202508200001';
 | 
			
		||||
    
 | 
			
		||||
    print('=== FUNGSI printToThermalPrinter DIPANGGIL ===');
 | 
			
		||||
    print('Memulai proses pencetakan struk...');
 | 
			
		||||
    print('Jumlah item: ${items.length}');
 | 
			
		||||
    print('Tanggal transaksi: ${transactionDate}');
 | 
			
		||||
    print('ID kasir: $cashierId');
 | 
			
		||||
    print('ID transaksi: $transactionId');
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      print('Menghasilkan byte array ESC/POS menggunakan flutter_esc_pos_utils...');
 | 
			
		||||
      print(
 | 
			
		||||
          'Menghasilkan byte array ESC/POS menggunakan flutter_esc_pos_utils...');
 | 
			
		||||
      print('Jumlah item: ${items.length}');
 | 
			
		||||
      print('Tanggal transaksi: ${transactionDate}');
 | 
			
		||||
      print('ID kasir: $cashierId');
 | 
			
		||||
      print('ID transaksi: $transactionId');
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Generate struk dalam format byte array menggunakan EscPosPrintService
 | 
			
		||||
      final bytes = await generateEscPosBytes(
 | 
			
		||||
        items: items,
 | 
			
		||||
        transactionDate: transactionDate,
 | 
			
		||||
        cashierId: cashierId,
 | 
			
		||||
        transactionId: transactionId,
 | 
			
		||||
      );
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      print('Byte array ESC/POS berhasil dihasilkan');
 | 
			
		||||
      print('Jumlah byte: ${bytes.length}');
 | 
			
		||||
 | 
			
		||||
      // Tampilkan byte array untuk debugging (dalam format hex)
 | 
			
		||||
      print('Isi byte array (hex):');
 | 
			
		||||
      if (bytes.length <= 1000) { // Batasi tampilan untuk mencegah output terlalu panjang
 | 
			
		||||
      if (bytes.length <= 1000) {
 | 
			
		||||
        // Batasi tampilan untuk mencegah output terlalu panjang
 | 
			
		||||
        print(bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' '));
 | 
			
		||||
      } else {
 | 
			
		||||
        print('Terlalu banyak byte untuk ditampilkan (${bytes.length} bytes)');
 | 
			
		||||
| 
						 | 
				
			
			@ -342,9 +325,9 @@ class EscPosPrintService {
 | 
			
		|||
        print('Printer tidak terhubung saat akan mencetak');
 | 
			
		||||
        throw SocketException('Printer tidak terhubung saat akan mencetak');
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      print('Mengirim byte array ke printer...');
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        // Konversi List<int> ke Uint8List
 | 
			
		||||
        final Uint8List data = Uint8List.fromList(bytes);
 | 
			
		||||
| 
						 | 
				
			
			@ -355,7 +338,8 @@ class EscPosPrintService {
 | 
			
		|||
        throw SocketException('Koneksi ke printer terputus: ${e.message}');
 | 
			
		||||
      } on PlatformException catch (e) {
 | 
			
		||||
        print('Platform error saat mengirim perintah cetak ke printer: $e');
 | 
			
		||||
        throw PlatformException(code: e.code, message: 'Error printer: ${e.message}');
 | 
			
		||||
        throw PlatformException(
 | 
			
		||||
            code: e.code, message: 'Error printer: ${e.message}');
 | 
			
		||||
      } catch (printError) {
 | 
			
		||||
        print('Error saat mengirim perintah cetak ke printer: $printError');
 | 
			
		||||
        throw Exception('Gagal mengirim perintah cetak: $printError');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,8 +15,9 @@ class FireflyApiService {
 | 
			
		|||
    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 uri = type != null
 | 
			
		||||
        ? Uri.parse('$baseUrl/api/v1/accounts?type=$type')
 | 
			
		||||
        : Uri.parse('$baseUrl/api/v1/accounts');
 | 
			
		||||
 | 
			
		||||
    final response = await http.get(
 | 
			
		||||
      uri,
 | 
			
		||||
| 
						 | 
				
			
			@ -28,14 +29,15 @@ class FireflyApiService {
 | 
			
		|||
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final dynamic responseBody = json.decode(response.body);
 | 
			
		||||
      
 | 
			
		||||
      if (responseBody is Map<String, dynamic> && responseBody.containsKey('data')) {
 | 
			
		||||
 | 
			
		||||
      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');
 | 
			
		||||
| 
						 | 
				
			
			@ -91,18 +93,17 @@ class FireflyApiService {
 | 
			
		|||
        // Parse response to get transaction ID
 | 
			
		||||
        try {
 | 
			
		||||
          final dynamic responseBody = json.decode(response.body);
 | 
			
		||||
          
 | 
			
		||||
          if (responseBody is Map<String, dynamic> && 
 | 
			
		||||
 | 
			
		||||
          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];
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +121,7 @@ class FireflyApiService {
 | 
			
		|||
        } 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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +145,7 @@ class FireflyApiService {
 | 
			
		|||
    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(
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +174,7 @@ class FireflyApiService {
 | 
			
		|||
  /// Menguji koneksi ke Firefly III.
 | 
			
		||||
  static Future<bool> testConnection({required String baseUrl}) async {
 | 
			
		||||
    try {
 | 
			
		||||
      final response = await http.get(Uri.parse('$baseUrl/api/v1/about'));
 | 
			
		||||
      final response = await http.get(Uri.parse(baseUrl));
 | 
			
		||||
      return response.statusCode == 200;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +182,8 @@ class FireflyApiService {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  /// Menguji autentikasi dengan token.
 | 
			
		||||
  static Future<bool> testAuthentication({required String baseUrl, required String accessToken}) async {
 | 
			
		||||
  static Future<bool> testAuthentication(
 | 
			
		||||
      {required String baseUrl, required String accessToken}) async {
 | 
			
		||||
    try {
 | 
			
		||||
      final response = await http.get(
 | 
			
		||||
        Uri.parse('$baseUrl/api/v1/about/user'),
 | 
			
		||||
| 
						 | 
				
			
			@ -195,4 +197,5 @@ class FireflyApiService {
 | 
			
		|||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,8 @@ class PrintService {
 | 
			
		|||
      // Detail tanggal dan ID transaksi
 | 
			
		||||
      list.add(LineText(
 | 
			
		||||
          type: LineText.TYPE_TEXT,
 | 
			
		||||
          content: 'Tanggal: ${DateFormat('dd/MM/yyyy HH:mm').format(transaction.timestamp)}',
 | 
			
		||||
          content:
 | 
			
		||||
              'Tanggal: ${DateFormat('dd/MM/yyyy HH:mm').format(transaction.timestamp)}',
 | 
			
		||||
          align: LineText.ALIGN_LEFT));
 | 
			
		||||
 | 
			
		||||
      list.add(LineText(
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +63,8 @@ class PrintService {
 | 
			
		|||
        final itemTotal = item.price * item.quantity;
 | 
			
		||||
        list.add(LineText(
 | 
			
		||||
            type: LineText.TYPE_TEXT,
 | 
			
		||||
            content: 'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(itemTotal)}',
 | 
			
		||||
            content:
 | 
			
		||||
                'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(itemTotal)}',
 | 
			
		||||
            align: LineText.ALIGN_RIGHT,
 | 
			
		||||
            linefeed: 1));
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +84,8 @@ class PrintService {
 | 
			
		|||
 | 
			
		||||
      list.add(LineText(
 | 
			
		||||
          type: LineText.TYPE_TEXT,
 | 
			
		||||
          content: 'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(transaction.total)}',
 | 
			
		||||
          content:
 | 
			
		||||
              'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(transaction.total)}',
 | 
			
		||||
          weight: 2,
 | 
			
		||||
          align: LineText.ALIGN_RIGHT,
 | 
			
		||||
          linefeed: 1));
 | 
			
		||||
| 
						 | 
				
			
			@ -138,4 +141,5 @@ class PrintService {
 | 
			
		|||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,9 +28,10 @@ class _ReceiptItemListState extends State<ReceiptItemList> {
 | 
			
		|||
    return Column(
 | 
			
		||||
      children: [
 | 
			
		||||
        // Baris tabel keterangan
 | 
			
		||||
        Row(
 | 
			
		||||
        const Row(
 | 
			
		||||
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
          children: [
 | 
			
		||||
            SizedBox(width: 5),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 4,
 | 
			
		||||
              child: Text(
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +80,7 @@ class _ReceiptItemListState extends State<ReceiptItemList> {
 | 
			
		|||
                textAlign: TextAlign.right,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            SizedBox(width: 5),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        // Garis pembatas
 | 
			
		||||
| 
						 | 
				
			
			@ -123,4 +125,5 @@ class _ReceiptItemListState extends State<ReceiptItemList> {
 | 
			
		|||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,15 +46,13 @@ class ReceiptSpeedDial extends StatelessWidget {
 | 
			
		|||
              : () {
 | 
			
		||||
                  ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                    const SnackBar(
 | 
			
		||||
                      content: Text(
 | 
			
		||||
                          'Pilih akun sumber dan tujuan terlebih dahulu'),
 | 
			
		||||
                      content:
 | 
			
		||||
                          Text('Pilih akun sumber dan tujuan terlebih dahulu'),
 | 
			
		||||
                      duration: Duration(seconds: 2),
 | 
			
		||||
                    ),
 | 
			
		||||
                  );
 | 
			
		||||
                },
 | 
			
		||||
          backgroundColor: hasItems &&
 | 
			
		||||
                  hasSourceAccount &&
 | 
			
		||||
                  hasDestinationAccount
 | 
			
		||||
          backgroundColor: hasItems && hasSourceAccount && hasDestinationAccount
 | 
			
		||||
              ? Colors.blue
 | 
			
		||||
              : Colors.grey,
 | 
			
		||||
        ),
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +70,8 @@ class ReceiptSpeedDial extends StatelessWidget {
 | 
			
		|||
        ),
 | 
			
		||||
        SpeedDialChild(
 | 
			
		||||
          child: bluetoothService.isPrinting
 | 
			
		||||
              ? const CircularProgressIndicator(color: Colors.white, strokeWidth: 2)
 | 
			
		||||
              ? const CircularProgressIndicator(
 | 
			
		||||
                  color: Colors.white, strokeWidth: 2)
 | 
			
		||||
              : const Icon(Icons.receipt),
 | 
			
		||||
          label: 'Cetak Struk',
 | 
			
		||||
          onTap: bluetoothService.isPrinting
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +79,7 @@ class ReceiptSpeedDial extends StatelessWidget {
 | 
			
		|||
              : () async {
 | 
			
		||||
                  // Panggil callback untuk memulai printing status
 | 
			
		||||
                  onPrintingStart();
 | 
			
		||||
                  
 | 
			
		||||
 | 
			
		||||
                  try {
 | 
			
		||||
                    // Periksa koneksi secara real-time
 | 
			
		||||
                    final isConnected = await onCheckConnection();
 | 
			
		||||
| 
						 | 
				
			
			@ -91,43 +90,50 @@ class ReceiptSpeedDial extends StatelessWidget {
 | 
			
		|||
                      if (bluetoothService.connectedDevice != null) {
 | 
			
		||||
                        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                          const SnackBar(
 | 
			
		||||
                            content: Text('Mencoba menyambungkan ke printer...')),
 | 
			
		||||
                              content:
 | 
			
		||||
                                  Text('Mencoba menyambungkan ke printer...')),
 | 
			
		||||
                        );
 | 
			
		||||
                        try {
 | 
			
		||||
                          bool connectResult = await bluetoothService.connectToDevice(bluetoothService.connectedDevice!);
 | 
			
		||||
                          bool connectResult =
 | 
			
		||||
                              await bluetoothService.connectToDevice(
 | 
			
		||||
                                  bluetoothService.connectedDevice!);
 | 
			
		||||
                          if (!connectResult) {
 | 
			
		||||
                            throw Exception('Gagal menyambungkan ke printer');
 | 
			
		||||
                          }
 | 
			
		||||
                          // Tunggu sebentar untuk memastikan koneksi stabil
 | 
			
		||||
                          await Future.delayed(const Duration(milliseconds: 500));
 | 
			
		||||
                          await Future.delayed(
 | 
			
		||||
                              const Duration(milliseconds: 500));
 | 
			
		||||
                          // Periksa koneksi lagi
 | 
			
		||||
                          final isConnectedAfterConnect = await onCheckConnection();
 | 
			
		||||
                          final isConnectedAfterConnect =
 | 
			
		||||
                              await onCheckConnection();
 | 
			
		||||
                          if (isConnectedAfterConnect) {
 | 
			
		||||
                            await onPrint();
 | 
			
		||||
                          } else {
 | 
			
		||||
                            ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                              const SnackBar(
 | 
			
		||||
                                content: Text('Gagal menyambungkan ke printer')),
 | 
			
		||||
                                  content:
 | 
			
		||||
                                      Text('Gagal menyambungkan ke printer')),
 | 
			
		||||
                            );
 | 
			
		||||
                          }
 | 
			
		||||
                        } catch (e) {
 | 
			
		||||
                          ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                            SnackBar(
 | 
			
		||||
                              content: Text('Gagal menyambungkan ke printer: $e')),
 | 
			
		||||
                                content:
 | 
			
		||||
                                    Text('Gagal menyambungkan ke printer: $e')),
 | 
			
		||||
                          );
 | 
			
		||||
                        }
 | 
			
		||||
                      } else {
 | 
			
		||||
                        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                          const SnackBar(
 | 
			
		||||
                            content: Text('Hubungkan printer terlebih dahulu')),
 | 
			
		||||
                              content:
 | 
			
		||||
                                  Text('Hubungkan printer terlebih dahulu')),
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                  } catch (e) {
 | 
			
		||||
                    // Tangani error secara umum
 | 
			
		||||
                    ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                      SnackBar(
 | 
			
		||||
                        content: Text('Terjadi kesalahan: $e')),
 | 
			
		||||
                      SnackBar(content: Text('Terjadi kesalahan: $e')),
 | 
			
		||||
                    );
 | 
			
		||||
                  } finally {
 | 
			
		||||
                    // Pastikan printing status selalu diakhiri
 | 
			
		||||
| 
						 | 
				
			
			@ -139,4 +145,5 @@ class ReceiptSpeedDial extends StatelessWidget {
 | 
			
		|||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue