Aidra Connect 10.0.2+16
Aidra Connect Mobile Application
Loading...
Searching...
No Matches
generate_voucher.dart
Go to the documentation of this file.
1import 'dart:io';
2import 'dart:convert';
3import 'package:another_flushbar/flushbar.dart';
4import 'package:connect/core/constants/assets.dart';
5import 'package:connect/core/services/service_locator.dart';
6import 'package:connect/core/ui/widgets/custom_snackbar.dart';
7import 'package:connect/core/utils/number_conversion.dart';
8import 'package:flutter/material.dart';
9import 'package:flutter/services.dart';
10import 'package:flutter_screenutil/flutter_screenutil.dart';
11import 'package:open_filex/open_filex.dart';
12import 'package:pdf/pdf.dart';
13import 'package:pdf/widgets.dart' as pw;
14import 'package:path_provider/path_provider.dart';
15
16import '../../../../../../../../../../core/api/api_client.dart';
17
18Future<CollectionVoucherDocumentModel?>? fetchData(
19 {required String collectionName}) async {
20 try {
21 final apiClient = sl<ApiClient>();
22 final response = await apiClient.get(
23 '',
24 {},
25 false,
26 'https://dev-api.aidra.tech/document/collectionDocument/$collectionName',
27 );
28 if (response == {}) {
29 return null;
30 }
31
32 return CollectionVoucherDocumentModel.fromJson(response);
33 } catch (e) {
34 return null;
35 }
36}
37
39 String collectionName,
40 BuildContext context,
41) async {
42 final data = await fetchData(collectionName: collectionName);
43
44 if (data == null) {
45 Flushbar(
46 margin: EdgeInsets.all(10),
47 borderRadius: BorderRadius.circular(10),
48 backgroundColor: Theme.of(context).colorScheme.error,
49 message: 'Voucher generation failed. Please try again.',
50 duration: Duration(seconds: 2),
51 ).show(context);
52 return;
53 }
54
55 final pdf = pw.Document();
56
57 // Load custom font with Unicode support
58 final fontData = await rootBundle.load('assets/fonts/notokufi_font.ttf');
59 final ttf = pw.Font.ttf(fontData);
60
61 final boldyFont =
62 await rootBundle.load("assets/fonts/NotoKufiArabic-Bold.ttf");
63 final ttf2 = pw.Font.ttf(boldyFont);
64
65 // Create theme with the custom font
66 final theme = pw.ThemeData.withFont(
67 base: ttf,
68 bold:
69 ttf, // Use the same font for bold since we don't have a separate bold variant
70 italic: ttf,
71 boldItalic: ttf,
72 );
73
74 String extractName(String? productName) {
75 if (productName == null || productName.isEmpty) {
76 return '';
77 }
78
79 final Map<String, dynamic> decoded = jsonDecode(productName);
80 return decoded['en_US'] ?? '';
81 }
82
83 final qrCode = pw.BarcodeWidget(
84 barcode: pw.Barcode.qrCode(),
85 data: data?.collection.name ?? '',
86 width: 50,
87 height: 50,
88 );
89
90 String _formatDate(String? dateString) {
91 if (dateString == null || dateString.isEmpty) return '--';
92
93 try {
94 DateTime parsedDate = DateTime.parse(dateString);
95 return '${parsedDate.day}/${parsedDate.month}/${parsedDate.year}';
96 } catch (e) {
97 return '--'; // Fallback if parsing fails
98 }
99 }
100
101 final directorSignature = pw.Column(
102 children: [
103 pw.Text(
104 'Driver / Collector : Driver 26',
105 style: pw.TextStyle(
106 font: ttf, fontSize: 10, fontWeight: pw.FontWeight.bold),
107 ),
108 pw.Container(
109 width: 90,
110 height: 90,
111 child: pw.Center(
112 child: pw.Image(
113 pw.MemoryImage(
114 (await rootBundle.load(Assets.directorSign)).buffer.asUint8List(),
115 ),
116 fit: pw.BoxFit.contain,
117 ),
118 ),
119 ),
120 ],
121 );
122
123 final collectorSignature = pw.Column(
124 children: [
125 pw.Text(
126 'Collection Point : ${data?.collection.partnerId?.name ?? ''}',
127 style: pw.TextStyle(
128 font: ttf, fontSize: 10, fontWeight: pw.FontWeight.bold),
129 ),
130 pw.Container(
131 width: 90,
132 height: 90,
133 child: pw.Center(
134 child: pw.Image(
135 pw.MemoryImage(
136 (await rootBundle.load(Assets.collectorSign))
137 .buffer
138 .asUint8List(),
139 ),
140 fit: pw.BoxFit.contain,
141 ),
142 ),
143 ),
144 ],
145 );
146
147 pdf.addPage(
148 pw.Page(
149 theme: theme,
150 pageFormat: PdfPageFormat.a4,
151 //textDirection: pw.TextDirection.rtl,
152 build: (context) {
153 return pw.Column(
154 crossAxisAlignment: pw.CrossAxisAlignment.center,
155 children: [
156 pw.Text(
157 'Collection Voucher',
158 style: pw.TextStyle(
159 font: ttf, fontSize: 20, fontWeight: pw.FontWeight.bold),
160 ),
161 pw.SizedBox(height: 8),
162 pw.Text(
163 'Document Number : ${data?.collection.name ?? ''}',
164 style: pw.TextStyle(
165 font: ttf,
166 fontSize: 16,
167 fontWeight: pw.FontWeight.normal,
168 color: PdfColors.blue,
169 ),
170 ),
171 pw.SizedBox(height: 25),
172 pw.Align(
173 alignment: pw.Alignment.centerLeft,
174 child: pw.Column(
175 crossAxisAlignment: pw.CrossAxisAlignment.start,
176 children: [
177 pw.Text(
178 'Name:',
179 textDirection: pw.TextDirection.rtl,
180 style: pw.TextStyle(
181 font: ttf,
182 fontSize: 15,
183 fontWeight: pw.FontWeight.bold,
184 color: PdfColors.blue,
185 ),
186 ),
187 pw.Text(
188 data?.collection.partnerId?.name ?? '',
189 textDirection: pw.TextDirection.rtl,
190 style: pw.TextStyle(
191 font: ttf,
192 fontSize: 14,
193 fontWeight: pw.FontWeight.normal),
194 ),
195 pw.SizedBox(height: 8),
196 pw.Text(
197 'Address:',
198 style: pw.TextStyle(
199 font: ttf,
200 fontSize: 15,
201 fontWeight: pw.FontWeight.bold,
202 color: PdfColors.blue,
203 ),
204 ),
205 pw.Text(
206 data?.collection.partnerId?.street ?? '',
207 textDirection: pw.TextDirection.rtl,
208 style: pw.TextStyle(
209 font: ttf,
210 fontSize: 14,
211 fontWeight: pw.FontWeight.normal,
212 ),
213 ),
214 ],
215 ),
216 ),
217 pw.SizedBox(height: 30),
218 pw.Table(
219 border: pw.TableBorder.all(),
220 children: [
221 pw.TableRow(
222 children: [
223 pw.Container(
224 padding: const pw.EdgeInsets.all(8),
225 child: pw.Text(
226 'Collection Date',
227 style: pw.TextStyle(
228 font: ttf2,
229 fontWeight: pw.FontWeight.bold,
230 ),
231 ),
232 ),
233 pw.Container(
234 padding: const pw.EdgeInsets.all(8),
235 child: pw.Text(
236 _formatDate(data?.collection
237 .confirmationDate), // Call function to format the date
238 style: pw.TextStyle(font: ttf),
239 ),
240 ),
241 pw.Container(
242 padding: const pw.EdgeInsets.all(8),
243 child: pw.Text(
244 'Payment Bill Ref',
245 style: pw.TextStyle(
246 font: ttf2,
247 fontWeight: pw.FontWeight.bold,
248 ),
249 ),
250 ),
251 pw.Container(
252 padding: const pw.EdgeInsets.all(8),
253 child: pw.Text(
254 data?.collection.paymentBillRef ?? '',
255 style: pw.TextStyle(font: ttf),
256 ),
257 ),
258 ],
259 ),
260 pw.TableRow(
261 children: [
262 pw.Container(
263 padding: const pw.EdgeInsets.all(8),
264 child: pw.Text(
265 'Collection Order Ref',
266 style: pw.TextStyle(
267 font: ttf2,
268 fontWeight: pw.FontWeight.bold,
269 ),
270 ),
271 ),
272 pw.Container(
273 padding: const pw.EdgeInsets.all(8),
274 child: pw.Text(
275 data?.collection.collectionOrderRef ?? '',
276 style: pw.TextStyle(font: ttf),
277 ),
278 ),
279 pw.Container(
280 padding: const pw.EdgeInsets.all(8),
281 child: pw.Text(
282 'Payment Method',
283 style: pw.TextStyle(
284 font: ttf2,
285 fontWeight: pw.FontWeight.bold,
286 ),
287 ),
288 ),
289 pw.Container(
290 padding: const pw.EdgeInsets.all(8),
291 child: pw.Text(
292 'Deferred Payment',
293 style: pw.TextStyle(font: ttf),
294 ),
295 ),
296 ],
297 ),
298 ],
299 ),
300 pw.SizedBox(height: 25),
301 // Use custom font explicitly for table
302 pw.Table(
303 border: pw.TableBorder.all(),
304 children: [
305 // Header row
306 pw.TableRow(
307 decoration: pw.BoxDecoration(color: PdfColors.grey200),
308 children: ['Product', 'Quantity Received', 'Quantity Billed']
309 .map((header) => pw.Container(
310 padding: const pw.EdgeInsets.all(8),
311 alignment: pw.Alignment.center,
312 child: pw.Text(
313 header,
314 style: pw.TextStyle(
315 font: ttf2,
316 fontWeight: pw.FontWeight.bold,
317 ),
318 ),
319 ))
320 .toList(),
321 ),
322 // Data row
323 pw.TableRow(
324 children: [
325 pw.Container(
326 padding: const pw.EdgeInsets.all(8),
327 alignment: pw.Alignment.centerLeft,
328 child: pw.Text(
329 extractName(data?.product),
330 style: pw.TextStyle(font: ttf),
331 ),
332 ),
333 pw.Container(
334 padding: const pw.EdgeInsets.all(8),
335 alignment: pw.Alignment.centerLeft,
336 child: pw.Text(
337 '${NumberConversionService.convertAndFormatWeight(data?.quantityReceived)} ',
338 style: pw.TextStyle(font: ttf),
339 ),
340 ),
341 pw.Container(
342 padding: const pw.EdgeInsets.all(8),
343 alignment: pw.Alignment.centerLeft,
344 child: pw.Text(
345 '${NumberConversionService.convertAndFormatWeight(data?.quantityBilled)} ',
346 style: pw.TextStyle(font: ttf),
347 ),
348 ),
349 ],
350 ),
351 ],
352 ),
353 pw.SizedBox(height: 30),
354 pw.Row(
355 mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
356 children: [directorSignature, collectorSignature],
357 ),
358 pw.SizedBox(height: 30),
359 pw.Row(
360 children: [
361 pw.Spacer(),
362 pw.Column(children: [
363 pw.Row(
364 mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
365 children: [
366 qrCode,
367 pw.SizedBox(width: 10),
368 ],
369 ),
370 pw.SizedBox(height: 10),
371 pw.Text(
372 'STRICTLY CONFIDENTIAL',
373 style: pw.TextStyle(
374 font: ttf,
375 fontSize: 8,
376 fontWeight: pw.FontWeight.normal,
377 color: const PdfColor(0.5, 0.5, 0.5),
378 ),
379 ),
380 ])
381 ],
382 ),
383 ],
384 );
385 },
386 ),
387 );
388
389 final output = await getTemporaryDirectory();
390 final file = File('${output.path}/collection_voucher_document.pdf');
391 await file.writeAsBytes(await pdf.save());
392 OpenFilex.open(file.path);
393}
394
395class CollectionVoucherDocumentModel {
396 final Collection collection;
397 final double? priceUnit;
398 final String? product;
399 final double? quantityBilled;
400 final double? quantityReceived;
401
402 CollectionVoucherDocumentModel({
403 required this.collection,
404 required this.priceUnit,
405 required this.product,
406 required this.quantityBilled,
407 required this.quantityReceived,
408 });
409
410 factory CollectionVoucherDocumentModel.fromJson(Map<String, dynamic> json) {
411 return CollectionVoucherDocumentModel(
412 collection: Collection.fromJson(json['collection']),
413 priceUnit: json['priceUnit']?.toDouble() ?? 0.0,
414 product: json['product'] ?? '',
415 quantityBilled: json['quantityBilled']?.toDouble() ?? 0,
416 quantityReceived: json['quantityReceived']?.toDouble() ?? 0,
417 );
418 }
419
420 Map<String, dynamic> toJson() {
421 return {
422 'collection': collection.toJson(),
423 'priceUnit': priceUnit,
424 'product': product,
425 'quantityBilled': quantityBilled,
426 'quantityReceived': quantityReceived,
427 };
428 }
429}
430
431class Collection {
432 final String? billStatus;
433 final String? collectionConfirmRef;
434 final String? collectionOrderRef;
435 final String? confirmationDate;
436 final int? id;
437 final String? name;
438 final Partner? partnerId;
439 final String? paymentBillRef;
440 final String? receiptStatus;
441 final String? status;
442
443 Collection({
444 required this.billStatus,
445 required this.collectionConfirmRef,
446 required this.collectionOrderRef,
447 required this.confirmationDate,
448 required this.id,
449 required this.name,
450 required this.partnerId,
451 required this.paymentBillRef,
452 required this.receiptStatus,
453 required this.status,
454 });
455
456 factory Collection.fromJson(Map<String, dynamic> json) {
457 return Collection(
458 billStatus: json['billStatus'] ?? '',
459 collectionConfirmRef: json['collectionConfirmRef'] ?? '',
460 collectionOrderRef: json['collectionOrderRef'] ?? '',
461 confirmationDate: json['confirmationDate'] ?? '',
462 id: json['id'] ?? 0,
463 name: json['name'] ?? '',
464 partnerId: Partner.fromJson(json['partnerId']),
465 paymentBillRef: json['paymentBillRef'] ?? '',
466 receiptStatus: json['receiptStatus'] ?? '',
467 status: json['status'] ?? '',
468 );
469 }
470
471 Map<String, dynamic> toJson() {
472 return {
473 'billStatus': billStatus,
474 'collectionConfirmRef': collectionConfirmRef,
475 'collectionOrderRef': collectionOrderRef,
476 'confirmationDate': confirmationDate,
477 'id': id,
478 'name': name,
479 'partnerId': partnerId?.toJson(),
480 'paymentBillRef': paymentBillRef,
481 'receiptStatus': receiptStatus,
482 'status': status,
483 };
484 }
485}
486
487class Partner {
488 final String? contactName;
489 final int? id;
490 final bool? isCompany;
491 final String? name;
492 final int? parentId;
493 final String? partnerLatitude;
494 final String? partnerLongitude;
495 final String? street;
496
497 Partner({
498 required this.contactName,
499 required this.id,
500 required this.isCompany,
501 required this.name,
502 required this.parentId,
503 required this.partnerLatitude,
504 required this.partnerLongitude,
505 required this.street,
506 });
507
508 factory Partner.fromJson(Map<String, dynamic> json) {
509 return Partner(
510 contactName: json['contactName'] ?? '',
511 id: json['id'] ?? 0,
512 isCompany: json['isCompany'] ?? false,
513 name: json['name'] ?? '',
514 parentId: json['parentId'] ?? 0,
515 partnerLatitude: json['partnerLatitude'] ?? '',
516 partnerLongitude: json['partnerLongitude'] ?? '',
517 street: json['street'] ?? '',
518 );
519 }
520
521 Map<String, dynamic> toJson() {
522 return {
523 'contactName': contactName,
524 'id': id,
525 'isCompany': isCompany,
526 'name': name,
527 'parentId': parentId,
528 'partnerLatitude': partnerLatitude,
529 'partnerLongitude': partnerLongitude,
530 'street': street,
531 };
532 }
533}
class CalenderTransactionsSummaryVeiw extends StatefulWidget collection
static const String collectorSign
Definition assets.dart:43
static const String directorSign
Definition assets.dart:44
factory CurrencyModel fromJson(Map< String, dynamic > json)
final num partnerId
class CurrencyEntity id
final num priceUnit
final String name
final Widget child
final Color backgroundColor
final String message
Definition failures.dart:0
Future< CollectionVoucherDocumentModel?> fetchData({required String collectionName}) async
Future< void > generateCollectionVoucher(String collectionName, BuildContext context,) async
override Widget build(BuildContext context)
final sl
Map< String, dynamic > toJson()