From b3cb5e3e05d7fb6e98866c7143b9486b33e87821 Mon Sep 17 00:00:00 2001 From: a2nr Date: Sun, 7 Sep 2025 22:29:09 +0700 Subject: [PATCH] manual edit cleaning and fix AI code --- lib/services/esc_pos_print_service.dart | 134 +++++++++++------------- lib/services/firefly_api_service.dart | 35 ++++--- lib/services/print_service.dart | 12 ++- lib/widgets/receipt_item_list.dart | 7 +- lib/widgets/receipt_speed_dial.dart | 41 +++++--- 5 files changed, 115 insertions(+), 114 deletions(-) diff --git a/lib/services/esc_pos_print_service.dart b/lib/services/esc_pos_print_service.dart index b2e7f2f..1f783c2 100644 --- a/lib/services/esc_pos_print_service.dart +++ b/lib/services/esc_pos_print_service.dart @@ -13,38 +13,35 @@ class EscPosPrintService { static Future> generateEscPosBytes({ required List 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 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 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'); diff --git a/lib/services/firefly_api_service.dart b/lib/services/firefly_api_service.dart index 6b008de..efd0376 100644 --- a/lib/services/firefly_api_service.dart +++ b/lib/services/firefly_api_service.dart @@ -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 && responseBody.containsKey('data')) { + + 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'); @@ -91,18 +93,17 @@ class FireflyApiService { // Parse response to get transaction ID try { final dynamic responseBody = json.decode(response.body); - - if (responseBody is Map && + + 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]; @@ -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 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 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 testAuthentication({required String baseUrl, required String accessToken}) async { + static Future 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; } } -} \ No newline at end of file +} + diff --git a/lib/services/print_service.dart b/lib/services/print_service.dart index cd16148..8074bc8 100644 --- a/lib/services/print_service.dart +++ b/lib/services/print_service.dart @@ -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; } } -} \ No newline at end of file +} + diff --git a/lib/widgets/receipt_item_list.dart b/lib/widgets/receipt_item_list.dart index 3340685..cbba338 100644 --- a/lib/widgets/receipt_item_list.dart +++ b/lib/widgets/receipt_item_list.dart @@ -28,9 +28,10 @@ class _ReceiptItemListState extends State { 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 { textAlign: TextAlign.right, ), ), + SizedBox(width: 5), ], ), // Garis pembatas @@ -123,4 +125,5 @@ class _ReceiptItemListState extends State { ], ); } -} \ No newline at end of file +} + diff --git a/lib/widgets/receipt_speed_dial.dart b/lib/widgets/receipt_speed_dial.dart index fb2ee8b..f15b6ed 100644 --- a/lib/widgets/receipt_speed_dial.dart +++ b/lib/widgets/receipt_speed_dial.dart @@ -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 { ], ); } -} \ No newline at end of file +} +