update struk

This commit is contained in:
2026-02-09 23:56:18 +07:00
parent d144d24596
commit 34ee0c3c01
4 changed files with 85 additions and 42 deletions

View File

@@ -25,6 +25,7 @@ import '../../widgets/dotted_border/global_dotted_border.dart';
import '../../widgets/universal_image.dart'; import '../../widgets/universal_image.dart';
import '../Products/add product/modle/create_product_model.dart'; import '../Products/add product/modle/create_product_model.dart';
import '../language/language_provider.dart'; import '../language/language_provider.dart';
import '../../Provider/product_provider.dart';
class SalesInvoiceDetails extends StatefulWidget { class SalesInvoiceDetails extends StatefulWidget {
const SalesInvoiceDetails({ const SalesInvoiceDetails({
@@ -134,6 +135,7 @@ class _SalesInvoiceDetailsState extends State<SalesInvoiceDetails> {
final locale = Localizations.localeOf(context).languageCode; final locale = Localizations.localeOf(context).languageCode;
return Consumer(builder: (context, ref, __) { return Consumer(builder: (context, ref, __) {
final printerData = ref.watch(thermalPrinterProvider); final printerData = ref.watch(thermalPrinterProvider);
final products = ref.watch(productProvider);
final businessSettingData = ref.watch(businessInfoProvider); final businessSettingData = ref.watch(businessInfoProvider);
final hasWarranty = widget.saleTransaction.salesDetails!.any((e) => e.warrantyInfo?.warrantyDuration != null); final hasWarranty = widget.saleTransaction.salesDetails!.any((e) => e.warrantyInfo?.warrantyDuration != null);
final hasGuarantee = widget.saleTransaction.salesDetails!.any((e) => e.warrantyInfo?.guaranteeDuration != null); final hasGuarantee = widget.saleTransaction.salesDetails!.any((e) => e.warrantyInfo?.guaranteeDuration != null);
@@ -1316,6 +1318,7 @@ class _SalesInvoiceDetailsState extends State<SalesInvoiceDetails> {
transaction: model, transaction: model,
productList: model.transitionModel!.salesDetails, productList: model.transitionModel!.salesDetails,
context: context, context: context,
products: products.value,
); );
// final defould = true; // final defould = true;

View File

@@ -260,6 +260,7 @@ class SalesProduct {
this.productPurchasePrice, this.productPurchasePrice,
this.productCode, this.productCode,
this.productType, this.productType,
this.vat,
}); });
SalesProduct.fromJson(dynamic json) { SalesProduct.fromJson(dynamic json) {
@@ -270,6 +271,7 @@ class SalesProduct {
productType = json['product_type']; productType = json['product_type'];
productPurchasePrice = json['productPurchasePrice']; productPurchasePrice = json['productPurchasePrice'];
category = json['category'] != null ? Category.fromJson(json['category']) : null; category = json['category'] != null ? Category.fromJson(json['category']) : null;
vat = json['vat'] != null ? SalesVat.fromJson(json['vat']) : null;
} }
num? id; num? id;
@@ -279,6 +281,7 @@ class SalesProduct {
num? productPurchasePrice; num? productPurchasePrice;
String? productType; String? productType;
Category? category; Category? category;
SalesVat? vat;
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final map = <String, dynamic>{}; final map = <String, dynamic>{};
@@ -288,6 +291,9 @@ class SalesProduct {
if (category != null) { if (category != null) {
map['category'] = category?.toJson(); map['category'] = category?.toJson();
} }
if (vat != null) {
map['vat'] = vat?.toJson();
}
return map; return map;
} }
} }
@@ -507,6 +513,14 @@ class SalesVat {
num? id; num? id;
String? name; String? name;
num? rate; num? rate;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['name'] = name;
map['rate'] = rate;
return map;
}
} }
class Branch { class Branch {

View File

@@ -129,10 +129,15 @@ class ThermalPrinter extends ChangeNotifier {
); );
} }
Future<void> printSalesThermalInvoiceNow({required PrintSalesTransactionModel transaction, required List<SalesDetails>? productList, required BuildContext context}) async { Future<void> printSalesThermalInvoiceNow(
{required PrintSalesTransactionModel transaction,
required List<SalesDetails>? productList,
required BuildContext context,
List<Product>? products}) async {
await getBluetooth(); await getBluetooth();
isBluetoothConnected isBluetoothConnected
? SalesThermalPrinterInvoice().printSalesTicket(printTransactionModel: transaction, productList: productList, context: context) ? SalesThermalPrinterInvoice().printSalesTicket(
printTransactionModel: transaction, productList: productList, context: context, products: products)
: listOfBluDialog(context: context); : listOfBluDialog(context: context);
} }

View File

@@ -10,6 +10,7 @@ import 'package:nb_utils/nb_utils.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart'; import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import '../Const/api_config.dart'; import '../Const/api_config.dart';
import '../Screens/Products/Model/product_model.dart';
import '../Screens/Products/add product/modle/create_product_model.dart'; import '../Screens/Products/add product/modle/create_product_model.dart';
import '../constant.dart'; import '../constant.dart';
import '../model/sale_transaction_model.dart'; import '../model/sale_transaction_model.dart';
@@ -23,6 +24,7 @@ class SalesThermalPrinterInvoice {
Future<void> printSalesTicket( Future<void> printSalesTicket(
{required PrintSalesTransactionModel printTransactionModel, {required PrintSalesTransactionModel printTransactionModel,
required List<SalesDetails>? productList, required List<SalesDetails>? productList,
List<Product>? products,
required BuildContext context}) async { required BuildContext context}) async {
bool? isConnected = await PrintBluetoothThermal.connectionStatus; bool? isConnected = await PrintBluetoothThermal.connectionStatus;
bool defould = (printTransactionModel.personalInformationModel.data?.invoiceLanguage == 'english' || bool defould = (printTransactionModel.personalInformationModel.data?.invoiceLanguage == 'english' ||
@@ -36,8 +38,10 @@ class SalesThermalPrinterInvoice {
if (defould) { if (defould) {
bytes = is80mm bytes = is80mm
? await getSalesTicket80mm(printTransactionModel: printTransactionModel, productList: productList) ? await getSalesTicket80mm(
: await getSalesTicket58mm(printTransactionModel: printTransactionModel, productList: productList); printTransactionModel: printTransactionModel, productList: productList, products: products)
: await getSalesTicket58mm(
printTransactionModel: printTransactionModel, productList: productList, products: products);
} else { } else {
final bool isRTL = rtlLang.contains(await getLanguageName()); final bool isRTL = rtlLang.contains(await getLanguageName());
@@ -62,7 +66,9 @@ class SalesThermalPrinterInvoice {
} }
Future<List<int>> getSalesTicket58mm( Future<List<int>> getSalesTicket58mm(
{required PrintSalesTransactionModel printTransactionModel, required List<SalesDetails>? productList}) async { {required PrintSalesTransactionModel printTransactionModel,
required List<SalesDetails>? productList,
List<Product>? products}) async {
List<DateTime> returnedDates = []; List<DateTime> returnedDates = [];
String productName({required num detailsId}) { String productName({required num detailsId}) {
final details = productList?[productList.indexWhere((element) => element.id == detailsId)]; final details = productList?[productList.indexWhere((element) => element.id == detailsId)];
@@ -258,11 +264,11 @@ class SalesThermalPrinterInvoice {
} }
bytes += generator.row([ bytes += generator.row([
PosColumn(text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)), PosColumn(text: 'Item', width: 4, styles: const PosStyles(align: PosAlign.left, bold: true)),
PosColumn(text: 'Product', 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: 'Qty', 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: 'VAT/Tax', width: 2, styles: const PosStyles(align: PosAlign.center, bold: true)), PosColumn(text: '%', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)),
PosColumn(text: 'Amount', width: 4, styles: const PosStyles(align: PosAlign.right, bold: true)), PosColumn(text: 'Amount', width: 3, styles: const PosStyles(align: PosAlign.right, bold: true)),
]); ]);
bytes += generator.hr(); bytes += generator.hr();
List.generate(productList?.length ?? 1, (index) { List.generate(productList?.length ?? 1, (index) {
@@ -279,14 +285,16 @@ class SalesThermalPrinterInvoice {
final name = "${productList?[index].product?.productName ?? ''}" final name = "${productList?[index].product?.productName ?? ''}"
"${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}"; "${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([ bytes += generator.row([
PosColumn(
text: '${index + 1}',
width: 1,
styles: const PosStyles(
align: PosAlign.left,
),
),
PosColumn( PosColumn(
text: name, text: name,
width: 4, width: 4,
@@ -295,19 +303,23 @@ class SalesThermalPrinterInvoice {
), ),
), ),
PosColumn( PosColumn(
text: formatPointNumber(getProductQuantity(detailsId: productList?[index].id ?? 0)), text: formatPointNumber(qty),
width: 1, width: 1,
styles: const PosStyles(align: PosAlign.center), styles: const PosStyles(align: PosAlign.center),
), ),
PosColumn( PosColumn(
text: formatPointNumber(0), text: formatPointNumber(price),
width: 2, width: 3,
styles: const PosStyles(align: PosAlign.center), styles: const PosStyles(align: PosAlign.center),
), ),
PosColumn( PosColumn(
text: text: "${vatPercent % 1 == 0 ? vatPercent.toInt() : vatPercent}",
"${((productList?[index].price ?? 0) * getProductQuantity(detailsId: productList?[index].id ?? 0)) - ((productList?[index].discount ?? 0) * getProductQuantity(detailsId: productList?[index].id ?? 0))}", width: 1,
width: 4, styles: const PosStyles(align: PosAlign.center),
),
PosColumn(
text: formatPointNumber(amountExVat),
width: 3,
styles: const PosStyles(align: PosAlign.right), styles: const PosStyles(align: PosAlign.right),
), ),
]); ]);
@@ -602,7 +614,9 @@ class SalesThermalPrinterInvoice {
} }
Future<List<int>> getSalesTicket80mm( Future<List<int>> getSalesTicket80mm(
{required PrintSalesTransactionModel printTransactionModel, required List<SalesDetails>? productList}) async { {required PrintSalesTransactionModel printTransactionModel,
required List<SalesDetails>? productList,
List<Product>? products}) async {
List<DateTime> returnedDates = []; List<DateTime> returnedDates = [];
String productName({required num detailsId}) { String productName({required num detailsId}) {
final details = productList?[productList.indexWhere((element) => element.id == detailsId)]; final details = productList?[productList.indexWhere((element) => element.id == detailsId)];
@@ -816,13 +830,12 @@ class SalesThermalPrinterInvoice {
bytes += generator.emptyLines(1); bytes += generator.emptyLines(1);
///____________Products_Section_________________________________ ///____________Products_Section_________________________________
bytes += generator.hr();
bytes += generator.row([ bytes += generator.row([
PosColumn(text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)), PosColumn(text: 'Item', 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: 'Qt', width: 1, styles: const PosStyles(align: PosAlign.center, bold: true)),
PosColumn(text: 'QTY', 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: 'VAT/Tax', 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', width: 3, styles: const PosStyles(align: PosAlign.right, bold: true)), PosColumn(text: 'Amount(Ex Vat)', width: 3, styles: const PosStyles(align: PosAlign.right, bold: true)),
]); ]);
bytes += generator.hr(); bytes += generator.hr();
List.generate(productList?.length ?? 1, (index) { List.generate(productList?.length ?? 1, (index) {
@@ -838,34 +851,42 @@ class SalesThermalPrinterInvoice {
final name = "${productList?[index].product?.productName ?? ''}" final name = "${productList?[index].product?.productName ?? ''}"
"${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}"; "${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([ bytes += generator.row([
PosColumn(
text: '${index + 1}',
width: 1,
styles: const PosStyles(
align: PosAlign.left,
)),
PosColumn( PosColumn(
text: text:
"${productList?[index].product?.productName ?? ''}${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}", "${productList?[index].product?.productName ?? ''}${productList?[index].product?.productType == ProductType.variant.name ? ' [${productList?[index].stock?.batchNo ?? ''}]' : ''}",
width: 5, width: 1,
styles: const PosStyles( styles: const PosStyles(
align: PosAlign.left, align: PosAlign.left,
)), )),
PosColumn( PosColumn(
text: formatPointNumber(getProductQuantity(detailsId: productList?[index].id ?? 0), addComma: true), text: formatPointNumber(qty, addComma: true),
width: 1, width: 1,
styles: const PosStyles(align: PosAlign.center)), styles: const PosStyles(align: PosAlign.center)),
PosColumn( PosColumn(
text: formatPointNumber(0, addComma: true), text: formatPointNumber(price, addComma: true),
width: 2, width: 2,
styles: const PosStyles( styles: const PosStyles(
align: PosAlign.center, align: PosAlign.center,
)), )),
PosColumn( PosColumn(
text: formatPointNumber( text: "${vatPercent % 1 == 0 ? vatPercent.toInt() : vatPercent}",
(productList?[index].price ?? 0) * getProductQuantity(detailsId: productList?[index].id ?? 0), width: 2,
addComma: true), styles: const PosStyles(
align: PosAlign.center,
)),
PosColumn(
text: formatPointNumber(amountExVat, addComma: true),
width: 3, width: 3,
styles: const PosStyles(align: PosAlign.right)), styles: const PosStyles(align: PosAlign.right)),
]); ]);