feat: print on receipt

This commit is contained in:
efrilm 2025-08-07 11:39:33 +07:00
parent 811ae06e58
commit 8022fa6b32
4 changed files with 336 additions and 6 deletions

View File

@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:enaklo_pos/core/extensions/string_ext.dart'; import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart'; import 'package:enaklo_pos/core/utils/printer_service.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart'; import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/order_response_model.dart'; import 'package:enaklo_pos/data/models/response/order_response_model.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart'; import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
@ -20,10 +21,6 @@ Future<void> onPrint(
final barPrinter = final barPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('bar'); await ProductLocalDatasource.instance.getPrinterByCode('bar');
log("Checker printer: ${checkerPrinter?.toMap()}");
log("Kitchen printer: ${kitchenPrinter?.toMap()}");
log("Bar printer: ${barPrinter?.toMap()}");
// Checker printer // Checker printer
if (checkerPrinter != null) { if (checkerPrinter != null) {
try { try {
@ -90,3 +87,37 @@ Future<void> onPrint(
} }
} }
} }
Future<void> onPrintRecipt(
context, {
required Order order,
required String paymentMethod,
required int nominalBayar,
required int kembalian,
required int taxPercentage,
}) async {
final receiptPrinter =
await ProductLocalDatasource.instance.getPrinterByCode('receipt');
final authData = await AuthLocalDataSource().getAuthData();
if (receiptPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance.printOrderV4(
order,
authData.user?.name ?? "",
paymentMethod,
nominalBayar,
kembalian,
taxPercentage,
receiptPrinter.paper.toIntegerFromText,
);
await PrinterService()
.printWithPrinter(receiptPrinter, printValue, context);
} catch (e) {
log("Error printing receipt order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing receipt order: $e')),
);
}
}
}

View File

@ -1,5 +1,6 @@
import 'dart:math'; import 'dart:math';
import 'package:enaklo_pos/data/models/response/order_response_model.dart';
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart'; import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart'; import 'package:enaklo_pos/core/extensions/int_ext.dart';
@ -739,6 +740,263 @@ class PrintDataoutputs {
return bytes; return bytes;
} }
Future<List<int>> printOrderV4(
Order order,
String chashierName,
String paymentMethod,
int nominalBayar,
int kembalian,
int taxPercentage,
int paper,
) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
final generator =
Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile);
bytes += generator.reset();
bytes += generator.text('Guapatlu Khas Bakmi Jambi',
styles: const PosStyles(
bold: true,
align: PosAlign.center,
height: PosTextSize.size1,
width: PosTextSize.size1,
));
bytes += generator.text('Gading Boulevard w2, No 25, Jakarta',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text('085-77777-3839',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.row([
PosColumn(
text: DateFormat('dd MMM yyyy').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: DateFormat('HH:mm').format(DateTime.now()),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Receipt Number',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: 'JF-${DateFormat('yyyyMMddhhmm').format(DateTime.now())}',
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Order ID',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: Random().nextInt(100000).toString(),
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Bill Name',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: order.metadata?['customer_name'] ?? '',
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Collected By',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: chashierName,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Pembayaran',
width: 8,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: paymentMethod,
width: 4,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.text('Dine In',
styles: const PosStyles(bold: true, align: PosAlign.center));
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
for (final product in (order.orderItems ?? <OrderItem>[])) {
bytes += generator.row([
PosColumn(
text: '${product.quantity} x ${product.productName}',
width: 8,
styles: const PosStyles(bold: true, align: PosAlign.left),
),
PosColumn(
text: (product.totalPrice ?? 0).currencyFormatRpV2,
width: 4,
styles: const PosStyles(bold: true, align: PosAlign.right),
),
]);
}
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.row([
PosColumn(
text: 'Subtotal ${order.orderItems?.length ?? "0"} Product',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: (order.subtotal ?? 0).currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Discount',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: (order.discountAmount ?? 0).currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
// Only show tax if it's greater than 0
if ((order.taxAmount ?? 0) > 0) {
bytes += generator.row([
PosColumn(
text: 'Tax PB1 ($taxPercentage%)',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: (order.taxAmount ?? 0).currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
}
// Only show service charge if it's greater than 0
// if (serviceCharge > 0) {
// bytes += generator.row([
// PosColumn(
// text: 'Service Charge($serviceChargePercentage%)',
// width: 6,
// styles: const PosStyles(align: PosAlign.left),
// ),
// PosColumn(
// text: serviceCharge.currencyFormatRpV2,
// width: 6,
// styles: const PosStyles(align: PosAlign.right),
// ),
// ]);
// }
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
bytes += generator.row([
PosColumn(
text: 'Total',
width: 6,
styles: const PosStyles(bold: true, align: PosAlign.left),
),
PosColumn(
text: '${order.totalAmount ?? ""}'.currencyFormatRpV2,
width: 6,
styles: const PosStyles(bold: true, align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Dibayar',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: nominalBayar.currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: 'Kembali',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: kembalian.currencyFormatRpV2,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.text(
paper == 80
? '------------------------------------------------'
: '--------------------------------',
styles: const PosStyles(bold: false, align: PosAlign.center));
// bytes += generator.text('Notes',
// styles: const PosStyles(bold: false, align: PosAlign.center));
// bytes += generator.text('Pass Wifi: fic14jilid2',
// styles: const PosStyles(bold: false, align: PosAlign.center));
// //terima kasih
// bytes += generator.text('Terima Kasih',
// styles: const PosStyles(bold: true, align: PosAlign.center));
paper == 80 ? bytes += generator.feed(3) : bytes += generator.feed(1);
bytes += generator.cut();
return bytes;
}
Future<List<int>> printQRIS( Future<List<int>> printQRIS(
int totalPrice, Uint8List imageQris, int paper) async { int totalPrice, Uint8List imageQris, int paper) async {
List<int> bytes = []; List<int> bytes = [];

View File

@ -961,6 +961,23 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
) => ) =>
products, products,
); );
int tax = state.maybeWhen(
orElse: () => 0,
loaded: (
products,
discountModel,
discount,
discountAmount,
tax,
serviceCharge,
totalQuantity,
totalPrice,
draftName,
orderType,
deliveryType,
) =>
tax,
);
return Container( return Container(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
@ -1018,6 +1035,11 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
.pushReplacement(SuccessOrderPage( .pushReplacement(SuccessOrderPage(
productQuantity: items, productQuantity: items,
order: data, order: data,
paymentMethod:
selectedPaymentMethod?.name ?? "",
nominalBayar: totalPriceController
.text.toIntegerFromText,
taxPercentage: tax,
)); ));
}, },
error: (message) => AppFlushbar.showError( error: (message) => AppFlushbar.showError(

View File

@ -11,8 +11,18 @@ import 'package:flutter/material.dart';
class SuccessOrderPage extends StatefulWidget { class SuccessOrderPage extends StatefulWidget {
final List<ProductQuantity> productQuantity; final List<ProductQuantity> productQuantity;
final Order order; final Order order;
const SuccessOrderPage( final String paymentMethod;
{super.key, required this.order, required this.productQuantity}); final int nominalBayar;
final int taxPercentage;
const SuccessOrderPage({
super.key,
required this.order,
required this.productQuantity,
required this.paymentMethod,
required this.nominalBayar,
required this.taxPercentage,
});
@override @override
State<SuccessOrderPage> createState() => _SuccessOrderPageState(); State<SuccessOrderPage> createState() => _SuccessOrderPageState();
@ -991,6 +1001,15 @@ class _SuccessOrderPageState extends State<SuccessOrderPage>
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
onTap: () async { onTap: () async {
onPrintRecipt(
context,
order: widget.order,
paymentMethod: widget.paymentMethod,
nominalBayar: widget.nominalBayar,
kembalian: widget.nominalBayar -
(widget.order.totalAmount ?? 0),
taxPercentage: widget.taxPercentage,
);
onPrint( onPrint(
context, context,
productQuantity: widget.productQuantity, productQuantity: widget.productQuantity,