first commit
This commit is contained in:
1008
lib/PDF Invoice/due_invoice_pdf.dart
Normal file
1008
lib/PDF Invoice/due_invoice_pdf.dart
Normal file
File diff suppressed because it is too large
Load Diff
221
lib/PDF Invoice/pdf_common_functions.dart
Normal file
221
lib/PDF Invoice/pdf_common_functions.dart
Normal file
@@ -0,0 +1,221 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/model/sale_transaction_model.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../Screens/PDF/pdf.dart';
|
||||
import '../http_client/customer_http_client_get.dart';
|
||||
|
||||
class PDFCommonFunctions {
|
||||
//-------------------image
|
||||
Future<dynamic> getNetworkImage(String imageURL) async {
|
||||
if (imageURL.isEmpty) return null;
|
||||
try {
|
||||
final Uri uri = Uri.parse(imageURL);
|
||||
final String fileExtension = uri.path.split('.').last.toLowerCase();
|
||||
if (fileExtension == 'png' || fileExtension == 'jpg' || fileExtension == 'jpeg') {
|
||||
final List<int> responseBytes = await http.readBytes(uri);
|
||||
return Uint8List.fromList(responseBytes);
|
||||
} else if (fileExtension == 'svg') {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final response = await clientGet.get(url: uri);
|
||||
return response.body;
|
||||
} else {
|
||||
print('Unsupported image type: $fileExtension');
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading image: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<Uint8List?> loadAssetImage(String path) async {
|
||||
try {
|
||||
final ByteData data = await rootBundle.load(path);
|
||||
return data.buffer.asUint8List();
|
||||
} catch (e) {
|
||||
print('Error loading local image: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int serialNumber = 1; // Initialize serial number
|
||||
num getProductQuantity({required num detailsId, required SalesTransactionModel transactions}) {
|
||||
num totalQuantity = transactions.salesDetails?.where((element) => element.id == detailsId).first.quantities ?? 0;
|
||||
if (transactions.salesReturns?.isNotEmpty ?? false) {
|
||||
for (var returns in transactions.salesReturns!) {
|
||||
if (returns.salesReturnDetails?.isNotEmpty ?? false) {
|
||||
for (var details in returns.salesReturnDetails!) {
|
||||
if (details.saleDetailId == detailsId) {
|
||||
totalQuantity += details.returnQty ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalQuantity;
|
||||
}
|
||||
|
||||
static Future<void> savePdfAndShowPdf(
|
||||
{required BuildContext context, required String shopName, required String invoice, required pw.Document doc, bool? isShare, bool? download}) async {
|
||||
if (Platform.isIOS) {
|
||||
// EasyLoading.show(status: 'Generating PDF');
|
||||
if (download ?? false) {
|
||||
EasyLoading.show(status: 'Downloading...');
|
||||
} else {
|
||||
EasyLoading.show(status: 'Generating PDF');
|
||||
}
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final file = File('${dir.path}/${'$appsName-$shopName-$invoice'}.pdf');
|
||||
|
||||
final byteData = await doc.save();
|
||||
try {
|
||||
await file.writeAsBytes(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
|
||||
EasyLoading.showSuccess('Done');
|
||||
if (isShare ?? false) {
|
||||
await SharePlus.instance.share(ShareParams(
|
||||
files: [XFile(file.path)],
|
||||
text: 'Here is your invoice PDF: ',
|
||||
));
|
||||
} else if (download ?? false) {
|
||||
EasyLoading.showSuccess('Download successful! Check your Downloads folder');
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PDFViewerPage(path: file.path),
|
||||
),
|
||||
);
|
||||
}
|
||||
} on FileSystemException catch (err) {
|
||||
EasyLoading.showError(err.message);
|
||||
// handle error
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
var status = await Permission.storage.status;
|
||||
if (status != PermissionStatus.granted) {
|
||||
status = await Permission.storage.request();
|
||||
}
|
||||
if (true) {
|
||||
if (download ?? false) {
|
||||
EasyLoading.show(status: 'Downloading...');
|
||||
} else {
|
||||
EasyLoading.show(status: 'Generating PDF');
|
||||
}
|
||||
const downloadsFolderPath = '/storage/emulated/0/Download/';
|
||||
Directory dir = Directory(downloadsFolderPath);
|
||||
var file = File('${dir.path}/${'$appsName-$shopName-$invoice'}.pdf');
|
||||
for (var i = 1; i < 20; i++) {
|
||||
if (await file.exists()) {
|
||||
try {
|
||||
await file.delete();
|
||||
break;
|
||||
} catch (e) {
|
||||
if (e.toString().contains('Cannot delete file')) {
|
||||
file = File('${file.path.replaceAll(RegExp(r'\(\d+\)?'), '').replaceAll('.pdf', '')}($i).pdf');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final byteData = await doc.save();
|
||||
|
||||
await file.writeAsBytes(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
|
||||
|
||||
EasyLoading.dismiss();
|
||||
|
||||
if (isShare ?? false) {
|
||||
await SharePlus.instance.share(ShareParams(files: [XFile(file.path)], text: 'Here is your invoice PDF: '));
|
||||
} else if (download ?? false) {
|
||||
EasyLoading.showSuccess('Download successful! Check your Downloads folder');
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PDFViewerPage(path: file.path),
|
||||
),
|
||||
);
|
||||
}
|
||||
} on FileSystemException catch (err) {
|
||||
EasyLoading.showError(err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String numberToWords(num amount) {
|
||||
int taka = amount.floor();
|
||||
int paisa = ((amount - taka) * 100).round();
|
||||
|
||||
String takaWords = _convertNumberToWords(taka);
|
||||
String paisaWords = paisa > 0 ? ' and ${_convertNumberToWords(paisa)} Cents' : '';
|
||||
|
||||
return '$takaWords $paisaWords Only';
|
||||
}
|
||||
|
||||
String _convertNumberToWords(int number) {
|
||||
if (number == 0) return 'Zero';
|
||||
|
||||
final units = [
|
||||
'',
|
||||
'One',
|
||||
'Two',
|
||||
'Three',
|
||||
'Four',
|
||||
'Five',
|
||||
'Six',
|
||||
'Seven',
|
||||
'Eight',
|
||||
'Nine',
|
||||
'Ten',
|
||||
'Eleven',
|
||||
'Twelve',
|
||||
'Thirteen',
|
||||
'Fourteen',
|
||||
'Fifteen',
|
||||
'Sixteen',
|
||||
'Seventeen',
|
||||
'Eighteen',
|
||||
'Nineteen'
|
||||
];
|
||||
|
||||
final tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety'];
|
||||
|
||||
String convert(int n) {
|
||||
if (n < 20) return units[n];
|
||||
if (n < 100) {
|
||||
return tens[n ~/ 10] + (n % 10 != 0 ? ' ' + units[n % 10] : '');
|
||||
}
|
||||
if (n < 1000) {
|
||||
return units[n ~/ 100] + ' Hundred' + (n % 100 != 0 ? ' ' + convert(n % 100) : '');
|
||||
}
|
||||
if (n < 100000) {
|
||||
return convert(n ~/ 1000) + ' Thousand' + (n % 1000 != 0 ? ' ' + convert(n % 1000) : '');
|
||||
}
|
||||
if (n < 10000000) {
|
||||
return convert(n ~/ 100000) + ' Lakh' + (n % 100000 != 0 ? ' ' + convert(n % 100000) : '');
|
||||
}
|
||||
return convert(n ~/ 10000000) + ' Crore' + (n % 10000000 != 0 ? ' ' + convert(n % 10000000) : '');
|
||||
}
|
||||
|
||||
return convert(number);
|
||||
}
|
||||
}
|
||||
1459
lib/PDF Invoice/purchase_invoice_pdf.dart
Normal file
1459
lib/PDF Invoice/purchase_invoice_pdf.dart
Normal file
File diff suppressed because it is too large
Load Diff
1868
lib/PDF Invoice/sales_invoice_pdf.dart
Normal file
1868
lib/PDF Invoice/sales_invoice_pdf.dart
Normal file
File diff suppressed because it is too large
Load Diff
592
lib/PDF Invoice/subscription_invoice_pdf.dart
Normal file
592
lib/PDF Invoice/subscription_invoice_pdf.dart
Normal file
@@ -0,0 +1,592 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:excel/excel.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobile_pos/Const/api_config.dart';
|
||||
import 'package:mobile_pos/PDF%20Invoice/universal_image_widget.dart';
|
||||
import 'package:mobile_pos/Screens/all_transaction/model/transaction_model.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as l;
|
||||
import 'package:mobile_pos/model/sale_transaction_model.dart';
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:pdf/pdf.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
import 'package:printing/printing.dart';
|
||||
import '../Screens/Products/add product/modle/create_product_model.dart';
|
||||
import '../model/business_info_model.dart';
|
||||
import '../model/subscription_report_model.dart';
|
||||
import 'pdf_common_functions.dart';
|
||||
|
||||
class SubscriptionInvoicePdf {
|
||||
static Future<void> generateSaleDocument(
|
||||
List<SubscriptionReportModel> subscription, BusinessInformationModel personalInformation, BuildContext context,
|
||||
{bool? share, bool? download, bool? showPreview}) async {
|
||||
final pw.Document doc = pw.Document();
|
||||
final _lang = l.S.of(context);
|
||||
|
||||
final String imageUrl =
|
||||
'${APIConfig.domain}${(personalInformation.data?.showA4InvoiceLogo == 1) ? personalInformation.data?.a4InvoiceLogo : ''}';
|
||||
dynamic imageData = await PDFCommonFunctions().getNetworkImage(imageUrl);
|
||||
imageData ??= (personalInformation.data?.showA4InvoiceLogo == 1)
|
||||
? await PDFCommonFunctions().loadAssetImage('images/logo.png')
|
||||
: null;
|
||||
final englishFont = pw.Font.ttf(await rootBundle.load('fonts/NotoSans/NotoSans-Regular.ttf'));
|
||||
final englishBold = pw.Font.ttf(await rootBundle.load('fonts/NotoSans/NotoSans-Medium.ttf'));
|
||||
final banglaFont = pw.Font.ttf(await rootBundle.load('assets/fonts/siyam_rupali_ansi.ttf'));
|
||||
final arabicFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Amiri-Regular.ttf'));
|
||||
final hindiFont = pw.Font.ttf(await rootBundle.load('assets/fonts/Hind-Regular.ttf'));
|
||||
final frenchFont = pw.Font.ttf(await rootBundle.load('assets/fonts/GFSDidot-Regular.ttf'));
|
||||
|
||||
// Helper function
|
||||
pw.Font getFont({bool bold = false}) {
|
||||
switch (selectedLanguage) {
|
||||
case 'en':
|
||||
return bold ? englishBold : englishFont;
|
||||
case 'bn':
|
||||
// Bold not available, fallback to regular
|
||||
return banglaFont;
|
||||
case 'ar':
|
||||
return arabicFont;
|
||||
case 'hi':
|
||||
return hindiFont;
|
||||
case 'fr':
|
||||
return frenchFont;
|
||||
default:
|
||||
return bold ? englishBold : englishFont;
|
||||
}
|
||||
}
|
||||
|
||||
getFontWithLangMatching(String data) {
|
||||
String detectedLanguage = detectLanguageEnhanced(data);
|
||||
if (detectedLanguage == 'en') {
|
||||
return englishFont;
|
||||
} else if (detectedLanguage == 'bn') {
|
||||
return banglaFont;
|
||||
} else if (detectedLanguage == 'ar') {
|
||||
return arabicFont;
|
||||
} else if (detectedLanguage == 'hi') {
|
||||
return hindiFont;
|
||||
} else if (detectedLanguage == 'fr') {
|
||||
return frenchFont;
|
||||
} else {
|
||||
return englishFont;
|
||||
}
|
||||
}
|
||||
|
||||
final showWarranty = personalInformation.data?.showWarranty == 1 &&
|
||||
(personalInformation.data?.warrantyVoidLabel != null || personalInformation.data?.warrantyVoid != null);
|
||||
|
||||
doc.addPage(
|
||||
pw.MultiPage(
|
||||
pageFormat: PdfPageFormat.letter.copyWith(marginBottom: 1.5 * PdfPageFormat.cm),
|
||||
margin: pw.EdgeInsets.zero,
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
header: (pw.Context context) {
|
||||
return pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(20.0),
|
||||
child: pw.Column(
|
||||
children: [
|
||||
pw.Row(mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [
|
||||
pw.Container(
|
||||
height: 54.12,
|
||||
width: 200,
|
||||
child: universalImage(
|
||||
imageData,
|
||||
w: 200,
|
||||
h: 54.12,
|
||||
),
|
||||
),
|
||||
pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (personalInformation.data?.meta?.showAddress == 1)
|
||||
pw.SizedBox(
|
||||
width: 200,
|
||||
child: getLocalizedPdfText(
|
||||
'${_lang.address}: ${personalInformation.data?.address ?? ''}',
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (personalInformation.data?.meta?.showPhoneNumber == 1)
|
||||
pw.SizedBox(
|
||||
width: 200,
|
||||
child: getLocalizedPdfText(
|
||||
'${_lang.mobile}: ${personalInformation.data?.phoneNumber ?? ''}',
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (personalInformation.data?.meta?.showEmail == 1)
|
||||
pw.SizedBox(
|
||||
width: 200,
|
||||
child: getLocalizedPdfText(
|
||||
'${_lang.emailText}: ${personalInformation.data?.invoiceEmail ?? ''}',
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
//vat Name
|
||||
if (personalInformation.data?.meta?.showVat == 1)
|
||||
if (personalInformation.data?.vatNo != null && personalInformation.data?.meta?.showVat == 1)
|
||||
pw.SizedBox(
|
||||
width: 200,
|
||||
child: getLocalizedPdfText(
|
||||
'${personalInformation.data?.vatName ?? _lang.vatNumber}: ${personalInformation.data?.vatNo ?? ''}',
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
pw.SizedBox(height: 16.0),
|
||||
pw.Center(
|
||||
child: pw.Container(
|
||||
padding: pw.EdgeInsets.symmetric(horizontal: 19, vertical: 10),
|
||||
decoration: pw.BoxDecoration(
|
||||
borderRadius: pw.BorderRadius.circular(20),
|
||||
border: pw.Border.all(color: PdfColors.black),
|
||||
),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.INVOICE,
|
||||
pw.TextStyle(
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
fontSize: 18,
|
||||
color: PdfColors.black,
|
||||
font: getFont(bold: true),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 20),
|
||||
pw.Row(mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [
|
||||
pw.Column(crossAxisAlignment: pw.CrossAxisAlignment.start, children: [
|
||||
//customer name
|
||||
pw.Row(children: [
|
||||
pw.SizedBox(
|
||||
width: 60.0,
|
||||
child: getLocalizedPdfText(
|
||||
_lang.billTO,
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.SizedBox(
|
||||
width: 10.0,
|
||||
child: pw.Text(
|
||||
':',
|
||||
style: pw.Theme.of(context).defaultTextStyle.copyWith(color: PdfColors.black),
|
||||
),
|
||||
),
|
||||
pw.SizedBox(
|
||||
width: 100.0,
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
personalInformation.data?.user?.name ?? '',
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFontWithLangMatching(personalInformation.data?.user?.name ?? ''),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
]),
|
||||
//mobile
|
||||
pw.Row(children: [
|
||||
pw.SizedBox(
|
||||
width: 60.0,
|
||||
child: getLocalizedPdfText(
|
||||
_lang.mobile,
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.SizedBox(
|
||||
width: 10.0,
|
||||
child: pw.Text(
|
||||
':',
|
||||
style: pw.Theme.of(context).defaultTextStyle.copyWith(color: PdfColors.black),
|
||||
),
|
||||
),
|
||||
pw.SizedBox(
|
||||
width: 100.0,
|
||||
child: getLocalizedPdfText(personalInformation.data?.phoneNumber ?? 'n/a',
|
||||
pw.TextStyle(font: getFont(), fontFallback: [englishFont])),
|
||||
),
|
||||
]),
|
||||
//Address
|
||||
pw.Row(children: [
|
||||
pw.SizedBox(
|
||||
width: 60.0,
|
||||
child: getLocalizedPdfText(
|
||||
_lang.address,
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.SizedBox(
|
||||
width: 10.0,
|
||||
child: pw.Text(
|
||||
':',
|
||||
style: pw.Theme.of(context).defaultTextStyle.copyWith(color: PdfColors.black),
|
||||
),
|
||||
),
|
||||
pw.SizedBox(
|
||||
width: 150.0,
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
personalInformation.data?.address ?? '',
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFontWithLangMatching(personalInformation.data?.address ?? ''),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
footer: (pw.Context context) {
|
||||
return pw.Column(children: [
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(10.0),
|
||||
child: pw.Row(mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [
|
||||
pw.Container(
|
||||
alignment: pw.Alignment.centerRight,
|
||||
margin: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm),
|
||||
padding: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm),
|
||||
child: pw.Column(children: [
|
||||
pw.Container(
|
||||
width: 120.0,
|
||||
height: 2.0,
|
||||
color: PdfColors.black,
|
||||
),
|
||||
pw.SizedBox(height: 4.0),
|
||||
getLocalizedPdfText(
|
||||
_lang.customerSignature,
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
))
|
||||
]),
|
||||
),
|
||||
pw.Container(
|
||||
alignment: pw.Alignment.centerRight,
|
||||
margin: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm),
|
||||
padding: const pw.EdgeInsets.only(bottom: 3.0 * PdfPageFormat.mm),
|
||||
child: pw.Column(children: [
|
||||
pw.Container(
|
||||
width: 120.0,
|
||||
height: 2.0,
|
||||
color: PdfColors.black,
|
||||
),
|
||||
pw.SizedBox(height: 4.0),
|
||||
getLocalizedPdfText(
|
||||
_lang.authorizedSignature,
|
||||
pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
))
|
||||
]),
|
||||
),
|
||||
]),
|
||||
),
|
||||
if (showWarranty)
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.symmetric(horizontal: 10),
|
||||
child: pw.Container(
|
||||
width: double.infinity,
|
||||
padding: const pw.EdgeInsets.all(4),
|
||||
decoration: pw.BoxDecoration(
|
||||
border: pw.Border.all(color: PdfColors.black),
|
||||
),
|
||||
child: pw.RichText(
|
||||
text: pw.TextSpan(
|
||||
children: [
|
||||
if (personalInformation.data?.warrantyVoidLabel != null)
|
||||
pw.TextSpan(
|
||||
text: '${personalInformation.data!.warrantyVoidLabel!}- ',
|
||||
style: pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
),
|
||||
),
|
||||
if (personalInformation.data?.warrantyVoid != null)
|
||||
pw.TextSpan(
|
||||
text: personalInformation.data!.warrantyVoid!,
|
||||
style: pw.TextStyle(
|
||||
color: PdfColors.black,
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
pw.SizedBox(height: 10),
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.symmetric(horizontal: 10),
|
||||
child: pw.Center(
|
||||
child: pw.Text(
|
||||
'${personalInformation.data?.developByLevel ?? ''} ${personalInformation.data?.developBy ?? ''}',
|
||||
style: pw.TextStyle(fontWeight: pw.FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
]);
|
||||
},
|
||||
build: (pw.Context context) => <pw.Widget>[
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0),
|
||||
child: pw.Column(
|
||||
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Main products table
|
||||
pw.Table(
|
||||
border: pw.TableBorder(
|
||||
horizontalInside: pw.BorderSide(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
verticalInside: pw.BorderSide(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
left: pw.BorderSide(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
right: pw.BorderSide(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
top: pw.BorderSide(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
bottom: pw.BorderSide(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
),
|
||||
columnWidths: <int, pw.TableColumnWidth>{
|
||||
0: const pw.FlexColumnWidth(1),
|
||||
1: const pw.FlexColumnWidth(2),
|
||||
2: const pw.FlexColumnWidth(3),
|
||||
3: const pw.FlexColumnWidth(2),
|
||||
4: const pw.FlexColumnWidth(2),
|
||||
5: const pw.FlexColumnWidth(3),
|
||||
},
|
||||
children: [
|
||||
// Table header
|
||||
pw.TableRow(
|
||||
children: [
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.sl,
|
||||
pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
),
|
||||
textAlignment: pw.TextAlign.center,
|
||||
),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.date,
|
||||
pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
textAlignment: pw.TextAlign.left,
|
||||
),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.packageName,
|
||||
pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
textAlignment: pw.TextAlign.center,
|
||||
),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.started,
|
||||
pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
textAlignment: pw.TextAlign.center,
|
||||
),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.end,
|
||||
pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
textAlignment: pw.TextAlign.center,
|
||||
),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8),
|
||||
child: getLocalizedPdfText(
|
||||
_lang.paymentMethod,
|
||||
pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
fontFallback: [englishFont],
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
),
|
||||
textAlignment: pw.TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Table rows for products
|
||||
for (int i = 0; i < subscription.length; i++)
|
||||
pw.TableRow(
|
||||
children: [
|
||||
pw.Padding(
|
||||
padding: const pw.EdgeInsets.all(8.0),
|
||||
child: pw.Text('${i + 1}', textAlign: pw.TextAlign.center),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.all(8.0),
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
textAlignment: pw.TextAlign.center,
|
||||
subscription[i].startDate == null
|
||||
? "N/A"
|
||||
: DateFormat('dd MMM yyyy').format(subscription[i].startDate!),
|
||||
pw.TextStyle(
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.all(8.0),
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
textAlignment: pw.TextAlign.center,
|
||||
subscription[i].name ?? 'n/a',
|
||||
pw.TextStyle(
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.all(8.0),
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
textAlignment: pw.TextAlign.center,
|
||||
subscription[i].startDate == null
|
||||
? "N/A"
|
||||
: DateFormat('dd MMM yyyy').format(subscription[i].startDate!),
|
||||
pw.TextStyle(
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.all(8.0),
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
textAlignment: pw.TextAlign.center,
|
||||
subscription[i].endDate == null
|
||||
? "N/A"
|
||||
: DateFormat('dd MMM yyyy').format(subscription[i].startDate!),
|
||||
pw.TextStyle(
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
pw.SizedBox(height: 12),
|
||||
pw.Padding(
|
||||
padding: pw.EdgeInsets.all(8.0),
|
||||
child: getLocalizedPdfTextWithLanguage(
|
||||
textAlignment: pw.TextAlign.center,
|
||||
subscription[i].paymentBy ?? 'n/a',
|
||||
pw.TextStyle(
|
||||
font: getFont(),
|
||||
fontFallback: [englishFont],
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
pw.SizedBox(height: 20.0),
|
||||
if ((!personalInformation.data!.invoiceNote.isEmptyOrNull ||
|
||||
!personalInformation.data!.invoiceNoteLevel.isEmptyOrNull) &&
|
||||
personalInformation.data!.showNote == 1)
|
||||
pw.RichText(
|
||||
textAlign: pw.TextAlign.start,
|
||||
text: pw.TextSpan(
|
||||
text: '${personalInformation.data?.invoiceNoteLevel ?? ''}: ',
|
||||
style: pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
),
|
||||
children: [
|
||||
pw.TextSpan(
|
||||
text: personalInformation.data?.invoiceNote ?? '',
|
||||
style: pw.TextStyle(
|
||||
font: getFont(bold: true),
|
||||
))
|
||||
])),
|
||||
pw.SizedBox(height: 30),
|
||||
|
||||
if (personalInformation.data?.showGratitudeMsg == 1)
|
||||
if (!personalInformation.data!.gratitudeMessage.isEmptyOrNull)
|
||||
pw.Container(
|
||||
width: double.infinity,
|
||||
padding: const pw.EdgeInsets.only(bottom: 8.0),
|
||||
child: pw.Center(
|
||||
child: pw.Text(
|
||||
personalInformation.data!.gratitudeMessage ?? '',
|
||||
)),
|
||||
),
|
||||
pw.Padding(padding: const pw.EdgeInsets.all(10)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (showPreview == true) {
|
||||
await Printing.layoutPdf(
|
||||
name: personalInformation.data?.companyName ?? '',
|
||||
usePrinterSettings: true,
|
||||
dynamicLayout: true,
|
||||
forceCustomPrintPaper: true,
|
||||
onLayout: (PdfPageFormat format) async => doc.save());
|
||||
} else {
|
||||
await PDFCommonFunctions.savePdfAndShowPdf(
|
||||
context: context,
|
||||
shopName: personalInformation.data?.companyName ?? '',
|
||||
invoice: '1',
|
||||
doc: doc,
|
||||
isShare: share,
|
||||
download: download,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
lib/PDF Invoice/universal_image_widget.dart
Normal file
83
lib/PDF Invoice/universal_image_widget.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:pdf/pdf.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
|
||||
pw.Widget universalImage(dynamic data, {double? w, double? h}) {
|
||||
try {
|
||||
// Case 1: Uint8List → PNG/JPG
|
||||
if (data is Uint8List) {
|
||||
return pw.Image(
|
||||
pw.MemoryImage(data),
|
||||
width: w,
|
||||
height: h,
|
||||
fit: pw.BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
// Case 2: SVG string
|
||||
if (data is String && data.trim().startsWith("<svg")) {
|
||||
return pw.SvgImage(
|
||||
svg: data,
|
||||
width: w,
|
||||
height: h,
|
||||
fit: pw.BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
// Case 3: Base64 Image String (data:image/png;base64,...)
|
||||
if (data is String && data.contains("base64")) {
|
||||
try {
|
||||
final base64Str = data.split(',').last;
|
||||
final bytes = base64Decode(base64Str);
|
||||
|
||||
return pw.Image(
|
||||
pw.MemoryImage(bytes),
|
||||
width: w,
|
||||
height: h,
|
||||
fit: pw.BoxFit.cover,
|
||||
);
|
||||
} catch (e) {
|
||||
// base64 decode failed
|
||||
return pw.Container(width: w, height: h);
|
||||
}
|
||||
}
|
||||
|
||||
// Case 4: Unknown String → DO NOT convert to image
|
||||
if (data is String) {
|
||||
return pw.Container(
|
||||
alignment: pw.Alignment.center,
|
||||
width: w,
|
||||
height: h,
|
||||
padding: pw.EdgeInsets.all(8),
|
||||
decoration: pw.BoxDecoration(
|
||||
shape: pw.BoxShape.circle,
|
||||
color: PdfColors.grey200,
|
||||
),
|
||||
child: pw.Text(
|
||||
"No Image",
|
||||
textAlign: pw.TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Unknown type
|
||||
return pw.Container(width: w, height: h);
|
||||
} catch (e) {
|
||||
return pw.Container(
|
||||
alignment: pw.Alignment.center,
|
||||
width: w,
|
||||
height: h,
|
||||
padding: pw.EdgeInsets.all(8),
|
||||
decoration: pw.BoxDecoration(
|
||||
shape: pw.BoxShape.circle,
|
||||
color: PdfColors.grey200,
|
||||
),
|
||||
child: pw.Text(
|
||||
"Invalid Image",
|
||||
textAlign: pw.TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user