apskel-pos-flutter-v2/lib/presentation/components/print/receipt_component_builder.dart

271 lines
6.3 KiB
Dart
Raw Normal View History

2025-11-06 18:15:54 +07:00
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:intl/intl.dart';
/// Reusable component builder untuk thermal receipt printer
class ReceiptComponentBuilder {
final Generator generator;
final int paperSize;
ReceiptComponentBuilder({required this.generator, this.paperSize = 58});
/// Get separator line based on paper size
String get _separator => paperSize == 80
? '------------------------------------------------'
: '--------------------------------';
/// Print text centered with custom style
List<int> textCenter(
String text, {
bool bold = false,
PosTextSize height = PosTextSize.size1,
PosTextSize width = PosTextSize.size1,
}) {
return generator.text(
text,
styles: PosStyles(
bold: bold,
align: PosAlign.center,
height: height,
width: width,
),
);
}
/// Print text aligned left
List<int> textLeft(String text, {bool bold = false}) {
return generator.text(
text,
styles: PosStyles(bold: bold, align: PosAlign.left),
);
}
/// Print text aligned right
List<int> textRight(String text, {bool bold = false}) {
return generator.text(
text,
styles: PosStyles(bold: bold, align: PosAlign.right),
);
}
/// Print separator line
List<int> separator({bool bold = false}) {
return generator.text(
_separator,
styles: PosStyles(bold: bold, align: PosAlign.center),
);
}
/// Print row with 2 columns (label: value)
List<int> row2Columns(
String leftText,
String rightText, {
bool bold = false,
int leftWidth = 6,
int rightWidth = 6,
}) {
return generator.row([
PosColumn(
text: leftText,
width: leftWidth,
styles: PosStyles(align: PosAlign.left, bold: bold),
),
PosColumn(
text: rightText,
width: rightWidth,
styles: PosStyles(align: PosAlign.right, bold: bold),
),
]);
}
/// Print row with 3 columns
List<int> row3Columns(
String leftText,
String centerText,
String rightText, {
int leftWidth = 4,
int centerWidth = 4,
int rightWidth = 4,
}) {
return generator.row([
PosColumn(
text: leftText,
width: leftWidth,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: centerText,
width: centerWidth,
styles: const PosStyles(align: PosAlign.center),
),
PosColumn(
text: rightText,
width: rightWidth,
styles: const PosStyles(align: PosAlign.right),
),
]);
}
/// Print empty lines
List<int> emptyLines(int count) {
return generator.emptyLines(count);
}
/// Print feed lines
List<int> feed(int count) {
return generator.feed(count);
}
/// Print header (outlet info)
List<int> header({
required String outletName,
required String address,
required String phoneNumber,
}) {
List<int> bytes = [];
bytes += textCenter(
outletName,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1,
);
bytes += textCenter(address);
bytes += textCenter(phoneNumber);
bytes += separator();
return bytes;
}
/// Print date and time
List<int> dateTime(DateTime dateTime) {
return row2Columns(
DateFormat('dd MMM yyyy').format(dateTime),
DateFormat('HH:mm').format(dateTime),
);
}
/// Print order info section
List<int> orderInfo({
required String orderNumber,
required String customerName,
required String cashierName,
String? paymentMethod,
String? tableNumber,
}) {
List<int> bytes = [];
bytes += row2Columns('No. Pesanan', orderNumber);
bytes += row2Columns('Pelanggan', customerName);
bytes += row2Columns('Kasir', cashierName);
if (paymentMethod != null) {
bytes += row2Columns(
'Pembayaran',
paymentMethod,
leftWidth: 8,
rightWidth: 4,
);
}
if (tableNumber != null && tableNumber.isNotEmpty) {
bytes += row2Columns('Meja', tableNumber, leftWidth: 8, rightWidth: 4);
}
return bytes;
}
/// Print order type (Dine In, Take Away, etc)
List<int> orderType(String type) {
List<int> bytes = [];
bytes += separator();
bytes += textCenter(type, bold: true);
bytes += separator();
return bytes;
}
/// Print single item
List<int> orderItem({
required String productName,
required int quantity,
required String unitPrice,
required String totalPrice,
String? variantName,
String? notes,
}) {
List<int> bytes = [];
final variantText = variantName != null && variantName.isNotEmpty
? "($variantName)"
: '';
bytes += textLeft('$productName $variantText', bold: true);
bytes += row2Columns(
'$quantity x $unitPrice',
totalPrice,
leftWidth: 8,
rightWidth: 4,
);
if (notes != null && notes.isNotEmpty) {
bytes += row2Columns('Note', notes, leftWidth: 4, rightWidth: 8);
}
bytes += emptyLines(1);
return bytes;
}
/// Print summary section
List<int> summary({
required int totalItems,
required String subtotal,
required String discount,
required String total,
required String paid,
}) {
List<int> bytes = [];
bytes += separator();
bytes += row2Columns('Total Item', totalItems.toString());
bytes += row2Columns('Subtotal', subtotal);
bytes += row2Columns('Diskon', discount);
bytes += separator();
bytes += row2Columns('Total', total, bold: true);
bytes += row2Columns('Bayar', paid);
bytes += separator();
return bytes;
}
/// Print footer (thank you message)
List<int> footer({String message = 'Terima kasih'}) {
List<int> bytes = [];
bytes += emptyLines(2);
bytes += textCenter(
message,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1,
);
bytes += feed(paperSize == 80 ? 3 : 1);
bytes += generator.cut();
return bytes;
}
/// Print QR Code
List<int> qrCode(String data, {PosAlign align = PosAlign.center}) {
return generator.qrcode(data, align: align);
}
/// Print barcode
// List<int> barcode(String data, {BarcodeType type = BarcodeType.code128}) {
// return generator.barcode(
// Barcode.code128(data),
// );
// }
}