first commit
This commit is contained in:
116
lib/thermal priting invoices/barcode_widget.dart
Normal file
116
lib/thermal priting invoices/barcode_widget.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'package:barcode/barcode.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class StickerData {
|
||||
final String businessName;
|
||||
final String name;
|
||||
final num price;
|
||||
final String code;
|
||||
final String mfg;
|
||||
final bool isTwoIch;
|
||||
final bool showBusinessName;
|
||||
final bool showName;
|
||||
final bool showPrice;
|
||||
final bool showCode;
|
||||
final bool showMfg;
|
||||
final double nameFontSize;
|
||||
final double priceFontSize;
|
||||
final double mfgFontSize;
|
||||
final double codeFontSize;
|
||||
|
||||
StickerData({
|
||||
required this.businessName,
|
||||
required this.name,
|
||||
required this.price,
|
||||
required this.code,
|
||||
required this.mfg,
|
||||
required this.isTwoIch,
|
||||
required this.showBusinessName,
|
||||
required this.showName,
|
||||
required this.showPrice,
|
||||
required this.showCode,
|
||||
required this.showMfg,
|
||||
required this.nameFontSize,
|
||||
required this.priceFontSize,
|
||||
required this.mfgFontSize,
|
||||
required this.codeFontSize,
|
||||
});
|
||||
}
|
||||
|
||||
class StickerWidget extends StatelessWidget {
|
||||
final StickerData data;
|
||||
|
||||
const StickerWidget({super.key, required this.data});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final barcode = Barcode.code128();
|
||||
final svg = barcode.toSvg(data.code, width: data.isTwoIch ? 300 : 200, height: 40, drawText: false);
|
||||
|
||||
String formatDateString(String? dateString) {
|
||||
if (dateString == null) return 'N/A';
|
||||
try {
|
||||
final parsed = DateTime.parse(dateString);
|
||||
return DateFormat('yyyy-MM-dd').format(parsed);
|
||||
} catch (e) {
|
||||
return 'N/A';
|
||||
}
|
||||
}
|
||||
|
||||
return Container(
|
||||
width: data.isTwoIch ? 350 : 280,
|
||||
height: 180,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (data.showBusinessName)
|
||||
Text(
|
||||
data.businessName,
|
||||
style: TextStyle(fontSize: data.nameFontSize, color: Colors.black),
|
||||
),
|
||||
if (data.showName)
|
||||
Text(
|
||||
data.name,
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: data.nameFontSize, color: Colors.black),
|
||||
),
|
||||
if (data.showPrice) const SizedBox(height: 2),
|
||||
if (data.showPrice)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Price: ', style: TextStyle(fontSize: data.priceFontSize, color: Colors.black)),
|
||||
Text(
|
||||
NumberFormat.currency(symbol: '€').format(data.price),
|
||||
style: TextStyle(fontSize: data.priceFontSize, fontWeight: FontWeight.bold, color: Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
if (data.showMfg)
|
||||
Text(
|
||||
'Packing Date: ${formatDateString(data.mfg)}',
|
||||
style: TextStyle(fontSize: data.mfgFontSize, color: Colors.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: SvgPicture.string(
|
||||
svg,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
if (data.showCode) Text(data.code, style: TextStyle(fontSize: data.codeFontSize, color: Colors.black)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
29
lib/thermal priting invoices/label_print_test.dart
Normal file
29
lib/thermal priting invoices/label_print_test.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_print_plus/bluetooth_print_plus.dart';
|
||||
|
||||
Future<void> printLabelTest({
|
||||
required String productName,
|
||||
required String price,
|
||||
required String date,
|
||||
required String barcodeData,
|
||||
required Uint8List pngBytes,
|
||||
required bool isTwoInch,
|
||||
}) async {
|
||||
TscCommand tscCommand = TscCommand();
|
||||
await tscCommand.cleanCommand();
|
||||
await tscCommand.size(width: isTwoInch ? 45 : 38, height: 25); // mm
|
||||
await tscCommand.gap(2);
|
||||
await tscCommand.cls();
|
||||
await tscCommand.image(image: pngBytes, x: 0, y: 0);
|
||||
await tscCommand.print(1);
|
||||
final cmd = await tscCommand.getCommand();
|
||||
BluetoothPrintPlus.write(cmd);
|
||||
}
|
||||
|
||||
String centerText(String text, {int lineWidth = 24}) {
|
||||
if (text.length >= lineWidth) return text;
|
||||
int totalPadding = lineWidth - text.length;
|
||||
int leftPadding = totalPadding ~/ 2; // only add left padding
|
||||
return ' ' * leftPadding + text;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:mobile_pos/model/sale_transaction_model.dart';
|
||||
|
||||
import '../../Screens/Due Calculation/Model/due_collection_model.dart';
|
||||
import '../../Screens/Purchase/Model/purchase_transaction_model.dart';
|
||||
import '../../model/business_info_model.dart';
|
||||
|
||||
class PrintSalesTransactionModel {
|
||||
PrintSalesTransactionModel({required this.transitionModel, required this.personalInformationModel});
|
||||
|
||||
BusinessInformationModel personalInformationModel;
|
||||
SalesTransactionModel? transitionModel;
|
||||
}
|
||||
|
||||
class PrintPurchaseTransactionModel {
|
||||
PrintPurchaseTransactionModel({required this.purchaseTransitionModel, required this.personalInformationModel});
|
||||
|
||||
BusinessInformationModel personalInformationModel;
|
||||
PurchaseTransaction? purchaseTransitionModel;
|
||||
}
|
||||
|
||||
class PrintDueTransactionModel {
|
||||
PrintDueTransactionModel({required this.dueTransactionModel, required this.personalInformationModel});
|
||||
|
||||
DueCollection? dueTransactionModel;
|
||||
BusinessInformationModel personalInformationModel;
|
||||
}
|
||||
28
lib/thermal priting invoices/network_image.dart
Normal file
28
lib/thermal priting invoices/network_image.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:image/image.dart' as img;
|
||||
|
||||
Future<img.Image?> getNetworkImage(
|
||||
String? url, {
|
||||
int width = 250,
|
||||
int height = 250,
|
||||
}) async {
|
||||
if (url == null) return null;
|
||||
|
||||
try {
|
||||
final response = await http.get(Uri.parse(url));
|
||||
if (response.statusCode != 200) return null;
|
||||
|
||||
final _image = img.decodeImage(response.bodyBytes);
|
||||
if (_image == null) return null;
|
||||
return _image;
|
||||
return img.copyResize(
|
||||
_image,
|
||||
width: width,
|
||||
height: height,
|
||||
interpolation: img.Interpolation.average,
|
||||
);
|
||||
// final img.Image grayscaleImage = img.grayscale(resizedImage);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
241
lib/thermal priting invoices/provider/custom_print_provider.dart
Normal file
241
lib/thermal priting invoices/provider/custom_print_provider.dart
Normal file
@@ -0,0 +1,241 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||
|
||||
import '../../constant.dart';
|
||||
import '../model/print_transaction_model.dart';
|
||||
|
||||
final printerPurchaseProviderNotifier = ChangeNotifierProvider((ref) => PrinterPurchase());
|
||||
|
||||
class PrinterPurchase extends ChangeNotifier {
|
||||
List<BluetoothInfo> availableBluetoothDevices = [];
|
||||
|
||||
Future<void> getBluetooth() async {
|
||||
final List<BluetoothInfo> bluetooths = await PrintBluetoothThermal.pairedBluetooths;
|
||||
availableBluetoothDevices = bluetooths;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<bool> setConnect(String mac) async {
|
||||
bool status = false;
|
||||
final bool result = await PrintBluetoothThermal.connect(macPrinterAddress: mac);
|
||||
if (result == true) {
|
||||
connected = true;
|
||||
status = true;
|
||||
}
|
||||
notifyListeners();
|
||||
return status;
|
||||
}
|
||||
|
||||
Future<bool> printCustomTicket(
|
||||
{required PrintPurchaseTransactionModel printTransactionModel,
|
||||
required String data,
|
||||
required String paperSize}) async {
|
||||
bool isPrinted = false;
|
||||
bool? isConnected = await PrintBluetoothThermal.connectionStatus;
|
||||
if (isConnected == true) {
|
||||
List<int> bytes = await customPrintTicket(
|
||||
printTransactionModel: printTransactionModel,
|
||||
data: data,
|
||||
paperSize: paperSize,
|
||||
);
|
||||
await PrintBluetoothThermal.writeBytes(bytes);
|
||||
isPrinted = true;
|
||||
} else {
|
||||
isPrinted = false;
|
||||
}
|
||||
notifyListeners();
|
||||
return isPrinted;
|
||||
}
|
||||
|
||||
Future<List<int>> customPrintTicket(
|
||||
{required PrintPurchaseTransactionModel printTransactionModel,
|
||||
required String data,
|
||||
required String paperSize}) async {
|
||||
List<int> bytes = [];
|
||||
PaperSize? size;
|
||||
if (paperSize == '2 inch 58mm') {
|
||||
size = PaperSize.mm58;
|
||||
} else {
|
||||
size = PaperSize.mm80;
|
||||
}
|
||||
|
||||
try {
|
||||
CapabilityProfile profile = await CapabilityProfile.load();
|
||||
final generator = Generator(PaperSize.mm58, profile);
|
||||
|
||||
Future<void> addText(String text, {PosStyles? styles, int linesAfter = 0}) async {
|
||||
if (_isAscii(text)) {
|
||||
bytes += generator.text(
|
||||
text,
|
||||
linesAfter: linesAfter,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final imageBytes = await _textToImageBytes(
|
||||
generator,
|
||||
text,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
),
|
||||
);
|
||||
bytes += imageBytes;
|
||||
if (linesAfter > 0) {
|
||||
bytes += generator.feed(linesAfter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add company name
|
||||
final companyNameText = printTransactionModel.personalInformationModel.data?.companyName ?? '';
|
||||
bytes += generator.text(
|
||||
companyNameText,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1,
|
||||
);
|
||||
|
||||
// Add address
|
||||
final address = printTransactionModel.personalInformationModel.data?.address ?? '';
|
||||
if (address.isNotEmpty) {
|
||||
bytes += generator.text(
|
||||
address,
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
);
|
||||
}
|
||||
|
||||
// Add phone number
|
||||
final phoneNumber = printTransactionModel.personalInformationModel.data?.phoneNumber ?? '';
|
||||
if (phoneNumber.isNotEmpty) {
|
||||
bytes += generator.text(
|
||||
'Tel: $phoneNumber',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: printTransactionModel.personalInformationModel.data?.vatNo?.trim().isNotEmpty == true ? 0 : 1,
|
||||
);
|
||||
}
|
||||
|
||||
// Add VAT information if available
|
||||
final vatNumber = printTransactionModel.personalInformationModel.data?.vatNo;
|
||||
if (vatNumber != null &&
|
||||
vatNumber.trim().isNotEmpty &&
|
||||
printTransactionModel.personalInformationModel.data?.meta?.showVat == 1) {
|
||||
final vatName = printTransactionModel.personalInformationModel.data?.vatName;
|
||||
final label = vatName != null ? '$vatName:' : 'Shop GST:';
|
||||
bytes += generator.text(
|
||||
'$label $vatNumber',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
|
||||
await addText(
|
||||
data,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
),
|
||||
linesAfter: 1,
|
||||
);
|
||||
|
||||
// Add footer
|
||||
bytes += generator.text('Thank you!', styles: const PosStyles(align: PosAlign.center, bold: true));
|
||||
bytes += generator.text(
|
||||
'Note: Goods once sold will not be taken back or exchanged.',
|
||||
styles: const PosStyles(align: PosAlign.center, bold: false),
|
||||
linesAfter: 1,
|
||||
);
|
||||
|
||||
bytes += generator.text(
|
||||
'Developed By: $companyName',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: 1,
|
||||
);
|
||||
|
||||
bytes += generator.cut();
|
||||
return bytes;
|
||||
} catch (e) {
|
||||
print('Error generating print ticket: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _isAscii(String input) {
|
||||
for (final c in input.runes) {
|
||||
if (c > 127) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<List<int>> _textToImageBytes(
|
||||
Generator generator,
|
||||
String text, {
|
||||
PosStyles? styles,
|
||||
}) async {
|
||||
try {
|
||||
const double fontSize = 26.0;
|
||||
const double horizontalPadding = 10.0;
|
||||
const double lineSpacing = 1.2;
|
||||
|
||||
const double printerWidthMm = 58.0;
|
||||
const double printerDpi = 203.0;
|
||||
|
||||
final double printerWidthPx = (printerWidthMm * printerDpi / 25.4) - (horizontalPadding * 2);
|
||||
const String fallbackFont = 'Arial Unicode MS';
|
||||
|
||||
final textStyle = TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: styles?.bold == true ? FontWeight.bold : FontWeight.normal,
|
||||
color: Colors.black,
|
||||
fontFamily: fallbackFont,
|
||||
height: lineSpacing,
|
||||
);
|
||||
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(text: text, style: textStyle),
|
||||
textDirection: TextDirection.ltr,
|
||||
maxLines: 100,
|
||||
ellipsis: '...',
|
||||
);
|
||||
|
||||
textPainter.layout(maxWidth: printerWidthPx);
|
||||
|
||||
final double imageWidth = printerWidthPx + (horizontalPadding * 2);
|
||||
final double imageHeight = textPainter.height + 20.0;
|
||||
|
||||
final recorder = ui.PictureRecorder();
|
||||
final canvas = Canvas(
|
||||
recorder,
|
||||
Rect.fromLTWH(0, 0, imageWidth, imageHeight),
|
||||
);
|
||||
|
||||
textPainter.paint(
|
||||
canvas,
|
||||
Offset(horizontalPadding, 10.0),
|
||||
);
|
||||
|
||||
final picture = recorder.endRecording();
|
||||
final uiImage = await picture.toImage(
|
||||
imageWidth.toInt(),
|
||||
imageHeight.toInt(),
|
||||
);
|
||||
|
||||
final byteData = await uiImage.toByteData(format: ui.ImageByteFormat.png);
|
||||
final pngBytes = byteData!.buffer.asUint8List();
|
||||
final image = img.decodePng(pngBytes)!;
|
||||
|
||||
return generator.image(image);
|
||||
} catch (e) {
|
||||
print('Error in _textToImageBytes: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
117
lib/thermal priting invoices/provider/print_lavel_provider.dart
Normal file
117
lib/thermal priting invoices/provider/print_lavel_provider.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:bluetooth_print_plus/bluetooth_print_plus.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
final labelPrinterProvider = ChangeNotifierProvider((ref) => ThermalPrinter());
|
||||
|
||||
class ThermalPrinter extends ChangeNotifier {
|
||||
@override
|
||||
void addListener(VoidCallback listener) async {
|
||||
// TODO: implement addListener
|
||||
super.addListener(listener);
|
||||
await BluetoothPrintPlus.startScan(timeout: Duration(seconds: 10));
|
||||
}
|
||||
|
||||
List<BluetoothDevice> availableBluetoothDevices = [];
|
||||
bool isBluetoothConnected = false;
|
||||
|
||||
// Future<void> getBluetooth() async {
|
||||
// availableBluetoothDevices = await BluetoothPrintPlus.scanResults;
|
||||
// isBluetoothConnected = await PrintBluetoothThermal.connectionStatus;
|
||||
// notifyListeners();
|
||||
// }
|
||||
//
|
||||
// Future<bool> setConnect(String mac) async {
|
||||
// bool status = false;
|
||||
// final bool result = await PrintBluetoothThermal.connect(macPrinterAddress: mac);
|
||||
// if (result == true) {
|
||||
// isBluetoothConnected = true;
|
||||
// status = true;
|
||||
// }
|
||||
// notifyListeners();
|
||||
// return status;
|
||||
// }
|
||||
|
||||
Future<dynamic> listOfBluDialog({required BuildContext context}) async {
|
||||
// begin scan
|
||||
|
||||
// final _scanResultsSubscription = BluetoothPrintPlus.scanResults.listen((event) {
|
||||
// print('${event.length}');
|
||||
// // if (mounted) {
|
||||
// // setState(() {
|
||||
// // _scanResults = event;
|
||||
// // });
|
||||
// // }
|
||||
// });
|
||||
return showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: CupertinoAlertDialog(
|
||||
insetAnimationCurve: Curves.bounceInOut,
|
||||
// content: Container(
|
||||
// height: availableBluetoothDevices.isNotEmpty ? (availableBluetoothDevices.length * 80).toDouble() : 150,
|
||||
// width: double.maxFinite,
|
||||
// child: StreamBuilder(
|
||||
// stream: FlutterBluetoothPrinter.discovery,
|
||||
// builder: (context, snapshot){
|
||||
//
|
||||
//
|
||||
// // final List<BluetoothDevice> hh = snapshot.data as List<BluetoothDevice>;
|
||||
// print('this is it--------->$snapshot');
|
||||
// return ListView.builder(
|
||||
// itemCount: 0,
|
||||
// itemBuilder: (context, index){
|
||||
// // final device = hh.elementAt(index);
|
||||
// return ListTile(
|
||||
// // title: Text(device.name ?? 'No Name'),
|
||||
// // subtitle: Text(device.address),
|
||||
// onTap: (){
|
||||
// // do anything
|
||||
// // FlutterBluetoothPrinter.printImage(
|
||||
// // address: device.address,
|
||||
// // image: // some image
|
||||
// // );
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// )),
|
||||
title: Text(
|
||||
'Connect Your Device',
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: Text(
|
||||
lang.S.of(context).cancel,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
onPressed: () async {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Navigator.pop(context);
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Future<void> printSalesThermalInvoiceNow({required PrintSalesTransactionModel transaction, required List<SalesDetails>? productList, required BuildContext context}) async {
|
||||
// await getBluetooth();
|
||||
// isBluetoothConnected ? SalesThermalPrinterInvoice().printSalesTicket(printTransactionModel: transaction, productList: productList) : listOfBluDialog(context: context);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/Products/Model/product_total_stock_model.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:mobile_pos/model/business_info_model.dart';
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||
|
||||
import '../../Screens/Products/Model/product_model.dart';
|
||||
import '../../Screens/Purchase/Model/purchase_transaction_model.dart' hide Product;
|
||||
import '../../constant.dart';
|
||||
import '../../model/sale_transaction_model.dart';
|
||||
import '../model/print_transaction_model.dart';
|
||||
import '../thermal_invoice_due.dart';
|
||||
import '../thermal_invoice_purchase.dart';
|
||||
import '../thermal_invoice_sales.dart';
|
||||
import '../thermal_invoice_stock.dart';
|
||||
import '../thermal_lebels_printing.dart';
|
||||
|
||||
final thermalPrinterProvider = ChangeNotifierProvider((ref) => ThermalPrinter());
|
||||
|
||||
class ThermalPrinter extends ChangeNotifier {
|
||||
List<BluetoothInfo> availableBluetoothDevices = [];
|
||||
bool isBluetoothConnected = false;
|
||||
|
||||
Future<void> getBluetooth() async {
|
||||
availableBluetoothDevices = await PrintBluetoothThermal.pairedBluetooths;
|
||||
isBluetoothConnected = await PrintBluetoothThermal.connectionStatus;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<bool> setConnect(String mac) async {
|
||||
bool status = false;
|
||||
final bool result = await PrintBluetoothThermal.connect(macPrinterAddress: mac);
|
||||
if (result == true) {
|
||||
isBluetoothConnected = true;
|
||||
status = true;
|
||||
}
|
||||
notifyListeners();
|
||||
return status;
|
||||
}
|
||||
|
||||
Future<dynamic> listOfBluDialog({required BuildContext context}) async {
|
||||
return showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: CupertinoAlertDialog(
|
||||
insetAnimationCurve: Curves.bounceInOut,
|
||||
content: Container(
|
||||
height: availableBluetoothDevices.isNotEmpty ? (availableBluetoothDevices.length * 80).toDouble() : 150,
|
||||
width: double.maxFinite,
|
||||
child: availableBluetoothDevices.isNotEmpty
|
||||
? ListView.builder(
|
||||
padding: EdgeInsets.all(0), // Removed padding from ListView
|
||||
shrinkWrap: true,
|
||||
itemCount: availableBluetoothDevices.isNotEmpty ? availableBluetoothDevices.length : 0,
|
||||
itemBuilder: (context1, index) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.all(0), // Removed padding from ListTile
|
||||
onTap: () async {
|
||||
BluetoothInfo select = availableBluetoothDevices[index];
|
||||
bool isConnect = await setConnect(select.macAdress);
|
||||
isConnect ? finish(context1) : toast(lang.S.of(context1).tryAgain);
|
||||
},
|
||||
title: Text(
|
||||
availableBluetoothDevices[index].name,
|
||||
style: TextStyle(fontSize: 12, color: Colors.black, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
lang.S.of(context1).clickToConnect,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey.shade500,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: const Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.bluetooth_disabled,
|
||||
size: 40,
|
||||
color: kMainColor,
|
||||
),
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
'Not available',
|
||||
style: TextStyle(fontSize: 14, color: kGreyTextColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
'Connect Your Device',
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: Text(
|
||||
lang.S.of(context).cancel,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
onPressed: () async {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Navigator.pop(context);
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> printSalesThermalInvoiceNow({required PrintSalesTransactionModel transaction, required List<SalesDetails>? productList, required BuildContext context}) async {
|
||||
await getBluetooth();
|
||||
isBluetoothConnected
|
||||
? SalesThermalPrinterInvoice().printSalesTicket(printTransactionModel: transaction, productList: productList, context: context)
|
||||
: listOfBluDialog(context: context);
|
||||
}
|
||||
|
||||
Future<void> printPurchaseThermalInvoiceNow(
|
||||
{required PrintPurchaseTransactionModel transaction, required List<PurchaseDetails>? productList, required BuildContext context, required String? invoiceSize}) async {
|
||||
await getBluetooth();
|
||||
isBluetoothConnected
|
||||
? PurchaseThermalPrinterInvoice().printPurchaseThermalInvoice(printTransactionModel: transaction, productList: productList, context: context)
|
||||
: listOfBluDialog(context: context);
|
||||
}
|
||||
|
||||
Future<void> printDueThermalInvoiceNow({required PrintDueTransactionModel transaction, required String? invoiceSize, required BuildContext context}) async {
|
||||
await getBluetooth();
|
||||
isBluetoothConnected
|
||||
? DueThermalPrinterInvoice().printDueTicket(printDueTransactionModel: transaction, invoiceSize: invoiceSize, context: context)
|
||||
: listOfBluDialog(context: context);
|
||||
}
|
||||
|
||||
Future<void> printStockInvoiceNow(
|
||||
{required List<Product> products, required BusinessInformationModel businessInformationModel, required BuildContext context, required ProductListResponse totalStock}) async {
|
||||
await getBluetooth();
|
||||
isBluetoothConnected
|
||||
? StockThermalPrinterInvoice().printStockTicket(businessInformationModel: businessInformationModel, productList: products, stock: totalStock)
|
||||
: listOfBluDialog(context: context);
|
||||
}
|
||||
|
||||
Future<void> printLabelsNow({required List<Product> products, required BuildContext context}) async {
|
||||
await getBluetooth();
|
||||
isBluetoothConnected ? SalesThermalLabels().printLabels(productList: products) : listOfBluDialog(context: context);
|
||||
}
|
||||
}
|
||||
57
lib/thermal priting invoices/sticker_image_generation.dart
Normal file
57
lib/thermal priting invoices/sticker_image_generation.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
Future<Uint8List?> createImageFromWidget(BuildContext context, Widget widget, {Duration? wait, Size? logicalSize, Size? imageSize}) async {
|
||||
final repaintBoundary = RenderRepaintBoundary();
|
||||
|
||||
logicalSize ??= View.of(context).physicalSize / View.of(context).devicePixelRatio;
|
||||
imageSize ??= View.of(context).physicalSize;
|
||||
|
||||
assert(logicalSize.aspectRatio == imageSize.aspectRatio, 'logicalSize and imageSize must not be the same');
|
||||
|
||||
final renderView = RenderView(
|
||||
child: RenderPositionedBox(alignment: Alignment.center, child: repaintBoundary),
|
||||
configuration: ViewConfiguration(
|
||||
physicalConstraints: BoxConstraints.tight(logicalSize),
|
||||
logicalConstraints: BoxConstraints.tight(logicalSize),
|
||||
devicePixelRatio: 1,
|
||||
),
|
||||
view: View.of(context) //PlatformDispatcher.instance.views.first,
|
||||
);
|
||||
|
||||
final pipelineOwner = PipelineOwner();
|
||||
final buildOwner = BuildOwner(focusManager: FocusManager());
|
||||
|
||||
pipelineOwner.rootNode = renderView;
|
||||
renderView.prepareInitialFrame();
|
||||
|
||||
final rootElement = RenderObjectToWidgetAdapter<RenderBox>(
|
||||
container: repaintBoundary,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: widget,
|
||||
)).attachToRenderTree(buildOwner);
|
||||
|
||||
buildOwner.buildScope(rootElement);
|
||||
|
||||
if (wait != null) {
|
||||
await Future.delayed(wait);
|
||||
}
|
||||
|
||||
buildOwner
|
||||
..buildScope(rootElement)
|
||||
..finalizeTree();
|
||||
|
||||
pipelineOwner
|
||||
..flushLayout()
|
||||
..flushCompositingBits()
|
||||
..flushPaint();
|
||||
|
||||
final image = await repaintBoundary.toImage(pixelRatio: imageSize.width / logicalSize.width);
|
||||
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
|
||||
return byteData?.buffer.asUint8List();
|
||||
}
|
||||
510
lib/thermal priting invoices/thermal_invoice_due.dart
Normal file
510
lib/thermal priting invoices/thermal_invoice_due.dart
Normal file
@@ -0,0 +1,510 @@
|
||||
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobile_pos/Const/lalnguage_data.dart';
|
||||
import 'package:mobile_pos/service/thermal_print/src/templates/_due_collection_invoice_template.dart';
|
||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||
|
||||
import '../Const/api_config.dart';
|
||||
import '../constant.dart';
|
||||
import 'model/print_transaction_model.dart';
|
||||
import 'network_image.dart';
|
||||
|
||||
class DueThermalPrinterInvoice {
|
||||
///_________Due________________________
|
||||
Future<void> printDueTicket(
|
||||
{required PrintDueTransactionModel printDueTransactionModel,
|
||||
required String? invoiceSize,
|
||||
required BuildContext context}) async {
|
||||
bool? isConnected = await PrintBluetoothThermal.connectionStatus;
|
||||
if (isConnected == true) {
|
||||
bool defould = (printDueTransactionModel.personalInformationModel.data?.invoiceLanguage == 'english' ||
|
||||
printDueTransactionModel.personalInformationModel.data?.invoiceLanguage == null)
|
||||
? true
|
||||
: false;
|
||||
List<int> bytes = [];
|
||||
final is80mm = printDueTransactionModel.personalInformationModel.data?.invoiceSize == '3_inch_80mm' &&
|
||||
printDueTransactionModel.personalInformationModel.data?.invoiceSize != null;
|
||||
if (defould) {
|
||||
bytes = (is80mm)
|
||||
? await getDueTicket80mm(printDueTransactionModel: printDueTransactionModel)
|
||||
: await getDueTicket50mm(printDueTransactionModel: printDueTransactionModel);
|
||||
} else {
|
||||
final bool isRTL = rtlLang.contains(await getLanguageName());
|
||||
DueThermalInvoiceTemplate dueThermalInvoiceTemplate = DueThermalInvoiceTemplate(
|
||||
context: context, printDueTransactionModel: printDueTransactionModel, is58mm: !is80mm, isRTL: isRTL);
|
||||
bytes = await dueThermalInvoiceTemplate.template;
|
||||
}
|
||||
await PrintBluetoothThermal.writeBytes(bytes);
|
||||
} else {}
|
||||
}
|
||||
|
||||
Future<List<int>> getDueTicket50mm({required PrintDueTransactionModel printDueTransactionModel}) async {
|
||||
final transactions = printDueTransactionModel.dueTransactionModel!.transactions ?? [];
|
||||
|
||||
List<String> paymentLabels = [];
|
||||
|
||||
for (var item in transactions) {
|
||||
String label;
|
||||
|
||||
switch (item.transactionType) {
|
||||
case 'cash_payment':
|
||||
label = 'Cash';
|
||||
break;
|
||||
case 'cheque_payment':
|
||||
label = 'Cheque';
|
||||
break;
|
||||
case 'wallet_payment':
|
||||
label = 'Wallet';
|
||||
break;
|
||||
default:
|
||||
label = item.paymentType?.name ?? 'n/a';
|
||||
}
|
||||
|
||||
paymentLabels.add(label);
|
||||
}
|
||||
|
||||
final paidViaText = "Paid Via : ${paymentLabels.join(', ')}";
|
||||
|
||||
List<int> bytes = [];
|
||||
CapabilityProfile profile = await CapabilityProfile.load();
|
||||
final generator = Generator(PaperSize.mm58, profile);
|
||||
// final ByteData data = await rootBundle.load('images/logo.png');
|
||||
// final Uint8List imageBytes = data.buffer.asUint8List();
|
||||
// final Image? imagez = decodeImage(imageBytes);
|
||||
// bytes += generator.image(imagez!);
|
||||
final _qrlogo = await getNetworkImage(
|
||||
"${APIConfig.domain}${printDueTransactionModel.personalInformationModel.data?.invoiceScannerLogo}");
|
||||
|
||||
final _logo = await getNetworkImage(
|
||||
"${APIConfig.domain}${printDueTransactionModel.personalInformationModel.data?.thermalInvoiceLogo}");
|
||||
|
||||
///____________Image__________________________________
|
||||
if (_logo != null && printDueTransactionModel.personalInformationModel.data?.showThermalInvoiceLogo == 1) {
|
||||
final img.Image resized = img.copyResize(
|
||||
_logo,
|
||||
width: 184,
|
||||
);
|
||||
final img.Image grayscale = img.grayscale(resized);
|
||||
bytes += generator.imageRaster(grayscale, imageFn: PosImageFn.bitImageRaster);
|
||||
}
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showCompanyName == 1) {
|
||||
bytes += generator.text(printDueTransactionModel.personalInformationModel.data?.companyName ?? '',
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1);
|
||||
}
|
||||
if (printDueTransactionModel.dueTransactionModel?.branch?.name != null) {
|
||||
bytes += generator.text(printDueTransactionModel.dueTransactionModel?.branch?.name ?? '',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
bytes += generator.text(
|
||||
'Seller :${printDueTransactionModel.dueTransactionModel?.user?.role == "shop-owner" ? 'Admin' : printDueTransactionModel.dueTransactionModel?.user?.name ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showAddress == 1) {
|
||||
if (printDueTransactionModel.dueTransactionModel?.branch?.address != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.address != null) {
|
||||
bytes += generator.text(
|
||||
printDueTransactionModel.dueTransactionModel?.branch?.address ??
|
||||
printDueTransactionModel.personalInformationModel.data?.address ??
|
||||
'',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showVat == 1) {
|
||||
if (printDueTransactionModel.personalInformationModel.data?.vatNo != null &&
|
||||
printDueTransactionModel.personalInformationModel.data?.meta?.showVat == 1) {
|
||||
bytes += generator.text(
|
||||
"${printDueTransactionModel.personalInformationModel.data?.vatName ?? 'VAT No :'}${printDueTransactionModel.personalInformationModel.data?.vatNo ?? ''}",
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
}
|
||||
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showPhoneNumber == 1) {
|
||||
if (printDueTransactionModel.dueTransactionModel?.branch?.phone != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.phoneNumber != null) {
|
||||
bytes += generator.text(
|
||||
printDueTransactionModel.dueTransactionModel?.branch?.phone ??
|
||||
printDueTransactionModel.personalInformationModel.data?.phoneNumber ??
|
||||
'n/a',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
}
|
||||
bytes += generator.emptyLines(1);
|
||||
bytes += generator.text('Receipt',
|
||||
styles: const PosStyles(
|
||||
underline: true,
|
||||
align: PosAlign.center,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1);
|
||||
bytes += generator.text('Received From: ${printDueTransactionModel.dueTransactionModel?.party?.name} ',
|
||||
styles: const PosStyles(align: PosAlign.left));
|
||||
bytes += generator.text('Mobile: ${printDueTransactionModel.dueTransactionModel?.party?.phone}',
|
||||
styles: const PosStyles(align: PosAlign.left));
|
||||
// bytes += generator.text('Received By: ${printDueTransactionModel.dueTransactionModel?.user?.name}', styles: const PosStyles(align: PosAlign.left));
|
||||
bytes += generator.text('Receipt: ${printDueTransactionModel.dueTransactionModel?.invoiceNumber ?? 'Not Provided'}',
|
||||
styles: const PosStyles(align: PosAlign.left));
|
||||
if (printDueTransactionModel.dueTransactionModel?.paymentDate != null) {
|
||||
DateTime saleDate = DateTime.parse(printDueTransactionModel.dueTransactionModel!.paymentDate!);
|
||||
String formattedDate = DateFormat('M/d/yyyy h:mm a').format(saleDate);
|
||||
|
||||
bytes += generator.text(
|
||||
'Date: $formattedDate',
|
||||
styles: const PosStyles(align: PosAlign.left),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
// bytes += generator.hr();
|
||||
// bytes += generator.row([
|
||||
// PosColumn(text: 'Invoice', width: 8, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
// PosColumn(text: 'Due', width: 4, styles: const PosStyles(align: PosAlign.right, bold: true)),
|
||||
// ]);
|
||||
// bytes += generator.hr();
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Total Due',
|
||||
width: 8,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.left,
|
||||
)),
|
||||
PosColumn(
|
||||
text: printDueTransactionModel.dueTransactionModel!.totalDue.toString(),
|
||||
width: 4,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
]);
|
||||
|
||||
bytes += generator.hr();
|
||||
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Payment Amount:',
|
||||
width: 8,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.left,
|
||||
)),
|
||||
PosColumn(
|
||||
text: printDueTransactionModel.dueTransactionModel!.payDueAmount.toString(),
|
||||
width: 4,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
]);
|
||||
bytes += generator.row([
|
||||
PosColumn(text: 'Remaining Due:', width: 8, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(
|
||||
text: printDueTransactionModel.dueTransactionModel!.dueAmountAfterPay.toString(),
|
||||
width: 4,
|
||||
styles: const PosStyles(align: PosAlign.right, bold: true)),
|
||||
]);
|
||||
bytes += generator.hr();
|
||||
// bytes += generator.row([
|
||||
// PosColumn(
|
||||
// text: 'Payment Type:',
|
||||
// width: 8,
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.left,
|
||||
// )),
|
||||
// PosColumn(
|
||||
// text: printDueTransactionModel.dueTransactionModel!.paymentType?.name ?? 'N/A',
|
||||
// width: 4,
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.right,
|
||||
// )),
|
||||
// ]);
|
||||
|
||||
bytes += generator.text(
|
||||
paidViaText,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.left,
|
||||
),
|
||||
linesAfter: 1,
|
||||
);
|
||||
|
||||
// ticket.feed(2);
|
||||
if (printDueTransactionModel.personalInformationModel.data?.gratitudeMessage != null &&
|
||||
printDueTransactionModel.personalInformationModel.data?.showGratitudeMsg == 1) {
|
||||
bytes += generator.text(printDueTransactionModel.personalInformationModel.data?.gratitudeMessage ?? '',
|
||||
styles: const PosStyles(align: PosAlign.center, bold: true));
|
||||
bytes += generator.text(printDueTransactionModel.dueTransactionModel!.paymentDate ?? '',
|
||||
styles: const PosStyles(align: PosAlign.center), linesAfter: 1);
|
||||
}
|
||||
|
||||
if ((printDueTransactionModel.personalInformationModel.data?.invoiceNoteLevel != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.invoiceNote != null) &&
|
||||
printDueTransactionModel.personalInformationModel.data?.showNote == 1) {
|
||||
bytes += generator.text(
|
||||
'${printDueTransactionModel.personalInformationModel.data?.invoiceNoteLevel ?? ''}: ${printDueTransactionModel.personalInformationModel.data?.invoiceNote ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.left, bold: false),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
if (printDueTransactionModel.personalInformationModel.data?.showInvoiceScannerLogo == 1) {
|
||||
if (_qrlogo != null) {
|
||||
final img.Image resized = img.copyResize(
|
||||
_qrlogo,
|
||||
width: 120,
|
||||
height: 120,
|
||||
);
|
||||
final img.Image grayscale = img.grayscale(resized);
|
||||
bytes += generator.imageRaster(grayscale, imageFn: PosImageFn.bitImageRaster);
|
||||
}
|
||||
}
|
||||
|
||||
if (printDueTransactionModel.personalInformationModel.data?.developByLevel != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.developBy != null) {
|
||||
bytes += generator.text(
|
||||
'${printDueTransactionModel.personalInformationModel.data?.developByLevel ?? ''}: ${printDueTransactionModel.personalInformationModel.data?.developBy ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: 1);
|
||||
}
|
||||
bytes += generator.cut();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Future<List<int>> getDueTicket80mm({required PrintDueTransactionModel printDueTransactionModel}) async {
|
||||
final transactions = printDueTransactionModel.dueTransactionModel!.transactions ?? [];
|
||||
|
||||
List<String> paymentLabels = [];
|
||||
|
||||
for (var item in transactions) {
|
||||
String label;
|
||||
|
||||
switch (item.transactionType) {
|
||||
case 'cash_payment':
|
||||
label = 'Cash';
|
||||
break;
|
||||
case 'cheque_payment':
|
||||
label = 'Cheque';
|
||||
break;
|
||||
case 'wallet_payment':
|
||||
label = 'Wallet';
|
||||
break;
|
||||
default:
|
||||
label = item.paymentType?.name ?? 'n/a';
|
||||
}
|
||||
|
||||
paymentLabels.add(label);
|
||||
}
|
||||
|
||||
final paidViaText = "Paid Via : ${paymentLabels.join(', ')}";
|
||||
List<int> bytes = [];
|
||||
|
||||
final _qrlogo = await getNetworkImage(
|
||||
"${APIConfig.domain}${printDueTransactionModel.personalInformationModel.data?.invoiceScannerLogo}");
|
||||
|
||||
final _logo = await getNetworkImage(
|
||||
"${APIConfig.domain}${printDueTransactionModel.personalInformationModel.data?.thermalInvoiceLogo}");
|
||||
CapabilityProfile profile = await CapabilityProfile.load();
|
||||
final generator = Generator(PaperSize.mm80, profile);
|
||||
|
||||
///____________Image__________________________________
|
||||
if (_logo != null) {
|
||||
final img.Image resized = img.copyResize(
|
||||
_logo,
|
||||
width: 184,
|
||||
);
|
||||
final img.Image grayscale = img.grayscale(resized);
|
||||
bytes += generator.imageRaster(grayscale, imageFn: PosImageFn.bitImageRaster);
|
||||
}
|
||||
|
||||
///____________Header_____________________________________
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showCompanyName == 1) {
|
||||
bytes += generator.text(printDueTransactionModel.personalInformationModel.data?.companyName ?? '',
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1);
|
||||
}
|
||||
if (printDueTransactionModel.dueTransactionModel?.branch?.name != null) {
|
||||
bytes += generator.text('Branch: ${printDueTransactionModel.dueTransactionModel?.branch?.name}',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showAddress == 1) {
|
||||
if (printDueTransactionModel.dueTransactionModel?.branch?.address != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.address != null) {
|
||||
bytes += generator.text(
|
||||
'Address: ${printDueTransactionModel.dueTransactionModel?.branch?.address ?? printDueTransactionModel.personalInformationModel.data?.address ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
}
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showPhoneNumber == 1) {
|
||||
if (printDueTransactionModel.dueTransactionModel?.branch?.phone != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.phoneNumber != null) {
|
||||
bytes += generator.text(
|
||||
'Mobile: ${printDueTransactionModel.dueTransactionModel?.branch?.phone ?? printDueTransactionModel.personalInformationModel.data?.phoneNumber ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
}
|
||||
if (printDueTransactionModel.personalInformationModel.data?.meta?.showVat == 1) {
|
||||
if (printDueTransactionModel.personalInformationModel.data?.vatNo != null &&
|
||||
printDueTransactionModel.personalInformationModel.data?.meta?.showVat == 1) {
|
||||
bytes += generator.text(
|
||||
"${printDueTransactionModel.personalInformationModel.data?.vatName ?? 'VAT No'}: ${printDueTransactionModel.personalInformationModel.data?.vatNo}",
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
}
|
||||
bytes += generator.emptyLines(1);
|
||||
bytes += generator.text('Receipt',
|
||||
styles: const PosStyles(
|
||||
bold: true,
|
||||
underline: true,
|
||||
align: PosAlign.center,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1);
|
||||
|
||||
///__________Customer_and_time_section_______________________
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Receipt: ${printDueTransactionModel.dueTransactionModel?.invoiceNumber ?? 'Not Provided'}',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.left)),
|
||||
PosColumn(
|
||||
text:
|
||||
'Date: ${DateFormat.yMd().format(DateTime.parse(printDueTransactionModel.dueTransactionModel?.paymentDate ?? DateTime.now().toString()))}',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.right)),
|
||||
]);
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Name: ${printDueTransactionModel.dueTransactionModel?.party?.name ?? ''}',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.left)),
|
||||
PosColumn(
|
||||
text:
|
||||
'Time: ${DateFormat.jm().format(DateTime.parse(printDueTransactionModel.dueTransactionModel?.paymentDate ?? DateTime.now().toString()))}',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.right)),
|
||||
]);
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Mobile: ${printDueTransactionModel.dueTransactionModel?.party?.phone ?? ''}',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.left)),
|
||||
PosColumn(
|
||||
text:
|
||||
'Received By: ${printDueTransactionModel.dueTransactionModel?.user?.role == "shop-owner" ? 'Admin' : printDueTransactionModel.dueTransactionModel!.user?.name}',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.right)),
|
||||
]);
|
||||
|
||||
bytes += generator.emptyLines(1);
|
||||
bytes += generator.hr();
|
||||
bytes += generator.row([
|
||||
PosColumn(text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(text: 'Invoice', width: 6, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(text: 'Due', width: 5, styles: const PosStyles(align: PosAlign.right, bold: true)),
|
||||
]);
|
||||
bytes += generator.hr();
|
||||
|
||||
bytes += generator.row([
|
||||
PosColumn(text: '1', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(
|
||||
text: printDueTransactionModel.dueTransactionModel?.invoiceNumber ?? '',
|
||||
width: 6,
|
||||
styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(
|
||||
text: formatPointNumber(printDueTransactionModel.dueTransactionModel?.totalDue ?? 0, addComma: true),
|
||||
width: 5,
|
||||
styles: const PosStyles(align: PosAlign.right)),
|
||||
]);
|
||||
|
||||
bytes += generator.hr();
|
||||
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Payment Amount:',
|
||||
width: 9,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
PosColumn(
|
||||
text: formatPointNumber(printDueTransactionModel.dueTransactionModel?.payDueAmount ?? 0, addComma: true),
|
||||
width: 3,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
]);
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Remaining Due:',
|
||||
width: 9,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
PosColumn(
|
||||
text:
|
||||
formatPointNumber((printDueTransactionModel.dueTransactionModel?.dueAmountAfterPay ?? 0), addComma: true),
|
||||
width: 3,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
]);
|
||||
|
||||
// bytes += generator.hr();
|
||||
bytes += generator.text(
|
||||
'-----------------------------',
|
||||
styles: const PosStyles(align: PosAlign.right),
|
||||
);
|
||||
bytes += generator.text(
|
||||
paidViaText,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.left,
|
||||
),
|
||||
linesAfter: 1,
|
||||
);
|
||||
|
||||
if (printDueTransactionModel.personalInformationModel.data?.gratitudeMessage != null &&
|
||||
printDueTransactionModel.personalInformationModel.data?.showGratitudeMsg == 1) {
|
||||
bytes += generator.text(printDueTransactionModel.personalInformationModel.data?.gratitudeMessage ?? '',
|
||||
styles: const PosStyles(align: PosAlign.center, bold: true));
|
||||
}
|
||||
bytes += generator.text(printDueTransactionModel.dueTransactionModel!.paymentDate ?? '',
|
||||
styles: const PosStyles(align: PosAlign.center), linesAfter: 1);
|
||||
|
||||
if ((printDueTransactionModel.personalInformationModel.data?.invoiceNoteLevel != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.invoiceNote != null) &&
|
||||
printDueTransactionModel.personalInformationModel.data?.showNote == 1) {
|
||||
bytes += generator.text(
|
||||
'${printDueTransactionModel.personalInformationModel.data?.invoiceNoteLevel ?? ''}: ${printDueTransactionModel.personalInformationModel.data?.invoiceNote ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.left, bold: false),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
if (printDueTransactionModel.personalInformationModel.data?.showInvoiceScannerLogo == 1) {
|
||||
if (_qrlogo != null) {
|
||||
final img.Image resized = img.copyResize(
|
||||
_qrlogo,
|
||||
width: 120,
|
||||
height: 120,
|
||||
);
|
||||
final img.Image grayscale = img.grayscale(resized);
|
||||
bytes += generator.imageRaster(grayscale, imageFn: PosImageFn.bitImageRaster);
|
||||
}
|
||||
}
|
||||
|
||||
if (printDueTransactionModel.personalInformationModel.data?.developByLevel != null ||
|
||||
printDueTransactionModel.personalInformationModel.data?.developBy != null) {
|
||||
bytes += generator.text(
|
||||
'${printDueTransactionModel.personalInformationModel.data?.developByLevel ?? ''}: ${printDueTransactionModel.personalInformationModel.data?.developBy ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: 1);
|
||||
}
|
||||
bytes += generator.cut();
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
1053
lib/thermal priting invoices/thermal_invoice_purchase.dart
Normal file
1053
lib/thermal priting invoices/thermal_invoice_purchase.dart
Normal file
File diff suppressed because it is too large
Load Diff
1170
lib/thermal priting invoices/thermal_invoice_sales.dart
Normal file
1170
lib/thermal priting invoices/thermal_invoice_sales.dart
Normal file
File diff suppressed because it is too large
Load Diff
195
lib/thermal priting invoices/thermal_invoice_stock.dart
Normal file
195
lib/thermal priting invoices/thermal_invoice_stock.dart
Normal file
@@ -0,0 +1,195 @@
|
||||
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobile_pos/Screens/Products/Model/product_model.dart';
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
|
||||
|
||||
import '../Const/api_config.dart';
|
||||
import '../Screens/Products/Model/product_total_stock_model.dart';
|
||||
import '../constant.dart';
|
||||
import '../model/business_info_model.dart';
|
||||
import 'network_image.dart';
|
||||
|
||||
class StockThermalPrinterInvoice {
|
||||
///________Sales____________________
|
||||
|
||||
Future<void> printStockTicket({
|
||||
required BusinessInformationModel businessInformationModel,
|
||||
required List<Product>? productList,
|
||||
required ProductListResponse stock,
|
||||
}) async {
|
||||
bool? isConnected = await PrintBluetoothThermal.connectionStatus;
|
||||
if (isConnected == true) {
|
||||
String st = await PrintBluetoothThermal.platformVersion;
|
||||
List<int> bytes = await getStockTicket(
|
||||
businessInformationModel: businessInformationModel,
|
||||
productList: productList,
|
||||
is80mm: businessInformationModel.data?.invoiceSize == '3_inch_80mm',
|
||||
stockValue: stock);
|
||||
if (productList?.isNotEmpty ?? false) {
|
||||
await PrintBluetoothThermal.writeBytes(bytes);
|
||||
EasyLoading.showSuccess('Successfully Printed');
|
||||
} else {
|
||||
toast('No Product Found');
|
||||
}
|
||||
} else {
|
||||
EasyLoading.showError('Unable to connect with printer');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<int>> getStockTicket(
|
||||
{required BusinessInformationModel businessInformationModel,
|
||||
required List<Product>? productList,
|
||||
required bool is80mm,
|
||||
ProductListResponse? stockValue}) async {
|
||||
final _logo = await getNetworkImage("${APIConfig.domain}${businessInformationModel.data?.thermalInvoiceLogo}");
|
||||
|
||||
String formattedDate = DateFormat('dd/MM/yyyy').format(DateTime.now());
|
||||
String formattedTime = DateFormat('hh:mm a').format(DateTime.now());
|
||||
|
||||
List<int> bytes = [];
|
||||
CapabilityProfile profile = await CapabilityProfile.load();
|
||||
final generator = Generator(is80mm ? PaperSize.mm80 : PaperSize.mm58, profile);
|
||||
|
||||
///____________Image__________________________________
|
||||
if (_logo != null && businessInformationModel.data?.showThermalInvoiceLogo == 1) {
|
||||
final img.Image resized = img.copyResize(
|
||||
_logo,
|
||||
width: 184,
|
||||
);
|
||||
final img.Image grayscale = img.grayscale(resized);
|
||||
bytes += generator.imageRaster(grayscale, imageFn: PosImageFn.bitImageRaster);
|
||||
}
|
||||
|
||||
if (businessInformationModel.data?.meta?.showCompanyName == 1) {
|
||||
bytes += generator.text(
|
||||
businessInformationModel.data?.companyName ?? '',
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
|
||||
bytes += generator.text(
|
||||
'Seller :${businessInformationModel.data?.user?.role == "shop-owner" ? 'Admin' : businessInformationModel.data?.user?.name}',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
if (businessInformationModel.data?.address != null) {
|
||||
bytes +=
|
||||
generator.text(businessInformationModel.data?.address ?? '', styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
if (businessInformationModel.data?.meta?.showPhoneNumber == 1) {
|
||||
if (businessInformationModel.data?.phoneNumber != null) {
|
||||
bytes += generator.text('Phone : ${businessInformationModel.data?.phoneNumber ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center));
|
||||
}
|
||||
}
|
||||
if (businessInformationModel.data?.meta?.showVat == 1) {
|
||||
if (businessInformationModel.data?.vatNo != null && businessInformationModel.data?.meta?.showVat == 1) {
|
||||
bytes += generator.text(
|
||||
"${businessInformationModel.data?.vatName ?? 'VAT No'}: ${businessInformationModel.data?.vatNo ?? ''}",
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: 1);
|
||||
}
|
||||
}
|
||||
bytes += generator.text('Stock List',
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.center,
|
||||
underline: true,
|
||||
height: PosTextSize.size2,
|
||||
width: PosTextSize.size2,
|
||||
),
|
||||
linesAfter: 1);
|
||||
bytes += generator.text('Date : $formattedDate', styles: const PosStyles(align: PosAlign.left));
|
||||
bytes += generator.text('Time : $formattedTime', styles: const PosStyles(align: PosAlign.left));
|
||||
bytes += generator.hr();
|
||||
bytes += generator.row([
|
||||
PosColumn(text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(text: 'Item Name', width: is80mm ? 7 : 6, styles: const PosStyles(align: PosAlign.left, bold: true)),
|
||||
PosColumn(text: 'Qty', width: 2, styles: const PosStyles(align: PosAlign.center, bold: true)),
|
||||
PosColumn(text: 'Price', width: is80mm ? 2 : 3, styles: const PosStyles(align: PosAlign.right, bold: true)),
|
||||
]);
|
||||
bytes += generator.hr();
|
||||
List.generate(productList?.length ?? 1, (index) {
|
||||
final stokePrice = productList![index].stocks != null && productList[index].stocks!.isNotEmpty
|
||||
? productList[index].stocks!.last.productPurchasePrice
|
||||
: 0;
|
||||
return bytes += generator.row([
|
||||
PosColumn(
|
||||
text: '${index + 1}',
|
||||
width: 1,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.left,
|
||||
)),
|
||||
PosColumn(
|
||||
text: '${productList[index].productName}',
|
||||
width: is80mm ? 7 : 6,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.left,
|
||||
)),
|
||||
PosColumn(
|
||||
text: '${productList[index].stocksSumProductStock}',
|
||||
width: 2,
|
||||
styles: const PosStyles(align: PosAlign.center)),
|
||||
PosColumn(
|
||||
text: (formatPointNumber(stokePrice ?? 0, addComma: true)),
|
||||
width: is80mm ? 2 : 3,
|
||||
styles: const PosStyles(align: PosAlign.right)),
|
||||
]);
|
||||
});
|
||||
bytes += generator.hr();
|
||||
|
||||
bytes += generator.row([
|
||||
PosColumn(
|
||||
text: 'Total Stock value :',
|
||||
width: 9,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
PosColumn(
|
||||
text: formatPointNumber(stockValue!.totalStockValue),
|
||||
width: 3,
|
||||
styles: const PosStyles(
|
||||
align: PosAlign.right,
|
||||
)),
|
||||
]);
|
||||
bytes += generator.text('');
|
||||
// bytes += generator.text('Developed By: $companyName', styles: const PosStyles(align: PosAlign.center), linesAfter: 1);
|
||||
if (businessInformationModel.data?.gratitudeMessage != null &&
|
||||
businessInformationModel.data?.showGratitudeMsg == 1) {
|
||||
bytes += generator.text(
|
||||
businessInformationModel.data?.gratitudeMessage ?? '',
|
||||
styles: const PosStyles(align: PosAlign.center, bold: true),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
|
||||
if ((businessInformationModel.data?.invoiceNoteLevel != null ||
|
||||
businessInformationModel.data?.invoiceNote != null) &&
|
||||
businessInformationModel.data?.showNote == 1) {
|
||||
bytes += generator.text(
|
||||
'${businessInformationModel.data?.invoiceNoteLevel ?? ''}: ${businessInformationModel.data?.invoiceNote ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.left, bold: false),
|
||||
linesAfter: 1,
|
||||
);
|
||||
}
|
||||
if (businessInformationModel.data?.developByLink != null) {
|
||||
bytes += generator.qrcode(
|
||||
businessInformationModel.data?.developByLink ?? '',
|
||||
);
|
||||
bytes += generator.emptyLines(1);
|
||||
}
|
||||
if (businessInformationModel.data?.developByLevel != null || businessInformationModel.data?.developBy != null) {
|
||||
bytes += generator.text(
|
||||
'${businessInformationModel.data?.developByLevel ?? ''}: ${businessInformationModel.data?.developBy ?? ''}',
|
||||
styles: const PosStyles(align: PosAlign.center),
|
||||
linesAfter: 1);
|
||||
}
|
||||
bytes += generator.cut();
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
47
lib/thermal priting invoices/thermal_lebels_printing.dart
Normal file
47
lib/thermal priting invoices/thermal_lebels_printing.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:bluetooth_print_plus/bluetooth_print_plus.dart';
|
||||
import 'package:mobile_pos/Screens/Products/Model/product_model.dart';
|
||||
|
||||
class SalesThermalLabels {
|
||||
///________Sales____________________
|
||||
|
||||
Future<void> printLabels({required List<Product>? productList}) async {
|
||||
bool conn = BluetoothPrintPlus.isConnected;
|
||||
|
||||
print('Collection State----------------> $conn');
|
||||
|
||||
///_________________Old_______________________________________________
|
||||
// bool? isConnected = await PrintBluetoothThermal.connectionStatus;
|
||||
// if (isConnected == true) {
|
||||
// List<int> bytes = await labelPrinter(productList: productList);
|
||||
// if (true) {
|
||||
// await PrintBluetoothThermal.writeBytes(bytes);
|
||||
// EasyLoading.showSuccess('Successfully Printed');
|
||||
// } else {
|
||||
// toast('No Product Found');
|
||||
// }
|
||||
// } else {
|
||||
// EasyLoading.showError('Unable to connect with printer');
|
||||
// }
|
||||
}
|
||||
//
|
||||
// Future<List<int>> labelPrinter({required List<ProductModel>? productList}) async {
|
||||
// List<int> bytes = [];
|
||||
// CapabilityProfile profile = await CapabilityProfile.load();
|
||||
//
|
||||
// final generator = Generator(PaperSize.mm80, profile);
|
||||
//
|
||||
// ///____________Header_____________________________________
|
||||
// bytes += generator.text('This is a test',
|
||||
// styles: const PosStyles(
|
||||
// align: PosAlign.center,
|
||||
// height: PosTextSize.size2,
|
||||
// width: PosTextSize.size2,
|
||||
// ),
|
||||
// linesAfter: 1);
|
||||
// final List<int> barData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 4];
|
||||
// bytes += generator.barcode(Barcode.upcA(barData));
|
||||
//
|
||||
// bytes += generator.cut();
|
||||
// return bytes;
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user