first commit

This commit is contained in:
2026-02-07 15:57:09 +07:00
commit 157096f164
1153 changed files with 415766 additions and 0 deletions

View File

@@ -0,0 +1,304 @@
import 'dart:typed_data';
import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:image/image.dart' as img;
import 'package:mobile_pos/Const/api_config.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import 'package:mobile_pos/thermal%20priting%20invoices/model/print_transaction_model.dart';
import '../../thermer/thermer.dart' as thermer;
class DueThermalInvoiceTemplate {
DueThermalInvoiceTemplate({
required this.printDueTransactionModel,
required this.is58mm,
required this.context,
required this.isRTL,
});
final PrintDueTransactionModel printDueTransactionModel;
final bool is58mm;
final BuildContext context;
final bool isRTL;
// --- Helpers: Styles & Formats ---
thermer.TextStyle _commonStyle({double fontSize = 24, bool isBold = false}) {
return thermer.TextStyle(
fontSize: fontSize,
fontWeight: isBold ? thermer.FontWeight.bold : thermer.FontWeight.w500,
color: thermer.Colors.black,
);
}
String formatPointNumber(num number, {bool addComma = false}) {
if (addComma) return NumberFormat("#,###.##", "en_US").format(number);
return number.toStringAsFixed(2);
}
// --- Main Generator ---
Future<List<int>> get template async {
final _lang = lang.S.of(context);
final _profile = await CapabilityProfile.load();
final _generator = Generator(is58mm ? PaperSize.mm58 : PaperSize.mm80, _profile);
final _imageBytes = await _generateLayout(_lang);
final _image = img.decodeImage(_imageBytes);
if (_image == null) throw Exception('Failed to generate receipt.');
List<int> _bytes = [];
_bytes += _generator.image(_image);
_bytes += _generator.cut();
return _bytes;
}
Future<Uint8List> _generateLayout(lang.S _lang) async {
final data = printDueTransactionModel.dueTransactionModel;
final info = printDueTransactionModel.personalInformationModel.data;
// 1. Prepare Logo
thermer.ThermerImage? _logo;
if (info?.thermalInvoiceLogo != null && info?.showThermalInvoiceLogo == 1) {
try {
_logo = await thermer.ThermerImage.network(
"${APIConfig.domain}${info?.thermalInvoiceLogo}",
width: is58mm ? 120 : 184,
height: is58mm ? 120 : 184,
);
} catch (_) {}
}
//qr logo
thermer.ThermerImage? _qrLogo;
if (info?.invoiceScannerLogo != null && info?.showInvoiceScannerLogo == 1) {
try {
_qrLogo = await thermer.ThermerImage.network(
APIConfig.domain + info!.invoiceScannerLogo!,
width: is58mm ? 120 : 140,
height: is58mm ? 120 : 140,
);
} catch (_) {}
}
// 2. Prepare Payment Labels
final paymentLabels = _buildPaymentLabels();
// 3. Build Layout
final _layout = thermer.ThermerLayout(
paperSize: is58mm ? thermer.PaperSize.mm58 : thermer.PaperSize.mm80,
textDirection: isRTL ? thermer.TextDirection.rtl : thermer.TextDirection.ltr,
widgets: [
// --- Header ---
if (_logo != null) ...[thermer.ThermerAlign(child: _logo), thermer.ThermerSizedBox(height: 16)],
if (info?.meta?.showCompanyName == 1)
thermer.ThermerText(
info?.companyName ?? '',
style: _commonStyle(fontSize: is58mm ? 46 : 54),
textAlign: thermer.TextAlign.center,
),
if (data?.branch?.name != null)
thermer.ThermerText('Branch: ${data?.branch?.name}',
style: _commonStyle(), textAlign: thermer.TextAlign.center),
if (info?.meta?.showAddress == 1)
if (data?.branch?.address != null || info?.address != null)
thermer.ThermerText(
'${_lang.address}: ${data?.branch?.address ?? info?.address ?? ''}',
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
if (info?.meta?.showPhoneNumber == 1)
if (data?.branch?.phone != null || info?.phoneNumber != null)
thermer.ThermerText(
'${_lang.mobile} ${data?.branch?.phone ?? info?.phoneNumber ?? ''}',
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
if (info?.meta?.showVat == 1)
if (info?.vatNo != null && info?.meta?.showVat == 1)
thermer.ThermerText(
"${info?.vatName ?? _lang.vatNumber}: ${info?.vatNo}",
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
thermer.ThermerSizedBox(height: 16),
thermer.ThermerText(
_lang.receipt, // Due collection is usually a Receipt
style: _commonStyle(fontSize: is58mm ? 30 : 48, isBold: true)
.copyWith(decoration: thermer.TextDecoration.underline),
textAlign: thermer.TextAlign.center,
),
thermer.ThermerSizedBox(height: 16),
// --- Info Section ---
..._buildInfoSection(_lang),
thermer.ThermerSizedBox(height: 16),
// --- Data Table (Single Row for Due Context) ---
thermer.ThermerTable(
header: thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText(_lang.sl, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.invoice, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.dueAmount, textAlign: thermer.TextAlign.end, style: _commonStyle(isBold: true)),
]),
data: [
thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText('1', style: _commonStyle()),
thermer.ThermerText(data?.invoiceNumber ?? '', style: _commonStyle()),
thermer.ThermerText(formatPointNumber(data?.totalDue ?? 0, addComma: true),
textAlign: thermer.TextAlign.end, style: _commonStyle()),
])
],
cellWidths: is58mm ? {0: null, 1: 0.3} : {0: 0.1, 1: null, 2: 0.3},
),
thermer.ThermerDivider.horizontal(),
// --- Calculations ---
_buildCalculationColumn(_lang),
thermer.ThermerDivider.horizontal(),
thermer.ThermerSizedBox(height: 8),
// --- Payment Info ---
thermer.ThermerText(
"${_lang.paidVia} : ${paymentLabels.join(', ')}",
style: _commonStyle(),
textAlign: thermer.TextAlign.left,
),
thermer.ThermerSizedBox(height: 16),
// --- Footer ---
if (info?.gratitudeMessage != null && info?.showGratitudeMsg == 1)
thermer.ThermerText(info?.gratitudeMessage ?? '',
textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
if (data?.paymentDate != null)
thermer.ThermerText(
DateFormat('M/d/yyyy h:mm a').format(DateTime.parse(data!.paymentDate!)),
textAlign: thermer.TextAlign.center,
style: _commonStyle(),
),
if (info?.showNote == 1)
thermer.ThermerText(
'${info?.invoiceNoteLevel ?? _lang.note}: ${info?.invoiceNote ?? ''}',
textAlign: thermer.TextAlign.left,
style: _commonStyle(),
),
thermer.ThermerSizedBox(height: 16),
if (_qrLogo != null) ...[thermer.ThermerAlign(child: _qrLogo), thermer.ThermerSizedBox(height: 1)],
if (info?.developBy != null)
thermer.ThermerText(
'${info?.developByLevel ?? _lang.developedBy}: ${info?.developBy}',
textAlign: thermer.TextAlign.center,
style: _commonStyle(),
),
thermer.ThermerSizedBox(height: 200), // Cutter Space
],
);
return _layout.toUint8List();
}
// --- Sub-Builders ---
List<thermer.ThermerWidget> _buildInfoSection(lang.S _lang) {
final data = printDueTransactionModel.dueTransactionModel;
final dateStr = data?.paymentDate != null ? DateFormat.yMd().format(DateTime.parse(data!.paymentDate!)) : '';
final timeStr = data?.paymentDate != null ? DateFormat.jm().format(DateTime.parse(data!.paymentDate!)) : '';
final receiptText = '${_lang.receipt}: ${data?.invoiceNumber ?? 'Not Provided'}';
final dateText = '${_lang.date}: $dateStr';
final timeText = '${_lang.time}: $timeStr';
final nameText = '${_lang.receivedFrom}: ${data?.party?.name ?? ''}';
final mobileText = '${_lang.mobile} ${data?.party?.phone ?? ''}';
final receivedByText =
'${_lang.receivedBy}: ${data?.user?.role == "shop-owner" ? 'Admin' : data?.user?.name ?? ''}';
if (is58mm) {
// 58mm: Stacked
return [
thermer.ThermerText(receiptText, style: _commonStyle()),
if (data?.paymentDate != null) thermer.ThermerText("$dateText $timeStr", style: _commonStyle()),
thermer.ThermerText(nameText, style: _commonStyle()),
thermer.ThermerText(mobileText, style: _commonStyle()),
thermer.ThermerText(receivedByText, style: _commonStyle()),
];
} else {
// 80mm: Two Columns
return [
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(receiptText, style: _commonStyle()),
if (data?.paymentDate != null) thermer.ThermerText(dateText, style: _commonStyle()),
],
),
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(nameText, style: _commonStyle()),
thermer.ThermerText(timeText, style: _commonStyle()),
],
),
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(mobileText, style: _commonStyle()),
thermer.ThermerText(receivedByText, style: _commonStyle()),
],
),
];
}
}
thermer.ThermerColumn _buildCalculationColumn(lang.S _lang) {
final data = printDueTransactionModel.dueTransactionModel;
thermer.ThermerRow calcRow(String label, num value, {bool bold = false, bool isCurrency = true}) {
return thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(label, style: _commonStyle(isBold: bold)),
thermer.ThermerText(
isCurrency ? formatPointNumber(value, addComma: true) : value.toString(),
textAlign: thermer.TextAlign.end,
style: _commonStyle(isBold: bold),
),
],
);
}
return thermer.ThermerColumn(children: [
calcRow('${_lang.totalDue}:', data?.totalDue ?? 0),
calcRow('${_lang.paymentsAmount}:', data?.payDueAmount ?? 0, bold: true),
calcRow('${_lang.remainingDue}:', data?.dueAmountAfterPay ?? 0, bold: true),
]);
}
List<String> _buildPaymentLabels() {
final transactions = printDueTransactionModel.dueTransactionModel?.transactions ?? [];
List<String> labels = [];
for (var item in transactions) {
String label = item.paymentType?.name ?? 'n/a';
if (item.transactionType == 'cash_payment') label = lang.S.of(context).cash;
if (item.transactionType == 'cheque_payment') label = lang.S.of(context).cheque;
if (item.transactionType == 'wallet_payment') label = lang.S.of(context).wallet;
labels.add(label);
}
return labels;
}
}

View File

@@ -0,0 +1,476 @@
import 'dart:typed_data';
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/api_config.dart';
import 'package:mobile_pos/Screens/Products/add%20product/modle/create_product_model.dart';
import 'package:mobile_pos/Screens/Purchase/Model/purchase_transaction_model.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import 'package:mobile_pos/thermal%20priting%20invoices/model/print_transaction_model.dart';
import '../../thermer/thermer.dart' as thermer;
class PurchaseThermalInvoiceTemplate {
PurchaseThermalInvoiceTemplate({
required this.printTransactionModel,
required this.productList,
required this.is58mm,
required this.context,
required this.isRTL,
});
final PrintPurchaseTransactionModel printTransactionModel;
final List<PurchaseDetails>? productList;
final bool is58mm;
final BuildContext context;
final bool isRTL;
// --- Helpers: Styles & Formats ---
thermer.TextStyle _commonStyle({double fontSize = 24, bool isBold = false}) {
return thermer.TextStyle(
fontSize: fontSize,
fontWeight: isBold ? thermer.FontWeight.bold : thermer.FontWeight.w500,
color: thermer.Colors.black,
);
}
String formatPointNumber(num number, {bool addComma = false}) {
if (addComma) return NumberFormat("#,###.##", "en_US").format(number);
return number.toStringAsFixed(2);
}
// --- Data Logic (Adapted from your provided code) ---
num _getProductPrice(num detailsId) {
return productList!.where((element) => element.id == detailsId).first.productPurchasePrice ?? 0;
}
String _getProductName(num detailsId) {
final details = printTransactionModel.purchaseTransitionModel?.details?.firstWhere(
(element) => element.id == detailsId,
orElse: () => PurchaseDetails(),
);
String name = details?.product?.productName ?? '';
if (details?.product?.productType == ProductType.variant.name) {
name += ' [${details?.stock?.batchNo ?? ''}]';
}
return name;
}
num _getProductQuantity(num detailsId) {
num totalQuantity = productList?.where((element) => element.id == detailsId).first.quantities ?? 0;
// Add returned quantities logic
if (printTransactionModel.purchaseTransitionModel?.purchaseReturns?.isNotEmpty ?? false) {
for (var returns in printTransactionModel.purchaseTransitionModel!.purchaseReturns!) {
if (returns.purchaseReturnDetails?.isNotEmpty ?? false) {
for (var details in returns.purchaseReturnDetails!) {
if (details.purchaseDetailId == detailsId) {
totalQuantity += details.returnQty ?? 0;
}
}
}
}
}
return totalQuantity;
}
num _getTotalForOldInvoice() {
num total = 0;
if (productList != null) {
for (var element in productList!) {
num productPrice = element.productPurchasePrice ?? 0;
num productQuantity = _getProductQuantity(element.id ?? 0);
total += productPrice * productQuantity;
}
}
return total;
}
num _getReturnedDiscountAmount() {
num totalReturnDiscount = 0;
if (printTransactionModel.purchaseTransitionModel?.purchaseReturns?.isNotEmpty ?? false) {
for (var returns in printTransactionModel.purchaseTransitionModel!.purchaseReturns!) {
if (returns.purchaseReturnDetails?.isNotEmpty ?? false) {
for (var details in returns.purchaseReturnDetails!) {
totalReturnDiscount += ((_getProductPrice(details.purchaseDetailId ?? 0) * (details.returnQty ?? 0)) -
((details.returnAmount ?? 0)));
}
}
}
}
return totalReturnDiscount;
}
num _getTotalReturnedAmount() {
num totalReturn = 0;
if (printTransactionModel.purchaseTransitionModel?.purchaseReturns?.isNotEmpty ?? false) {
for (var returns in printTransactionModel.purchaseTransitionModel!.purchaseReturns!) {
if (returns.purchaseReturnDetails?.isNotEmpty ?? false) {
for (var details in returns.purchaseReturnDetails!) {
totalReturn += details.returnAmount ?? 0;
}
}
}
}
return totalReturn;
}
// --- Main Generator ---
Future<List<int>> get template async {
final _profile = await CapabilityProfile.load();
final _generator = Generator(is58mm ? PaperSize.mm58 : PaperSize.mm80, _profile);
final _imageBytes = await _generateLayout();
final _image = img.decodeImage(_imageBytes);
if (_image == null) throw Exception('Failed to generate invoice.');
List<int> _bytes = [];
_bytes += _generator.image(_image);
_bytes += _generator.cut();
return _bytes;
}
Future<Uint8List> _generateLayout() async {
final data = printTransactionModel.purchaseTransitionModel;
final info = printTransactionModel.personalInformationModel.data;
final _lang = lang.S.of(context);
// 1. Prepare Logo
thermer.ThermerImage? _logo;
if (info?.thermalInvoiceLogo != null && info?.showThermalInvoiceLogo == 1) {
try {
_logo = await thermer.ThermerImage.network(
"${APIConfig.domain}${info?.thermalInvoiceLogo}",
width: is58mm ? 120 : 184,
height: is58mm ? 120 : 184,
);
} catch (_) {}
}
//qr logo
thermer.ThermerImage? _qrLogo;
if (info?.invoiceScannerLogo != null && info?.showInvoiceScannerLogo == 1) {
try {
_qrLogo = await thermer.ThermerImage.network(
APIConfig.domain + info!.invoiceScannerLogo!,
width: is58mm ? 120 : 140,
height: is58mm ? 120 : 140,
);
} catch (_) {}
}
// 2. Prepare Product Rows
final productRows = _buildProductRows();
// 3. Prepare Returns
final returnWidgets = _buildReturnSection(_lang);
// 4. Build Layout
final _layout = thermer.ThermerLayout(
paperSize: is58mm ? thermer.PaperSize.mm58 : thermer.PaperSize.mm80,
textDirection: isRTL ? thermer.TextDirection.rtl : thermer.TextDirection.ltr,
widgets: [
// --- Header ---
if (_logo != null) ...[thermer.ThermerAlign(child: _logo), thermer.ThermerSizedBox(height: 16)],
if (info?.meta?.showCompanyName == 1)
thermer.ThermerText(
info?.companyName ?? '',
style: _commonStyle(fontSize: is58mm ? 46 : 54),
textAlign: thermer.TextAlign.center,
),
if (data?.branch?.name != null)
thermer.ThermerText('${_lang.branch}: ${data?.branch?.name}',
style: _commonStyle(), textAlign: thermer.TextAlign.center),
if (info?.meta?.showAddress == 1)
if (data?.branch?.address != null || info?.address != null)
thermer.ThermerText(
'${_lang.address}: ${data?.branch?.address ?? info?.address ?? ''}',
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
if (info?.meta?.showPhoneNumber == 1)
if (data?.branch?.phone != null || info?.phoneNumber != null)
thermer.ThermerText(
'${_lang.mobile} ${data?.branch?.phone ?? info?.phoneNumber ?? ''}',
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
if (info?.meta?.showVat == 1)
if (info?.vatNo != null && info?.meta?.showVat == 1)
thermer.ThermerText(
"${info?.vatName ?? _lang.vatNumber}: ${info?.vatNo}",
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
thermer.ThermerSizedBox(height: 16),
thermer.ThermerText(
_lang.invoice,
style: _commonStyle(fontSize: is58mm ? 30 : 48, isBold: true)
.copyWith(decoration: thermer.TextDecoration.underline),
textAlign: thermer.TextAlign.center,
),
thermer.ThermerSizedBox(height: 16),
// --- Info Section ---
..._buildInfoSection(_lang),
thermer.ThermerSizedBox(height: 8),
// --- Product Table ---
thermer.ThermerTable(
header: thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText(_lang.sl, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.item, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.qty, textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.price, textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.total, textAlign: thermer.TextAlign.end, style: _commonStyle(isBold: true)),
]),
data: productRows,
cellWidths: is58mm
? {0: null, 1: 0.2, 2: 0.2, 3: 0.25} // 58mm
: {0: 0.1, 1: null, 2: 0.15, 3: 0.15, 4: 0.2}, // 80mm
columnSpacing: 10.0,
rowSpacing: 3.0,
),
thermer.ThermerDivider.horizontal(),
// --- Calculations ---
if (!is58mm)
thermer.ThermerRow(
children: [
thermer.ThermerExpanded(flex: 4, child: thermer.ThermerAlign(child: _buildPaymentInfoText(_lang))),
thermer.ThermerExpanded(flex: 6, child: _buildCalculationColumn(_lang)),
],
)
else ...[
_buildCalculationColumn(_lang),
thermer.ThermerDivider.horizontal(),
_buildPaymentInfoText(_lang),
],
thermer.ThermerSizedBox(height: 16),
// --- Returns ---
...returnWidgets,
// --- Footer ---
if (info?.gratitudeMessage != null && info?.showGratitudeMsg == 1)
thermer.ThermerText(info?.gratitudeMessage ?? '',
textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
if (data?.purchaseDate != null)
thermer.ThermerText(
DateFormat('M/d/yyyy h:mm a').format(DateTime.parse(data!.purchaseDate!)),
textAlign: thermer.TextAlign.center,
style: _commonStyle(),
),
thermer.ThermerSizedBox(height: 16),
if (info?.showNote == 1)
thermer.ThermerText(
'${info?.invoiceNoteLevel ?? _lang.note}: ${info?.invoiceNote ?? ''}',
textAlign: thermer.TextAlign.left,
style: _commonStyle(),
),
thermer.ThermerSizedBox(height: 16),
if (_qrLogo != null) ...[thermer.ThermerAlign(child: _qrLogo), thermer.ThermerSizedBox(height: 1)],
// if (info?.developByLink != null)
// thermer.ThermerAlign(child: thermer.ThermerQRCode(data: info?.developByLink ?? '', size: 120)),
if (info?.developBy != null)
thermer.ThermerText(
'${info?.developByLevel ?? _lang.developedBy}: ${info?.developBy}',
textAlign: thermer.TextAlign.center,
style: _commonStyle(),
),
thermer.ThermerSizedBox(height: 200), // Cutter Space
],
);
return _layout.toUint8List();
}
// --- Sub-Builders ---
List<thermer.ThermerWidget> _buildInfoSection(lang.S _lang) {
final data = printTransactionModel.purchaseTransitionModel;
final dateStr = data?.purchaseDate != null ? DateFormat.yMd().format(DateTime.parse(data!.purchaseDate!)) : '';
final timeStr = data?.purchaseDate != null ? DateFormat.jm().format(DateTime.parse(data!.purchaseDate!)) : '';
final invText = '${_lang.invoice}: ${data?.invoiceNumber ?? 'Not Provided'}';
final dateText = '${_lang.date}: $dateStr';
final timeText = '${_lang.time}: $timeStr';
final nameText = '${_lang.name}: ${data?.party?.name ?? 'Guest'}';
final mobileText = '${_lang.mobile} ${data?.party?.phone ?? ''}';
final purchaseByText = '${_lang.purchaseBy} ${data?.user?.role == "shop-owner" ? 'Admin' : data?.user?.name ?? ''}';
if (is58mm) {
return [
thermer.ThermerText(invText, style: _commonStyle()),
if (data?.purchaseDate != null) thermer.ThermerText("$dateText $timeStr", style: _commonStyle()),
thermer.ThermerText(nameText, style: _commonStyle()),
thermer.ThermerText(mobileText, style: _commonStyle()),
thermer.ThermerText(purchaseByText, style: _commonStyle()),
];
} else {
return [
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(invText, style: _commonStyle()),
if (data?.purchaseDate != null) thermer.ThermerText(dateText, style: _commonStyle()),
],
),
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(nameText, style: _commonStyle()),
thermer.ThermerText(timeText, style: _commonStyle()),
],
),
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(mobileText, style: _commonStyle()),
thermer.ThermerText(purchaseByText, style: _commonStyle()),
],
),
];
}
}
List<thermer.ThermerTableRow> _buildProductRows() {
List<thermer.ThermerTableRow> rows = [];
if (productList == null) return rows;
for (var index = 0; index < productList!.length; index++) {
final item = productList![index];
final qty = _getProductQuantity(item.id ?? 0);
final price = item.productPurchasePrice ?? 0;
final amount = price * qty;
rows.add(thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText('${index + 1}', style: _commonStyle()),
thermer.ThermerText(_getProductName(item.id ?? 0), style: _commonStyle()),
thermer.ThermerText(formatPointNumber(qty, addComma: true),
textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerText(formatPointNumber(price, addComma: true),
textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerText(formatPointNumber(amount, addComma: true),
textAlign: thermer.TextAlign.end, style: _commonStyle()),
]));
}
return rows;
}
thermer.ThermerColumn _buildCalculationColumn(lang.S _lang) {
final data = printTransactionModel.purchaseTransitionModel;
thermer.ThermerRow calcRow(String label, num value, {bool bold = false, bool isCurrency = true}) {
return thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(label, style: _commonStyle(isBold: bold)),
thermer.ThermerText(
isCurrency ? formatPointNumber(value, addComma: true) : value.toString(),
textAlign: thermer.TextAlign.end,
style: _commonStyle(isBold: bold),
),
],
);
}
return thermer.ThermerColumn(children: [
calcRow('${_lang.subTotal}:', _getTotalForOldInvoice()),
calcRow('${_lang.discount}:', (data?.discountAmount ?? 0) + _getReturnedDiscountAmount()),
calcRow('${data?.vat?.name ?? _lang.vat}:', data?.vatAmount ?? 0),
thermer.ThermerDivider.horizontal(),
if (_getTotalReturnedAmount() > 0) calcRow('${_lang.returnAmount}:', _getTotalReturnedAmount()),
calcRow('${_lang.totalPayable}:', data?.totalAmount ?? 0, bold: true),
calcRow('${_lang.paidAmount}:', ((data?.totalAmount ?? 0) - (data?.dueAmount ?? 0)) + (data?.changeAmount ?? 0)),
if ((data?.dueAmount ?? 0) > 0) calcRow('${_lang.dueAmount}', data?.dueAmount ?? 0),
if ((data?.changeAmount ?? 0) > 0) calcRow('${_lang.changeAmount}:', data?.changeAmount ?? 0),
]);
}
thermer.ThermerText _buildPaymentInfoText(lang.S _lang) {
final transactions = printTransactionModel.purchaseTransitionModel?.transactions ?? [];
List<String> labels = [];
for (var item in transactions) {
String label = item.paymentType?.name ?? 'n/a';
if (item.transactionType == 'cash_payment') label = _lang.cash;
if (item.transactionType == 'cheque_payment') label = _lang.cheque;
if (item.transactionType == 'wallet_payment') label = _lang.wallet;
labels.add(label);
}
return thermer.ThermerText(
"${_lang.paidVia}: ${labels.join(', ')}",
style: _commonStyle(),
textAlign: is58mm ? thermer.TextAlign.left : thermer.TextAlign.left,
);
}
List<thermer.ThermerWidget> _buildReturnSection(lang.S _lang) {
final returns = printTransactionModel.purchaseTransitionModel?.purchaseReturns;
if (returns?.isEmpty ?? true) return [];
List<thermer.ThermerWidget> widgets = [];
List<String> processedDates = [];
for (var i = 0; i < (returns?.length ?? 0); i++) {
final dateStr = returns![i].returnDate?.substring(0, 10);
if (dateStr != null && !processedDates.contains(dateStr)) {
processedDates.add(dateStr);
widgets.add(thermer.ThermerDivider.horizontal());
// Return Header
widgets.add(thermer.ThermerRow(
children: [
if (!is58mm) thermer.ThermerText(_lang.sl, style: _commonStyle(isBold: true)),
thermer.ThermerText('${_lang.retur}-${DateFormat.yMd().format(DateTime.parse(returns[i].returnDate!))}',
style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.qty, textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.total, textAlign: thermer.TextAlign.end, style: _commonStyle(isBold: true)),
],
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
));
}
widgets.add(thermer.ThermerTable(
data: (returns[i].purchaseReturnDetails ?? []).map((d) {
// Re-using index logic might be tricky here for SL, simplify if needed
return thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText('*', style: _commonStyle()), // Bullet for return items or dynamic index
thermer.ThermerText(_getProductName(d.purchaseDetailId ?? 0), style: _commonStyle()),
thermer.ThermerText('${d.returnQty ?? 0}', textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerText(formatPointNumber(d.returnAmount ?? 0, addComma: true),
textAlign: thermer.TextAlign.end, style: _commonStyle()),
]);
}).toList(),
cellWidths: is58mm ? {0: null, 1: 0.2, 2: 0.25} : {0: 0.1, 1: null, 2: 0.15, 3: 0.2},
));
}
// Add Total Return Footer inside Calculation Column generally,
// but if you want separate divider:
widgets.add(thermer.ThermerDivider.horizontal());
return widgets;
}
}

View File

@@ -0,0 +1,490 @@
import 'dart:typed_data';
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/api_config.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
import 'package:mobile_pos/model/business_info_model.dart';
import 'package:mobile_pos/model/sale_transaction_model.dart';
import '../../thermer/thermer.dart' as thermer;
class SaleThermalInvoiceTemplate {
SaleThermalInvoiceTemplate({
required this.saleInvoice,
required this.is58mm,
required this.business,
required this.context,
required this.isRTL,
});
final SalesTransactionModel saleInvoice;
final BusinessInformationModel business;
final bool is58mm;
final bool isRTL;
final BuildContext context;
// --- Helpers: Styles & Formats ---
/// Centralized Text Style to ensure Black Color everywhere
thermer.TextStyle _commonStyle({double fontSize = 24, bool isBold = false}) {
return thermer.TextStyle(
fontSize: fontSize,
fontWeight: isBold ? thermer.FontWeight.bold : thermer.FontWeight.w500,
color: thermer.Colors.black,
);
}
String formatPointNumber(num number, {bool addComma = false}) {
if (addComma) return NumberFormat("#,###.##", "en_US").format(number);
return number.toStringAsFixed(2);
}
// --- Data Logic ---
String _getProductName(num detailsId) {
final details = saleInvoice.salesDetails?.firstWhere(
(e) => e.id == detailsId,
orElse: () => SalesDetails(),
);
String name = details?.product?.productName ?? '';
if (details?.product?.productType == 'variant' && details?.stock?.batchNo != null) {
name += ' [${details!.stock!.batchNo}]';
}
return name;
}
num _getProductQty(num detailsId) {
num totalQty =
saleInvoice.salesDetails?.firstWhere((e) => e.id == detailsId, orElse: () => SalesDetails()).quantities ?? 0;
// Add returned quantities back logic
if (saleInvoice.salesReturns?.isNotEmpty ?? false) {
for (var ret in saleInvoice.salesReturns!) {
for (var det in ret.salesReturnDetails ?? []) {
if (det.saleDetailId == detailsId) totalQty += det.returnQty ?? 0;
}
}
}
return totalQty;
}
// --- Main Generator ---
@override
Future<List<int>> get template async {
final _profile = await CapabilityProfile.load();
final _generator = Generator(is58mm ? PaperSize.mm58 : PaperSize.mm80, _profile);
// Generate Layout
final _imageBytes = await _generateLayout();
final _image = img.decodeImage(_imageBytes);
if (_image == null) throw Exception('Failed to generate invoice.');
List<int> _bytes = [];
_bytes += _generator.image(_image);
_bytes += _generator.cut();
return _bytes;
}
Future<Uint8List> _generateLayout() async {
final _lang = lang.S.of(context);
// 1. Prepare Logo
thermer.ThermerImage? _logo;
if (business.data?.thermalInvoiceLogo != null && business.data?.showThermalInvoiceLogo == 1) {
try {
_logo = await thermer.ThermerImage.network(
APIConfig.domain + business.data!.thermalInvoiceLogo!,
width: is58mm ? 120 : 200,
height: is58mm ? 120 : 200,
);
} catch (_) {}
}
//qr logo
thermer.ThermerImage? _qrLogo;
if (business.data?.invoiceScannerLogo != null && business.data?.showInvoiceScannerLogo == 1) {
try {
_qrLogo = await thermer.ThermerImage.network(
APIConfig.domain + business.data!.invoiceScannerLogo!,
width: is58mm ? 120 : 140,
height: is58mm ? 120 : 140,
);
} catch (_) {}
}
// 2. Prepare Product Rows
final productRows = _buildProductRows();
// 3. Prepare Return Section
final returnWidgets = _buildReturnSection(context);
// 4. Build Layout
final _layout = thermer.ThermerLayout(
textDirection: isRTL ? thermer.TextDirection.rtl : thermer.TextDirection.ltr,
paperSize: is58mm ? thermer.PaperSize.mm58 : thermer.PaperSize.mm80,
widgets: [
// --- Header Section ---
if (_logo != null) ...[thermer.ThermerAlign(child: _logo), thermer.ThermerSizedBox(height: 16)],
if (business.data?.meta?.showCompanyName == 1)
thermer.ThermerText(
business.data?.companyName ?? "N/A",
style: _commonStyle(fontSize: is58mm ? 46 : 54, isBold: false),
textAlign: thermer.TextAlign.center,
),
if (saleInvoice.branch?.name != null)
thermer.ThermerText('${_lang.branch}: ${saleInvoice.branch?.name}',
style: _commonStyle(), textAlign: thermer.TextAlign.center),
if (business.data?.meta?.showAddress == 1)
if (business.data?.address != null || saleInvoice.branch?.address != null)
thermer.ThermerText(
saleInvoice.branch?.address ?? business.data?.address ?? 'N/A',
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
if (business.data?.meta?.showPhoneNumber == 1)
if (business.data?.phoneNumber != null || saleInvoice.branch?.phone != null)
thermer.ThermerText(
'${_lang.mobile} ${saleInvoice.branch?.phone ?? business.data?.phoneNumber ?? "N/A"}',
style: _commonStyle(),
textAlign: thermer.TextAlign.center,
),
if (business.data?.vatName != null && business.data?.meta?.showVat == 1)
thermer.ThermerText("${business.data?.vatName}: ${business.data?.vatNo}",
style: _commonStyle(), textAlign: thermer.TextAlign.center),
thermer.ThermerSizedBox(height: 16),
thermer.ThermerText(
_lang.invoice,
style: _commonStyle(fontSize: is58mm ? 30 : 48, isBold: true)
.copyWith(decoration: thermer.TextDecoration.underline),
textAlign: thermer.TextAlign.center,
),
thermer.ThermerSizedBox(height: 16),
// --- Info Section (Layout adjusted based on is58mm) ---
..._buildInfoSection(_lang),
thermer.ThermerSizedBox(height: 8),
// --- Product Table ---
thermer.ThermerTable(
header: thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText(_lang.sl, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.item, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.qty, textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.price, textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
thermer.ThermerText(_lang.amount, textAlign: thermer.TextAlign.end, style: _commonStyle(isBold: true)),
]),
data: productRows,
cellWidths: is58mm
? {0: null, 1: 0.2, 2: 0.15, 3: 0.2} // 58mm layout
: {0: 0.1, 1: null, 2: 0.15, 3: 0.2, 4: 0.2}, // 80mm layout
columnSpacing: 15.0,
rowSpacing: 3.0,
),
thermer.ThermerDivider.horizontal(),
// --- Totals Section ---
if (!is58mm)
// 80mm Split Layout
thermer.ThermerRow(
children: [
thermer.ThermerExpanded(flex: 4, child: thermer.ThermerAlign(child: _buildPaymentInfoText(_lang))),
thermer.ThermerExpanded(flex: 6, child: _buildCalculationColumn(_lang)),
],
)
else ...[
// 58mm Stacked Layout
_buildCalculationColumn(_lang),
thermer.ThermerDivider.horizontal(),
_buildPaymentInfoText(_lang),
],
thermer.ThermerSizedBox(height: 16),
// --- Returns ---
...returnWidgets,
// --- Footer ---
if (business.data?.gratitudeMessage != null && business.data?.showGratitudeMsg == 1)
thermer.ThermerText(business.data?.gratitudeMessage ?? '',
textAlign: thermer.TextAlign.center, style: _commonStyle(isBold: true)),
if (business.data?.showNote == 1)
thermer.ThermerText('${business.data?.invoiceNoteLevel ?? _lang.note}: ${business.data?.invoiceNote}',
textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerSizedBox(height: 16),
if (_qrLogo != null) ...[thermer.ThermerAlign(child: _qrLogo), thermer.ThermerSizedBox(height: 1)],
// if (business.data?.developByLink != null)
// thermer.ThermerAlign(child: thermer.ThermerQRCode(data: business.data?.developByLink ?? '', size: 120)),
if (business.data?.developBy != null)
thermer.ThermerText('${business.data?.developByLevel ?? _lang.developedBy} ${business.data?.developBy}',
textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerSizedBox(height: 200), // Cutter space
],
);
return _layout.toUint8List();
}
// --- Sub-Builders ---
List<thermer.ThermerWidget> _buildInfoSection(lang.S _lang) {
DateTime? saleDateTime;
if (saleInvoice.saleDate != null && saleInvoice.saleDate!.isNotEmpty) {
saleDateTime = DateTime.tryParse(saleInvoice.saleDate!);
}
final formattedDate = saleDateTime != null
? DateFormat('dd MMMM yyyy').format(saleDateTime) // 25 January 2026
: '';
final formattedTime = saleDateTime != null
? DateFormat('hh:mm a').format(saleDateTime).toLowerCase() // 12:55 pm
: '';
final invText = '${_lang.invoice}: ${saleInvoice.invoiceNumber ?? ''}';
final dateText = '${_lang.date}: ${formattedDate ?? ''}';
final timeText = "${_lang.time}: ${formattedTime ?? ''}";
final nameText = '${_lang.name}: ${saleInvoice.party?.name ?? 'Guest'}';
final mobileText = '${_lang.mobile} ${saleInvoice.party?.phone ?? 'N/A'}';
final salesByText =
"${_lang.salesBy} ${saleInvoice.user?.role == 'shop-owner' ? _lang.admin : saleInvoice.user?.role ?? "N/A"}";
if (is58mm) {
// 58mm: Vertical Stack (One below another)
return [
thermer.ThermerText(
invText,
style: _commonStyle(),
),
if (saleInvoice.saleDate != null) thermer.ThermerText(dateText, style: _commonStyle()),
thermer.ThermerText(nameText, style: _commonStyle()),
thermer.ThermerText(mobileText, style: _commonStyle()),
thermer.ThermerText(salesByText, style: _commonStyle()),
];
} else {
// 80mm: Two columns (Side by side)
return [
// Row 1: Invoice | Date
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(invText, style: _commonStyle()),
if (saleInvoice.saleDate != null) thermer.ThermerText(dateText, style: _commonStyle()),
],
),
// Row 2: Name | Time
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(nameText, style: _commonStyle()),
thermer.ThermerText(timeText, style: _commonStyle()),
],
),
// Row 3: Mobile | Sales By
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(mobileText, style: _commonStyle()),
thermer.ThermerText(salesByText, style: _commonStyle()),
],
),
];
}
}
List<thermer.ThermerTableRow> _buildProductRows() {
List<thermer.ThermerTableRow> rows = [];
if (saleInvoice.salesDetails == null) return rows;
for (var index = 0; index < saleInvoice.salesDetails!.length; index++) {
final item = saleInvoice.salesDetails![index];
final qty = _getProductQty(item.id ?? 0);
final price = item.price ?? 0;
final discount = item.discount ?? 0;
final amount = (price * qty) - (discount * qty);
// Main Row
rows.add(thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText((index + 1).toString(), style: _commonStyle()),
thermer.ThermerText(_getProductName(item.id ?? 0), style: _commonStyle()),
thermer.ThermerText(formatPointNumber(qty), textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerText('$price', textAlign: thermer.TextAlign.center, style: _commonStyle()),
thermer.ThermerText(formatPointNumber(amount), textAlign: thermer.TextAlign.end, style: _commonStyle()),
]));
// Warranty/Guarantee
final w = item.warrantyInfo;
if (w?.warrantyDuration != null) {
rows.add(_buildInfoRow("${lang.S.of(context).warranty} : ${w!.warrantyDuration} ${w.warrantyUnit}"));
}
if (w?.guaranteeDuration != null) {
rows.add(_buildInfoRow("${lang.S.of(context).guarantee} : ${w!.guaranteeDuration} ${w.guaranteeUnit}"));
}
}
return rows;
}
thermer.ThermerTableRow _buildInfoRow(String text) {
return thermer.ThermerTableRow([
if (!is58mm) thermer.ThermerText(""),
thermer.ThermerText(text, style: _commonStyle(fontSize: 20)),
thermer.ThermerText(""),
thermer.ThermerText(""),
thermer.ThermerText(""),
]);
}
thermer.ThermerColumn _buildCalculationColumn(lang.S _lang) {
thermer.ThermerRow calcRow(String label, num value, {bool bold = false, bool isCurrency = true}) {
return thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(label, style: _commonStyle(isBold: bold)),
thermer.ThermerText(
isCurrency ? formatPointNumber(value, addComma: true) : value.toString(),
textAlign: thermer.TextAlign.end,
style: _commonStyle(isBold: bold),
),
],
);
}
num subTotal = 0;
num totalDiscount = 0;
if (saleInvoice.salesDetails != null) {
for (var e in saleInvoice.salesDetails!) {
final q = _getProductQty(e.id ?? 0);
subTotal += ((e.price ?? 0) * q) - ((e.discount ?? 0) * q);
totalDiscount += (e.discount ?? 0) * q;
}
}
num returnDiscount = 0;
if (saleInvoice.salesReturns != null) {
for (var ret in saleInvoice.salesReturns!) {
for (var det in ret.salesReturnDetails ?? []) {
final price = saleInvoice.salesDetails
?.firstWhere((e) => e.id == det.saleDetailId, orElse: () => SalesDetails())
.price ??
0;
returnDiscount += ((price * (det.returnQty ?? 0)) - (det.returnAmount ?? 0));
}
}
}
return thermer.ThermerColumn(
children: [
calcRow('${_lang.subTotal}: ', subTotal),
calcRow('${_lang.discount}: ', (saleInvoice.discountAmount ?? 0) + returnDiscount + totalDiscount),
calcRow("${saleInvoice.vat?.name ?? _lang.vat}: ", saleInvoice.vatAmount ?? 0, isCurrency: false),
calcRow('${_lang.shippingCharge}:', saleInvoice.shippingCharge ?? 0, isCurrency: false),
if ((saleInvoice.roundingAmount ?? 0) != 0) ...[
calcRow('${_lang.total}:', saleInvoice.actualTotalAmount ?? 0),
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText('${_lang.rounding}:', style: _commonStyle()),
thermer.ThermerText(
"${!(saleInvoice.roundingAmount?.isNegative ?? true) ? '+' : ''}${formatPointNumber(saleInvoice.roundingAmount ?? 0)}",
textAlign: thermer.TextAlign.end,
style: _commonStyle(),
),
],
),
],
thermer.ThermerDivider.horizontal(),
calcRow('${_lang.totalPayable}: ', saleInvoice.totalAmount ?? 0, bold: true),
calcRow('${_lang.paidAmount}: ',
((saleInvoice.totalAmount ?? 0) - (saleInvoice.dueAmount ?? 0)) + (saleInvoice.changeAmount ?? 0)),
if ((saleInvoice.dueAmount ?? 0) > 0) calcRow('${_lang.dueAmount}: ', saleInvoice.dueAmount ?? 0),
if ((saleInvoice.changeAmount ?? 0) > 0) calcRow('${_lang.changeAmount}: ', saleInvoice.changeAmount ?? 0),
],
);
}
thermer.ThermerText _buildPaymentInfoText(lang.S _lang) {
List<String> labels = [];
if (saleInvoice.transactions != null) {
for (var item in saleInvoice.transactions!) {
String label = item.paymentType?.name ?? 'n/a';
if (item.transactionType == 'cash_payment') label = _lang.cash;
if (item.transactionType == 'cheque_payment') label = _lang.cheque;
if (item.transactionType == 'wallet_payment') label = _lang.wallet;
labels.add(label);
}
}
return thermer.ThermerText(
"${_lang.paidVia} : ${labels.join(', ')}",
style: _commonStyle(),
textAlign: is58mm ? thermer.TextAlign.center : thermer.TextAlign.start,
);
}
List<thermer.ThermerWidget> _buildReturnSection(BuildContext context) {
final _lang = lang.S.of(context);
if (saleInvoice.salesReturns?.isEmpty ?? true) return [];
List<thermer.ThermerWidget> widgets = [];
List<String> processedDates = [];
num totalReturnedAmount = 0;
for (var ret in saleInvoice.salesReturns!) {
final dateStr = ret.returnDate?.substring(0, 10);
if (dateStr != null && !processedDates.contains(dateStr)) {
processedDates.add(dateStr);
widgets.add(thermer.ThermerDivider.horizontal());
widgets.add(thermer.ThermerText('${_lang.retur}-$dateStr', style: _commonStyle(isBold: true)));
}
widgets.add(thermer.ThermerTable(
header: thermer.ThermerTableRow([
thermer.ThermerText(_lang.item, style: _commonStyle(fontSize: 22, isBold: true)),
thermer.ThermerText(_lang.qty,
textAlign: thermer.TextAlign.center, style: _commonStyle(fontSize: 22, isBold: true)),
thermer.ThermerText(_lang.total,
textAlign: thermer.TextAlign.end, style: _commonStyle(fontSize: 22, isBold: true)),
]),
data: (ret.salesReturnDetails ?? []).map((d) {
totalReturnedAmount += d.returnAmount ?? 0;
return thermer.ThermerTableRow([
thermer.ThermerText(_getProductName(d.saleDetailId ?? 0), style: _commonStyle(fontSize: 22)),
thermer.ThermerText('${d.returnQty ?? 0}',
textAlign: thermer.TextAlign.center, style: _commonStyle(fontSize: 22)),
thermer.ThermerText('${d.returnAmount ?? 0}',
textAlign: thermer.TextAlign.end, style: _commonStyle(fontSize: 22)),
]);
}).toList(),
cellWidths: {0: null, 1: 0.2, 2: 0.25},
));
}
widgets.add(thermer.ThermerDivider.horizontal());
widgets.add(
thermer.ThermerRow(
mainAxisAlignment: thermer.ThermerMainAxisAlignment.spaceBetween,
children: [
thermer.ThermerText(_lang.returnAmount, style: _commonStyle()),
thermer.ThermerText(formatPointNumber(totalReturnedAmount),
textAlign: thermer.TextAlign.end, style: _commonStyle()),
],
),
);
widgets.add(thermer.ThermerSizedBox(height: 10));
return widgets;
}
}

View File

@@ -0,0 +1,69 @@
// import 'dart:typed_data' show Uint8List;
// import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_riverpod/flutter_riverpod.dart' as riverpod;
// import 'package:image/image.dart' as img;
// import 'package:intl/intl.dart';
// import 'package:mobile_pos/generated/l10n.dart' as lang;
// import 'package:mobile_pos/generated/l10n.dart';
// import 'package:mobile_pos/model/business_info_model.dart';
// import 'package:mobile_pos/model/sale_transaction_model.dart';
// import '../../thermer/thermer.dart' as thermer;
// // part '_purchase_invoice_template.dart';
// part '_sale_invoice_template.dart';
// // part '_kot_ticket_template.dart';
// // part '_due_collection_invoice_template.dart';
// abstract class ThermalInvoiceTemplateBase {
// ThermalInvoiceTemplateBase(this.ref);
// final riverpod.Ref ref;
// thermer.TextDirection get textDirection {
// final _rtlLang = ['ar', 'ar-bh', 'eg-ar', 'fa', 'prs', 'ps', 'ur'];
// // if (_rtlLang.contains(ref.read(GlobalContextHolder.localeProvider).languageCode)) {
// // return thermer.TextDirection.rtl;
// // }
// return thermer.TextDirection.ltr;
// }
// Future<List<int>> get template;
// // Future<img.Image?> getNetworkImage(
// // String? url, {
// // int width = 100,
// // int height = 100,
// // }) async {
// // if (url == null) return null;
// // try {
// // final _response = await dio.Dio().get<List<int>>(
// // url,
// // options: dio.Options(responseType: dio.ResponseType.bytes),
// // );
// // final _image = img.decodeImage(Uint8List.fromList(_response.data!));
// // if (_image == null) return null;
// // return img.copyResize(
// // _image,
// // width: width,
// // height: height,
// // interpolation: img.Interpolation.average,
// // );
// // } catch (e) {
// // return null;
// // }
// // }
// }
// // extension ThermalPrinterPaperSizeExt {
// // PaperSize get escPosSize {
// // return switch (this) {
// // ThermalPrinterPaperSize.mm582Inch => PaperSize.mm58,
// // ThermalPrinterPaperSize.mm803Inch => PaperSize.mm80,
// // };
// // }
// // }