manual edit cleaning and fix AI code

master
a2nr 2025-09-07 22:29:09 +07:00
parent b88c301d7d
commit b3cb5e3e05
5 changed files with 115 additions and 114 deletions

View File

@ -13,19 +13,16 @@ class EscPosPrintService {
static Future<List<int>> generateEscPosBytes({ static Future<List<int>> generateEscPosBytes({
required List<ReceiptItem> items, required List<ReceiptItem> items,
required DateTime transactionDate, required DateTime transactionDate,
required String cashierId,
required String transactionId,
}) async { }) async {
print('Memulai generateEscPosBytes...'); print('Memulai generateEscPosBytes...');
print('Jumlah item: ${items.length}'); print('Jumlah item: ${items.length}');
print('Tanggal transaksi: $transactionDate'); print('Tanggal transaksi: $transactionDate');
print('ID kasir: $cashierId');
print('ID transaksi: $transactionId');
// Load store info from shared preferences // Load store info from shared preferences
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final storeName = prefs.getString('store_name') ?? 'TOKO SEMBAKO MURAH'; 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 adminName = prefs.getString('admin_name') ?? 'Budi Santoso';
final adminPhone = prefs.getString('admin_phone') ?? '08123456789'; final adminPhone = prefs.getString('admin_phone') ?? '08123456789';
@ -97,18 +94,6 @@ class EscPosPrintService {
bytes += generator.feed(1); 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 // Garis pemisah
bytes += generator.text('================================', bytes += generator.text('================================',
styles: PosStyles(align: PosAlign.center)); styles: PosStyles(align: PosAlign.center));
@ -132,7 +117,8 @@ class EscPosPrintService {
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
try { try {
var item = items[i]; 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 // Untuk item dengan detail kuantitas dan harga
bytes += generator.row([ bytes += generator.row([
@ -147,7 +133,10 @@ class EscPosPrintService {
PosColumn( PosColumn(
text: '${item.quantity} x ${formatRupiah(item.price)}', text: '${item.quantity} x ${formatRupiah(item.price)}',
width: 7, 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( PosColumn(
text: formatRupiah(item.total), text: formatRupiah(item.total),
@ -200,15 +189,17 @@ class EscPosPrintService {
try { try {
customDisclaimer = prefs.getString('store_disclaimer_text') ?? customDisclaimer = prefs.getString('store_disclaimer_text') ??
'Barang yang sudah dibeli tidak dapat dikembalikan/ditukar. ' 'Barang yang sudah dibeli tidak dapat dikembalikan/ditukar. '
'Harap periksa kembali struk belanja Anda sebelum meninggalkan toko.'; 'Harap periksa kembali struk belanja Anda sebelum meninggalkan toko.';
} catch (e) { } catch (e) {
print('Error saat memuat disclaimer: $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; String customThankYou;
try { try {
customThankYou = prefs.getString('thank_you_text') ?? '*** TERIMA KASIH ***'; customThankYou =
prefs.getString('thank_you_text') ?? '*** TERIMA KASIH ***';
} catch (e) { } catch (e) {
print('Error saat memuat thank you text: $e'); print('Error saat memuat thank you text: $e');
customThankYou = '*** TERIMA KASIH ***'; customThankYou = '*** TERIMA KASIH ***';
@ -218,9 +209,9 @@ class EscPosPrintService {
try { try {
customPantun = prefs.getString('pantun_text') ?? customPantun = prefs.getString('pantun_text') ??
'Belanja di toko kami, hemat dan nyaman\n' '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.';
} catch (e) { } catch (e) {
print('Error saat memuat pantun: $e'); print('Error saat memuat pantun: $e');
customPantun = ''; customPantun = '';
@ -248,8 +239,8 @@ class EscPosPrintService {
} }
for (final line in wrappedDisclaimer) { for (final line in wrappedDisclaimer) {
bytes += generator.text(line, bytes +=
styles: PosStyles(align: PosAlign.center)); generator.text(line, styles: PosStyles(align: PosAlign.center));
} }
} catch (e) { } catch (e) {
print('Error saat memproses disclaimer: $e'); print('Error saat memproses disclaimer: $e');
@ -269,8 +260,8 @@ class EscPosPrintService {
bytes += generator.feed(1); bytes += generator.feed(1);
final pantunLines = customPantun.split('\n'); final pantunLines = customPantun.split('\n');
for (final line in pantunLines) { for (final line in pantunLines) {
bytes += generator.text(line, bytes +=
styles: PosStyles(align: PosAlign.center)); generator.text(line, styles: PosStyles(align: PosAlign.center));
} }
} catch (e) { } catch (e) {
print('Error saat menambahkan pantun: $e'); print('Error saat menambahkan pantun: $e');
@ -299,30 +290,21 @@ class EscPosPrintService {
required BuildContext context, required BuildContext context,
required dynamic bluetoothService, // Kita akan sesuaikan tipe ini nanti required dynamic bluetoothService, // Kita akan sesuaikan tipe ini nanti
}) async { }) async {
// Definisikan cashierId dan transactionId di sini karena tidak berubah
final String cashierId = 'KSR001';
final String transactionId = 'TXN202508200001';
print('=== FUNGSI printToThermalPrinter DIPANGGIL ==='); print('=== FUNGSI printToThermalPrinter DIPANGGIL ===');
print('Memulai proses pencetakan struk...'); print('Memulai proses pencetakan struk...');
print('Jumlah item: ${items.length}'); print('Jumlah item: ${items.length}');
print('Tanggal transaksi: ${transactionDate}'); print('Tanggal transaksi: ${transactionDate}');
print('ID kasir: $cashierId');
print('ID transaksi: $transactionId');
try { 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('Jumlah item: ${items.length}');
print('Tanggal transaksi: ${transactionDate}'); print('Tanggal transaksi: ${transactionDate}');
print('ID kasir: $cashierId');
print('ID transaksi: $transactionId');
// Generate struk dalam format byte array menggunakan EscPosPrintService // Generate struk dalam format byte array menggunakan EscPosPrintService
final bytes = await generateEscPosBytes( final bytes = await generateEscPosBytes(
items: items, items: items,
transactionDate: transactionDate, transactionDate: transactionDate,
cashierId: cashierId,
transactionId: transactionId,
); );
print('Byte array ESC/POS berhasil dihasilkan'); print('Byte array ESC/POS berhasil dihasilkan');
@ -330,7 +312,8 @@ class EscPosPrintService {
// Tampilkan byte array untuk debugging (dalam format hex) // Tampilkan byte array untuk debugging (dalam format hex)
print('Isi byte array (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(' ')); print(bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' '));
} else { } else {
print('Terlalu banyak byte untuk ditampilkan (${bytes.length} bytes)'); print('Terlalu banyak byte untuk ditampilkan (${bytes.length} bytes)');
@ -355,7 +338,8 @@ class EscPosPrintService {
throw SocketException('Koneksi ke printer terputus: ${e.message}'); throw SocketException('Koneksi ke printer terputus: ${e.message}');
} on PlatformException catch (e) { } on PlatformException catch (e) {
print('Platform error saat mengirim perintah cetak ke printer: $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) { } catch (printError) {
print('Error saat mengirim perintah cetak ke printer: $printError'); print('Error saat mengirim perintah cetak ke printer: $printError');
throw Exception('Gagal mengirim perintah cetak: $printError'); throw Exception('Gagal mengirim perintah cetak: $printError');

View File

@ -15,8 +15,9 @@ class FireflyApiService {
required String accessToken, required String accessToken,
String? type, String? type,
}) async { }) async {
final uri = final uri = type != null
type != null ? Uri.parse('$baseUrl/api/v1/accounts?type=$type') : Uri.parse('$baseUrl/api/v1/accounts'); ? Uri.parse('$baseUrl/api/v1/accounts?type=$type')
: Uri.parse('$baseUrl/api/v1/accounts');
final response = await http.get( final response = await http.get(
uri, uri,
@ -29,7 +30,8 @@ class FireflyApiService {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final dynamic responseBody = json.decode(response.body); 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 accountsJson = responseBody['data'] as List;
final List<FireflyAccount> accounts = accountsJson final List<FireflyAccount> accounts = accountsJson
@ -94,7 +96,6 @@ class FireflyApiService {
if (responseBody is Map<String, dynamic> && if (responseBody is Map<String, dynamic> &&
responseBody.containsKey('data')) { responseBody.containsKey('data')) {
final dynamic data = responseBody['data']; final dynamic data = responseBody['data'];
// Handle case where data is a single object with an 'id' field // Handle case where data is a single object with an 'id' field
@ -173,7 +174,7 @@ class FireflyApiService {
/// Menguji koneksi ke Firefly III. /// Menguji koneksi ke Firefly III.
static Future<bool> testConnection({required String baseUrl}) async { static Future<bool> testConnection({required String baseUrl}) async {
try { try {
final response = await http.get(Uri.parse('$baseUrl/api/v1/about')); final response = await http.get(Uri.parse(baseUrl));
return response.statusCode == 200; return response.statusCode == 200;
} catch (e) { } catch (e) {
return false; return false;
@ -181,7 +182,8 @@ class FireflyApiService {
} }
/// Menguji autentikasi dengan token. /// 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 { try {
final response = await http.get( final response = await http.get(
Uri.parse('$baseUrl/api/v1/about/user'), Uri.parse('$baseUrl/api/v1/about/user'),
@ -196,3 +198,4 @@ class FireflyApiService {
} }
} }
} }

View File

@ -37,7 +37,8 @@ class PrintService {
// Detail tanggal dan ID transaksi // Detail tanggal dan ID transaksi
list.add(LineText( list.add(LineText(
type: LineText.TYPE_TEXT, 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)); align: LineText.ALIGN_LEFT));
list.add(LineText( list.add(LineText(
@ -62,7 +63,8 @@ class PrintService {
final itemTotal = item.price * item.quantity; final itemTotal = item.price * item.quantity;
list.add(LineText( list.add(LineText(
type: LineText.TYPE_TEXT, type: LineText.TYPE_TEXT,
content: 'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(itemTotal)}', content:
'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(itemTotal)}',
align: LineText.ALIGN_RIGHT, align: LineText.ALIGN_RIGHT,
linefeed: 1)); linefeed: 1));
} }
@ -82,7 +84,8 @@ class PrintService {
list.add(LineText( list.add(LineText(
type: LineText.TYPE_TEXT, type: LineText.TYPE_TEXT,
content: 'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(transaction.total)}', content:
'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(transaction.total)}',
weight: 2, weight: 2,
align: LineText.ALIGN_RIGHT, align: LineText.ALIGN_RIGHT,
linefeed: 1)); linefeed: 1));
@ -139,3 +142,4 @@ class PrintService {
} }
} }
} }

View File

@ -28,9 +28,10 @@ class _ReceiptItemListState extends State<ReceiptItemList> {
return Column( return Column(
children: [ children: [
// Baris tabel keterangan // Baris tabel keterangan
Row( const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SizedBox(width: 5),
Expanded( Expanded(
flex: 4, flex: 4,
child: Text( child: Text(
@ -79,6 +80,7 @@ class _ReceiptItemListState extends State<ReceiptItemList> {
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
), ),
SizedBox(width: 5),
], ],
), ),
// Garis pembatas // Garis pembatas
@ -124,3 +126,4 @@ class _ReceiptItemListState extends State<ReceiptItemList> {
); );
} }
} }

View File

@ -46,15 +46,13 @@ class ReceiptSpeedDial extends StatelessWidget {
: () { : () {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text( content:
'Pilih akun sumber dan tujuan terlebih dahulu'), Text('Pilih akun sumber dan tujuan terlebih dahulu'),
duration: Duration(seconds: 2), duration: Duration(seconds: 2),
), ),
); );
}, },
backgroundColor: hasItems && backgroundColor: hasItems && hasSourceAccount && hasDestinationAccount
hasSourceAccount &&
hasDestinationAccount
? Colors.blue ? Colors.blue
: Colors.grey, : Colors.grey,
), ),
@ -72,7 +70,8 @@ class ReceiptSpeedDial extends StatelessWidget {
), ),
SpeedDialChild( SpeedDialChild(
child: bluetoothService.isPrinting child: bluetoothService.isPrinting
? const CircularProgressIndicator(color: Colors.white, strokeWidth: 2) ? const CircularProgressIndicator(
color: Colors.white, strokeWidth: 2)
: const Icon(Icons.receipt), : const Icon(Icons.receipt),
label: 'Cetak Struk', label: 'Cetak Struk',
onTap: bluetoothService.isPrinting onTap: bluetoothService.isPrinting
@ -91,43 +90,50 @@ class ReceiptSpeedDial extends StatelessWidget {
if (bluetoothService.connectedDevice != null) { if (bluetoothService.connectedDevice != null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Mencoba menyambungkan ke printer...')), content:
Text('Mencoba menyambungkan ke printer...')),
); );
try { try {
bool connectResult = await bluetoothService.connectToDevice(bluetoothService.connectedDevice!); bool connectResult =
await bluetoothService.connectToDevice(
bluetoothService.connectedDevice!);
if (!connectResult) { if (!connectResult) {
throw Exception('Gagal menyambungkan ke printer'); throw Exception('Gagal menyambungkan ke printer');
} }
// Tunggu sebentar untuk memastikan koneksi stabil // Tunggu sebentar untuk memastikan koneksi stabil
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(
const Duration(milliseconds: 500));
// Periksa koneksi lagi // Periksa koneksi lagi
final isConnectedAfterConnect = await onCheckConnection(); final isConnectedAfterConnect =
await onCheckConnection();
if (isConnectedAfterConnect) { if (isConnectedAfterConnect) {
await onPrint(); await onPrint();
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Gagal menyambungkan ke printer')), content:
Text('Gagal menyambungkan ke printer')),
); );
} }
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Gagal menyambungkan ke printer: $e')), content:
Text('Gagal menyambungkan ke printer: $e')),
); );
} }
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Hubungkan printer terlebih dahulu')), content:
Text('Hubungkan printer terlebih dahulu')),
); );
} }
} }
} catch (e) { } catch (e) {
// Tangani error secara umum // Tangani error secara umum
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(content: Text('Terjadi kesalahan: $e')),
content: Text('Terjadi kesalahan: $e')),
); );
} finally { } finally {
// Pastikan printing status selalu diakhiri // Pastikan printing status selalu diakhiri
@ -140,3 +146,4 @@ class ReceiptSpeedDial extends StatelessWidget {
); );
} }
} }