From 34ee0c3c014cf18bc0e03011ed02cbd2318ea666 Mon Sep 17 00:00:00 2001 From: eko54r Date: Mon, 9 Feb 2026 23:56:18 +0700 Subject: [PATCH] update struk --- .../sales_invoice_details_screen.dart | 3 + lib/model/sale_transaction_model.dart | 14 +++ .../print_thermal_invoice_provider.dart | 9 +- .../thermal_invoice_sales.dart | 101 +++++++++++------- 4 files changed, 85 insertions(+), 42 deletions(-) diff --git a/lib/Screens/invoice_details/sales_invoice_details_screen.dart b/lib/Screens/invoice_details/sales_invoice_details_screen.dart index 47e3411..09e5c9e 100644 --- a/lib/Screens/invoice_details/sales_invoice_details_screen.dart +++ b/lib/Screens/invoice_details/sales_invoice_details_screen.dart @@ -25,6 +25,7 @@ import '../../widgets/dotted_border/global_dotted_border.dart'; import '../../widgets/universal_image.dart'; import '../Products/add product/modle/create_product_model.dart'; import '../language/language_provider.dart'; +import '../../Provider/product_provider.dart'; class SalesInvoiceDetails extends StatefulWidget { const SalesInvoiceDetails({ @@ -134,6 +135,7 @@ class _SalesInvoiceDetailsState extends State { final locale = Localizations.localeOf(context).languageCode; return Consumer(builder: (context, ref, __) { final printerData = ref.watch(thermalPrinterProvider); + final products = ref.watch(productProvider); final businessSettingData = ref.watch(businessInfoProvider); final hasWarranty = widget.saleTransaction.salesDetails!.any((e) => e.warrantyInfo?.warrantyDuration != null); final hasGuarantee = widget.saleTransaction.salesDetails!.any((e) => e.warrantyInfo?.guaranteeDuration != null); @@ -1316,6 +1318,7 @@ class _SalesInvoiceDetailsState extends State { transaction: model, productList: model.transitionModel!.salesDetails, context: context, + products: products.value, ); // final defould = true; diff --git a/lib/model/sale_transaction_model.dart b/lib/model/sale_transaction_model.dart index 2f90339..5d38636 100644 --- a/lib/model/sale_transaction_model.dart +++ b/lib/model/sale_transaction_model.dart @@ -260,6 +260,7 @@ class SalesProduct { this.productPurchasePrice, this.productCode, this.productType, + this.vat, }); SalesProduct.fromJson(dynamic json) { @@ -270,6 +271,7 @@ class SalesProduct { productType = json['product_type']; productPurchasePrice = json['productPurchasePrice']; category = json['category'] != null ? Category.fromJson(json['category']) : null; + vat = json['vat'] != null ? SalesVat.fromJson(json['vat']) : null; } num? id; @@ -279,6 +281,7 @@ class SalesProduct { num? productPurchasePrice; String? productType; Category? category; + SalesVat? vat; Map toJson() { final map = {}; @@ -288,6 +291,9 @@ class SalesProduct { if (category != null) { map['category'] = category?.toJson(); } + if (vat != null) { + map['vat'] = vat?.toJson(); + } return map; } } @@ -507,6 +513,14 @@ class SalesVat { num? id; String? name; num? rate; + + Map toJson() { + final map = {}; + map['id'] = id; + map['name'] = name; + map['rate'] = rate; + return map; + } } class Branch { diff --git a/lib/thermal priting invoices/provider/print_thermal_invoice_provider.dart b/lib/thermal priting invoices/provider/print_thermal_invoice_provider.dart index e509e25..70e6f0a 100644 --- a/lib/thermal priting invoices/provider/print_thermal_invoice_provider.dart +++ b/lib/thermal priting invoices/provider/print_thermal_invoice_provider.dart @@ -129,10 +129,15 @@ class ThermalPrinter extends ChangeNotifier { ); } - Future printSalesThermalInvoiceNow({required PrintSalesTransactionModel transaction, required List? productList, required BuildContext context}) async { + Future printSalesThermalInvoiceNow( + {required PrintSalesTransactionModel transaction, + required List? productList, + required BuildContext context, + List? products}) async { await getBluetooth(); isBluetoothConnected - ? SalesThermalPrinterInvoice().printSalesTicket(printTransactionModel: transaction, productList: productList, context: context) + ? SalesThermalPrinterInvoice().printSalesTicket( + printTransactionModel: transaction, productList: productList, context: context, products: products) : listOfBluDialog(context: context); } diff --git a/lib/thermal priting invoices/thermal_invoice_sales.dart b/lib/thermal priting invoices/thermal_invoice_sales.dart index eadea46..f2a5cfc 100644 --- a/lib/thermal priting invoices/thermal_invoice_sales.dart +++ b/lib/thermal priting invoices/thermal_invoice_sales.dart @@ -10,6 +10,7 @@ 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_model.dart'; import '../Screens/Products/add product/modle/create_product_model.dart'; import '../constant.dart'; import '../model/sale_transaction_model.dart'; @@ -23,6 +24,7 @@ class SalesThermalPrinterInvoice { Future printSalesTicket( {required PrintSalesTransactionModel printTransactionModel, required List? productList, + List? products, required BuildContext context}) async { bool? isConnected = await PrintBluetoothThermal.connectionStatus; bool defould = (printTransactionModel.personalInformationModel.data?.invoiceLanguage == 'english' || @@ -36,8 +38,10 @@ class SalesThermalPrinterInvoice { if (defould) { bytes = is80mm - ? await getSalesTicket80mm(printTransactionModel: printTransactionModel, productList: productList) - : await getSalesTicket58mm(printTransactionModel: printTransactionModel, productList: productList); + ? await getSalesTicket80mm( + printTransactionModel: printTransactionModel, productList: productList, products: products) + : await getSalesTicket58mm( + printTransactionModel: printTransactionModel, productList: productList, products: products); } else { final bool isRTL = rtlLang.contains(await getLanguageName()); @@ -62,7 +66,9 @@ class SalesThermalPrinterInvoice { } Future> getSalesTicket58mm( - {required PrintSalesTransactionModel printTransactionModel, required List? productList}) async { + {required PrintSalesTransactionModel printTransactionModel, + required List? productList, + List? products}) async { List returnedDates = []; String productName({required num detailsId}) { final details = productList?[productList.indexWhere((element) => element.id == detailsId)]; @@ -258,11 +264,11 @@ class SalesThermalPrinterInvoice { } bytes += generator.row([ - PosColumn(text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)), - PosColumn(text: 'Product', width: 4, styles: const PosStyles(align: PosAlign.left, bold: true)), - PosColumn(text: 'Qty', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)), - PosColumn(text: 'VAT/Tax', width: 2, styles: const PosStyles(align: PosAlign.center, bold: true)), - PosColumn(text: 'Amount', width: 4, styles: const PosStyles(align: PosAlign.right, bold: true)), + PosColumn(text: 'Item', width: 4, styles: const PosStyles(align: PosAlign.left, bold: true)), + PosColumn(text: 'Qt', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)), + PosColumn(text: 'Price', width: 3, styles: const PosStyles(align: PosAlign.center, bold: true)), + PosColumn(text: '%', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)), + PosColumn(text: 'Amount', width: 3, styles: const PosStyles(align: PosAlign.right, bold: true)), ]); bytes += generator.hr(); List.generate(productList?.length ?? 1, (index) { @@ -279,14 +285,16 @@ class SalesThermalPrinterInvoice { final name = "${productList?[index].product?.productName ?? ''}" "${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}"; + final double qty = getProductQuantity(detailsId: productList?[index].id ?? 0).toDouble(); + final double price = num.tryParse(productList?[index].price.toString() ?? '0')?.toDouble() ?? 0; + final double discount = num.tryParse(productList?[index].discount.toString() ?? '0')?.toDouble() ?? 0; + final product = products?.firstWhere((element) => element.id == productList?[index].productId, + orElse: () => Product()); + final double vatPercent = num.tryParse(product?.vat?.rate.toString() ?? '0')?.toDouble() ?? 0; + final double amountExVat = (price * qty) - (discount * qty); + final double vatAmount = (amountExVat * vatPercent) / 100; + bytes += generator.row([ - PosColumn( - text: '${index + 1}', - width: 1, - styles: const PosStyles( - align: PosAlign.left, - ), - ), PosColumn( text: name, width: 4, @@ -295,19 +303,23 @@ class SalesThermalPrinterInvoice { ), ), PosColumn( - text: formatPointNumber(getProductQuantity(detailsId: productList?[index].id ?? 0)), + text: formatPointNumber(qty), width: 1, styles: const PosStyles(align: PosAlign.center), ), PosColumn( - text: formatPointNumber(0), - width: 2, + text: formatPointNumber(price), + width: 3, styles: const PosStyles(align: PosAlign.center), ), PosColumn( - text: - "${((productList?[index].price ?? 0) * getProductQuantity(detailsId: productList?[index].id ?? 0)) - ((productList?[index].discount ?? 0) * getProductQuantity(detailsId: productList?[index].id ?? 0))}", - width: 4, + text: "${vatPercent % 1 == 0 ? vatPercent.toInt() : vatPercent}", + width: 1, + styles: const PosStyles(align: PosAlign.center), + ), + PosColumn( + text: formatPointNumber(amountExVat), + width: 3, styles: const PosStyles(align: PosAlign.right), ), ]); @@ -602,7 +614,9 @@ class SalesThermalPrinterInvoice { } Future> getSalesTicket80mm( - {required PrintSalesTransactionModel printTransactionModel, required List? productList}) async { + {required PrintSalesTransactionModel printTransactionModel, + required List? productList, + List? products}) async { List returnedDates = []; String productName({required num detailsId}) { final details = productList?[productList.indexWhere((element) => element.id == detailsId)]; @@ -816,13 +830,12 @@ class SalesThermalPrinterInvoice { bytes += generator.emptyLines(1); ///____________Products_Section_________________________________ - bytes += generator.hr(); bytes += generator.row([ - PosColumn(text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)), - PosColumn(text: 'Product', width: 5, styles: const PosStyles(align: PosAlign.left, bold: true)), - PosColumn(text: 'QTY', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)), - PosColumn(text: 'VAT/Tax', width: 2, styles: const PosStyles(align: PosAlign.center, bold: true)), - PosColumn(text: 'Amount', width: 3, styles: const PosStyles(align: PosAlign.right, bold: true)), + PosColumn(text: 'Item', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)), + PosColumn(text: 'Qt', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)), + PosColumn(text: 'Price', width: 2, styles: const PosStyles(align: PosAlign.center, bold: true)), + PosColumn(text: '%', width: 2, styles: const PosStyles(align: PosAlign.center, bold: true)), + PosColumn(text: 'Amount(Ex Vat)', width: 3, styles: const PosStyles(align: PosAlign.right, bold: true)), ]); bytes += generator.hr(); List.generate(productList?.length ?? 1, (index) { @@ -838,34 +851,42 @@ class SalesThermalPrinterInvoice { final name = "${productList?[index].product?.productName ?? ''}" "${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}"; + + final double qty = getProductQuantity(detailsId: productList?[index].id ?? 0).toDouble(); + final double price = num.tryParse(productList?[index].price.toString() ?? '0')?.toDouble() ?? 0; + final double discount = num.tryParse(productList?[index].discount.toString() ?? '0')?.toDouble() ?? 0; + final product = products?.firstWhere((element) => element.id == productList?[index].productId, + orElse: () => Product()); + final double vatPercent = num.tryParse(product?.vat?.rate.toString() ?? '0')?.toDouble() ?? 0; + final double amountExVat = (price * qty) - (discount * qty); + final double vatAmount = (amountExVat * vatPercent) / 100; + bytes += generator.row([ - PosColumn( - text: '${index + 1}', - width: 1, - styles: const PosStyles( - align: PosAlign.left, - )), PosColumn( text: "${productList?[index].product?.productName ?? ''}${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}", - width: 5, + width: 1, styles: const PosStyles( align: PosAlign.left, )), PosColumn( - text: formatPointNumber(getProductQuantity(detailsId: productList?[index].id ?? 0), addComma: true), + text: formatPointNumber(qty, addComma: true), width: 1, styles: const PosStyles(align: PosAlign.center)), PosColumn( - text: formatPointNumber(0, addComma: true), + text: formatPointNumber(price, addComma: true), width: 2, styles: const PosStyles( align: PosAlign.center, )), PosColumn( - text: formatPointNumber( - (productList?[index].price ?? 0) * getProductQuantity(detailsId: productList?[index].id ?? 0), - addComma: true), + text: "${vatPercent % 1 == 0 ? vatPercent.toInt() : vatPercent}", + width: 2, + styles: const PosStyles( + align: PosAlign.center, + )), + PosColumn( + text: formatPointNumber(amountExVat, addComma: true), width: 3, styles: const PosStyles(align: PosAlign.right)), ]);