@tomei/finance 0.4.19 → 0.4.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<img src="./assets/img/logo.svg" alt="Logo" />
|
|
23
23
|
</div>
|
|
24
24
|
</div>
|
|
25
|
-
<div class="tm_invoice_right tm_text_right">
|
|
25
|
+
<div class="tm_invoice_right tm_text_right tm_invoice_title">
|
|
26
26
|
<div class="tm_primary_color tm_f50 tm_text_uppercase">
|
|
27
27
|
Invoice
|
|
28
28
|
</div>
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
Credit Card - 236***********928 <br />Amount: $1732
|
|
144
144
|
</p>
|
|
145
145
|
</div>
|
|
146
|
-
<div class="
|
|
146
|
+
<div class="tm_item_total_section">
|
|
147
147
|
<table>
|
|
148
148
|
<tbody>
|
|
149
149
|
<tr>
|
|
@@ -187,23 +187,26 @@
|
|
|
187
187
|
</div>
|
|
188
188
|
</div>
|
|
189
189
|
</div>
|
|
190
|
-
<div class="
|
|
191
|
-
<
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
190
|
+
<div class="tm_footer_section">
|
|
191
|
+
<div class="tm_padd_15_20 tm_round_border">
|
|
192
|
+
<p class="tm_mb5">
|
|
193
|
+
<b class="tm_primary_color">Terms & Conditions:</b>
|
|
194
|
+
</p>
|
|
195
|
+
<ul class="tm_m0 tm_note_list tm_tnc_content">
|
|
196
|
+
<li>
|
|
197
|
+
All claims relating to quantity or shipping errors shall be
|
|
198
|
+
waived by Buyer unless made in writing to Seller within
|
|
199
|
+
thirty (30) days after delivery of goods to the address
|
|
200
|
+
stated.
|
|
201
|
+
</li>
|
|
202
|
+
<li>
|
|
203
|
+
Delivery dates are not guaranteed and Seller has no
|
|
204
|
+
liability for damages that may be incurred due to any delay
|
|
205
|
+
in shipment of goods hereunder. Taxes are excluded unless
|
|
206
|
+
otherwise stated.
|
|
207
|
+
</li>
|
|
208
|
+
</ul>
|
|
209
|
+
</div>
|
|
207
210
|
</div>
|
|
208
211
|
<!-- .tm_note -->
|
|
209
212
|
</div>
|
package/package.json
CHANGED
package/src/document/document.ts
CHANGED
|
@@ -340,7 +340,7 @@ export default class Document extends AccountSystemEntity {
|
|
|
340
340
|
const invoiceNo = this.DocNo;
|
|
341
341
|
|
|
342
342
|
// Update the invoice number and date in the HTML content
|
|
343
|
-
const
|
|
343
|
+
const invoiceTitleInfo = `
|
|
344
344
|
<div class="tm_invoice_seperator tm_gray_bg"></div>
|
|
345
345
|
<div class="tm_invoice_info_list">
|
|
346
346
|
<p class="tm_invoice_number tm_m0">
|
|
@@ -351,10 +351,10 @@ export default class Document extends AccountSystemEntity {
|
|
|
351
351
|
</p>
|
|
352
352
|
</div>
|
|
353
353
|
`;
|
|
354
|
-
const
|
|
355
|
-
await
|
|
356
|
-
invoice.innerHTML =
|
|
357
|
-
},
|
|
354
|
+
const invoiceTitleInfoHandle = await page.$('.tm_invoice_info');
|
|
355
|
+
await invoiceTitleInfoHandle.evaluate((invoice, invoiceTitleInfo) => {
|
|
356
|
+
invoice.innerHTML = invoiceTitleInfo;
|
|
357
|
+
}, invoiceTitleInfo);
|
|
358
358
|
|
|
359
359
|
// Update the invoice customer detail in the HTML content
|
|
360
360
|
const invoiceCustomerContent = `
|
|
@@ -435,7 +435,7 @@ export default class Document extends AccountSystemEntity {
|
|
|
435
435
|
</tbody>
|
|
436
436
|
</table>
|
|
437
437
|
`;
|
|
438
|
-
const invoiceRightFooterHandle = await page.$('.
|
|
438
|
+
const invoiceRightFooterHandle = await page.$('.tm_item_total_section');
|
|
439
439
|
await invoiceRightFooterHandle.evaluate(
|
|
440
440
|
(invoice, invoiceRightFooterContent) => {
|
|
441
441
|
invoice.innerHTML = invoiceRightFooterContent;
|
|
@@ -448,9 +448,9 @@ export default class Document extends AccountSystemEntity {
|
|
|
448
448
|
<li>
|
|
449
449
|
All payment must be made to:
|
|
450
450
|
<ul>
|
|
451
|
-
<li>Bank: <b>RHB</b></li>
|
|
451
|
+
<li>Bank: <b>RHB BANK BHD</b></li>
|
|
452
452
|
<li>Account Name: <b>TXG Financial Solutions Sdn Bhd</b></li>
|
|
453
|
-
<li>Account No.: <b>
|
|
453
|
+
<li>Account No.: <b>2142 13000 17954</b></li>
|
|
454
454
|
</ul>
|
|
455
455
|
</li>
|
|
456
456
|
<li>
|
|
@@ -515,6 +515,238 @@ export default class Document extends AccountSystemEntity {
|
|
|
515
515
|
return pdfStream;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Returns PDF template file for receipt generation
|
|
520
|
+
*
|
|
521
|
+
* @remarks - This function is converting generated HTML into PDF
|
|
522
|
+
*
|
|
523
|
+
* @see generateReceipt - for implementation
|
|
524
|
+
*
|
|
525
|
+
* @returns {Express.Multer.File} - PDF File
|
|
526
|
+
*/
|
|
527
|
+
public async DEFAULT_RECEIPT_TEMPLATE_PDF(
|
|
528
|
+
customer: FinanceCustomerBase,
|
|
529
|
+
): Promise<Express.Multer.File> {
|
|
530
|
+
/* prepping HTML conversion by getting its URL and String */
|
|
531
|
+
|
|
532
|
+
/* todo: needs to generate HTML first before converting to PDF */
|
|
533
|
+
const templateLocation = path.resolve('./invoice-template/index.html');
|
|
534
|
+
const cssLocation = path.resolve('./invoice-template/assets/css/style.css');
|
|
535
|
+
const readFileContent = util.promisify(fs.readFile);
|
|
536
|
+
// const htmlString = await readFileContent(templateLocation, 'utf8');
|
|
537
|
+
const [htmlString] = await Promise.all([
|
|
538
|
+
readFileContent(templateLocation, 'utf8'),
|
|
539
|
+
]);
|
|
540
|
+
|
|
541
|
+
/* converting HTML to PDF */
|
|
542
|
+
const browser = await puppeteer.launch();
|
|
543
|
+
const page = await browser.newPage();
|
|
544
|
+
|
|
545
|
+
const imagePath = path.resolve(
|
|
546
|
+
'./invoice-template/assets/img/tomei-logo.svg',
|
|
547
|
+
);
|
|
548
|
+
const imageBuffer = await readFileContent(imagePath);
|
|
549
|
+
const imageDataUrl = `data:image/svg+xml;base64,${imageBuffer.toString(
|
|
550
|
+
'base64',
|
|
551
|
+
)}`;
|
|
552
|
+
|
|
553
|
+
const imgTag = `<img src="${imageDataUrl}" alt="Logo">`;
|
|
554
|
+
|
|
555
|
+
await page.setContent(htmlString);
|
|
556
|
+
await page.addStyleTag({ path: cssLocation });
|
|
557
|
+
|
|
558
|
+
// Replace the <img> tag with the new image
|
|
559
|
+
const divHandle = await page.$('.tm_logo');
|
|
560
|
+
await divHandle.evaluate((div, imgTag) => {
|
|
561
|
+
div.innerHTML = imgTag;
|
|
562
|
+
}, imgTag);
|
|
563
|
+
|
|
564
|
+
// Get the current date and recei[t] number from customer
|
|
565
|
+
const currentDate = new Date().toLocaleDateString('en-GB');
|
|
566
|
+
const receiptNo = this.DocNo;
|
|
567
|
+
|
|
568
|
+
// Change and update from invoice template title to receipt title
|
|
569
|
+
const receiptTitle = `
|
|
570
|
+
<div class="tm_primary_color tm_f50 tm_text_uppercase">
|
|
571
|
+
Receipt
|
|
572
|
+
</div>
|
|
573
|
+
`;
|
|
574
|
+
const receiptTitleHandle = await page.$('.tm_invoice_title');
|
|
575
|
+
await receiptTitleHandle.evaluate((receipt, receiptTitle) => {
|
|
576
|
+
receipt.innerHTML = receiptTitle;
|
|
577
|
+
}, receiptTitle);
|
|
578
|
+
|
|
579
|
+
// Update the receipt number and date in the HTML content
|
|
580
|
+
const receiptTitleInfo = `
|
|
581
|
+
<div class="tm_invoice_seperator tm_gray_bg"></div>
|
|
582
|
+
<div class="tm_invoice_info_list">
|
|
583
|
+
<p class="tm_invoice_number tm_m0">
|
|
584
|
+
Receipt No: <b class="tm_primary_color">${receiptNo}</b>
|
|
585
|
+
</p>
|
|
586
|
+
<p class="tm_invoice_date tm_m0">
|
|
587
|
+
Date: <b class="tm_primary_color">${currentDate}</b>
|
|
588
|
+
</p>
|
|
589
|
+
</div>
|
|
590
|
+
`;
|
|
591
|
+
const receiptTitleInfoHandle = await page.$('.tm_invoice_info');
|
|
592
|
+
await receiptTitleInfoHandle.evaluate((receipt, receiptTitleInfo) => {
|
|
593
|
+
receipt.innerHTML = receiptTitleInfo;
|
|
594
|
+
}, receiptTitleInfo);
|
|
595
|
+
|
|
596
|
+
// Update the receipt customer detail in the HTML content
|
|
597
|
+
const receiptCustomerContent = `
|
|
598
|
+
<p class="tm_mb2">
|
|
599
|
+
<b class="tm_primary_color">Issued To:</b>
|
|
600
|
+
</p>
|
|
601
|
+
<p>
|
|
602
|
+
${customer.FullName} <br />
|
|
603
|
+
${customer.DefaultAddress.Address}, ${customer.DefaultAddress.City} <br />
|
|
604
|
+
${customer.DefaultAddress.Country} <br />
|
|
605
|
+
${customer.Email}
|
|
606
|
+
</p>
|
|
607
|
+
`;
|
|
608
|
+
const receiptCustomerHandle = await page.$('.tm_customer_detail');
|
|
609
|
+
await receiptCustomerHandle.evaluate((receipt, receiptCustomerContent) => {
|
|
610
|
+
receipt.innerHTML = receiptCustomerContent;
|
|
611
|
+
}, receiptCustomerContent);
|
|
612
|
+
|
|
613
|
+
// Remove the receipt Pay To details in the HTML content
|
|
614
|
+
const receiptPayToContent = ``;
|
|
615
|
+
const receiptPayToHandle = await page.$('.tm_payto_detail');
|
|
616
|
+
await receiptPayToHandle.evaluate((receipt, receiptPayToContent) => {
|
|
617
|
+
receipt.innerHTML = receiptPayToContent;
|
|
618
|
+
}, receiptPayToContent);
|
|
619
|
+
|
|
620
|
+
// Remove the receipt payment info in the HTML content
|
|
621
|
+
const receiptPaymentInfoContent = ``;
|
|
622
|
+
const receiptPaymentInfoHandle = await page.$('.tm_payment_info');
|
|
623
|
+
await receiptPaymentInfoHandle.evaluate(
|
|
624
|
+
(receipt, receiptPaymentInfoContent) => {
|
|
625
|
+
receipt.innerHTML = receiptPaymentInfoContent;
|
|
626
|
+
},
|
|
627
|
+
receiptPaymentInfoContent,
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
// Update the receipt right footer content in the HTML content
|
|
631
|
+
const receiptSubtotal = +this.Amount;
|
|
632
|
+
const receiptTax = 0;
|
|
633
|
+
const receiptRightFooterContent = `
|
|
634
|
+
<table>
|
|
635
|
+
<tbody>
|
|
636
|
+
<tr>
|
|
637
|
+
<td
|
|
638
|
+
class="tm_width_3 tm_primary_color tm_border_none tm_bold"
|
|
639
|
+
>
|
|
640
|
+
Subtotal
|
|
641
|
+
</td>
|
|
642
|
+
<td
|
|
643
|
+
class="tm_width_3 tm_primary_color tm_text_right tm_border_none tm_bold"
|
|
644
|
+
>
|
|
645
|
+
${this.Currency} ${receiptSubtotal}
|
|
646
|
+
</td>
|
|
647
|
+
</tr>
|
|
648
|
+
<tr>
|
|
649
|
+
<td
|
|
650
|
+
class="tm_width_3 tm_primary_color tm_border_none tm_pt0"
|
|
651
|
+
>
|
|
652
|
+
Tax <span class="tm_ternary_color">(0%)</span>
|
|
653
|
+
</td>
|
|
654
|
+
<td
|
|
655
|
+
class="tm_width_3 tm_primary_color tm_text_right tm_border_none tm_pt0"
|
|
656
|
+
>
|
|
657
|
+
${this.Currency} ${receiptTax}
|
|
658
|
+
</td>
|
|
659
|
+
</tr>
|
|
660
|
+
<tr class="tm_border_top tm_border_bottom">
|
|
661
|
+
<td
|
|
662
|
+
class="tm_width_3 tm_border_top_0 tm_bold tm_f16 tm_primary_color"
|
|
663
|
+
>
|
|
664
|
+
Grand Total
|
|
665
|
+
</td>
|
|
666
|
+
<td
|
|
667
|
+
class="tm_width_3 tm_border_top_0 tm_bold tm_f16 tm_primary_color tm_text_right"
|
|
668
|
+
>
|
|
669
|
+
${this.Currency} ${receiptSubtotal + receiptTax}
|
|
670
|
+
</td>
|
|
671
|
+
</tr>
|
|
672
|
+
</tbody>
|
|
673
|
+
</table>
|
|
674
|
+
`;
|
|
675
|
+
const receiptRightFooterHandle = await page.$('.tm_item_total_section');
|
|
676
|
+
await receiptRightFooterHandle.evaluate(
|
|
677
|
+
(receipt, receiptRightFooterContent) => {
|
|
678
|
+
receipt.innerHTML = receiptRightFooterContent;
|
|
679
|
+
},
|
|
680
|
+
receiptRightFooterContent,
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
// Update the receipt items content in the HTML content
|
|
684
|
+
const receiptDocumentItems = await this.DocumentItems;
|
|
685
|
+
|
|
686
|
+
let receiptItemContent = ``;
|
|
687
|
+
receiptDocumentItems.forEach((documentItem, i) => {
|
|
688
|
+
receiptItemContent += `
|
|
689
|
+
<tr>
|
|
690
|
+
<td class="tm_width_3">${i + 1}. ${documentItem.Name}</td>
|
|
691
|
+
<td class="tm_width_4">
|
|
692
|
+
${documentItem.Description}
|
|
693
|
+
</td>
|
|
694
|
+
<td class="tm_width_2">${documentItem.Currency} ${
|
|
695
|
+
documentItem.UnitPrice
|
|
696
|
+
}</td>
|
|
697
|
+
<td class="tm_width_1">${documentItem.Quantity}</td>
|
|
698
|
+
<td class="tm_width_2 tm_text_right">${documentItem.Currency} ${
|
|
699
|
+
+documentItem.UnitPrice * +documentItem.Quantity
|
|
700
|
+
}</td>
|
|
701
|
+
</tr>
|
|
702
|
+
`;
|
|
703
|
+
});
|
|
704
|
+
const receiptDocumentItemHandle = await page.$('.tm_items_content');
|
|
705
|
+
await receiptDocumentItemHandle.evaluate((receipt, receiptItemContent) => {
|
|
706
|
+
receipt.innerHTML = receiptItemContent;
|
|
707
|
+
}, receiptItemContent);
|
|
708
|
+
|
|
709
|
+
// Update the receipt Terms & Agreement content in the HTML content
|
|
710
|
+
const receiptFooterContent = `
|
|
711
|
+
<p class="tm_mb5 tm_text_center tm_f11">
|
|
712
|
+
<i>
|
|
713
|
+
Thank you for your payment. This is computer generated receipt
|
|
714
|
+
- no signature is required.
|
|
715
|
+
</i>
|
|
716
|
+
</p>
|
|
717
|
+
`;
|
|
718
|
+
const receiptFooterHandle = await page.$('.tm_footer_section');
|
|
719
|
+
await receiptFooterHandle.evaluate((receipt, receiptFooterContent) => {
|
|
720
|
+
receipt.innerHTML = receiptFooterContent;
|
|
721
|
+
}, receiptFooterContent);
|
|
722
|
+
|
|
723
|
+
const pdfBuffer = await page.pdf({
|
|
724
|
+
format: 'a4',
|
|
725
|
+
printBackground: true,
|
|
726
|
+
margin: {
|
|
727
|
+
left: '0px',
|
|
728
|
+
top: '0px',
|
|
729
|
+
right: '0px',
|
|
730
|
+
bottom: '0px',
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
const pdfStream: Express.Multer.File = {
|
|
735
|
+
buffer: pdfBuffer,
|
|
736
|
+
fieldname: 'FileStream',
|
|
737
|
+
originalname: 'sample-invoice.pdf',
|
|
738
|
+
mimetype: 'application/pdf',
|
|
739
|
+
size: pdfBuffer.length,
|
|
740
|
+
encoding: '8bit',
|
|
741
|
+
filename: 'sample-invoice.pdf',
|
|
742
|
+
destination: '',
|
|
743
|
+
path: '',
|
|
744
|
+
stream: null,
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
return pdfStream;
|
|
748
|
+
}
|
|
749
|
+
|
|
518
750
|
/**
|
|
519
751
|
* Returns the media representing the document in HTML format
|
|
520
752
|
*
|
|
@@ -569,7 +801,7 @@ export default class Document extends AccountSystemEntity {
|
|
|
569
801
|
}
|
|
570
802
|
|
|
571
803
|
/**
|
|
572
|
-
* Returns generated HTML & PDF Media
|
|
804
|
+
* Returns generated HTML & PDF Invoice Media
|
|
573
805
|
*
|
|
574
806
|
* @remarks - This function also create each media element in common_Media table
|
|
575
807
|
*
|
|
@@ -634,7 +866,7 @@ export default class Document extends AccountSystemEntity {
|
|
|
634
866
|
userId ?? 'System',
|
|
635
867
|
);
|
|
636
868
|
|
|
637
|
-
/*Below is commented because the current implentation flow does not add up*/
|
|
869
|
+
/*Below is commented because the current implentation flow does not add up (PLEASE DON'T DELETE)*/
|
|
638
870
|
// try {
|
|
639
871
|
// const document = await this.RepositoryBase.findOne({
|
|
640
872
|
// where: {
|
|
@@ -659,6 +891,75 @@ export default class Document extends AccountSystemEntity {
|
|
|
659
891
|
};
|
|
660
892
|
}
|
|
661
893
|
|
|
894
|
+
/**
|
|
895
|
+
* Returns generated HTML & PDF Receipt Media
|
|
896
|
+
*
|
|
897
|
+
* @remarks - This function also create each media element in common_Media table
|
|
898
|
+
*
|
|
899
|
+
* @see DocumentFileInHTML - for implementation
|
|
900
|
+
*
|
|
901
|
+
* @param userId - userId that trigger this function
|
|
902
|
+
*
|
|
903
|
+
* @returns {MediasModel} - HTML & PDF Media
|
|
904
|
+
*/
|
|
905
|
+
async generateReceipt(
|
|
906
|
+
userId?: string,
|
|
907
|
+
customer?: FinanceCustomerBase,
|
|
908
|
+
): Promise<{
|
|
909
|
+
HTMLMedia: MediasModel;
|
|
910
|
+
PDFMedia: MediasModel;
|
|
911
|
+
}> {
|
|
912
|
+
const media: Medias = new Medias(
|
|
913
|
+
Document._MediaRepository,
|
|
914
|
+
this.mediaCommonService,
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
/* insert FileStream on htmlPayload */
|
|
918
|
+
const htmlPayload: InternalMediaDto = {
|
|
919
|
+
ObjectId: this.DocNo,
|
|
920
|
+
ObjectType: this.DocType,
|
|
921
|
+
Type: MediaType.Document,
|
|
922
|
+
FileName: `${this.DocType}-${this.DocNo}`,
|
|
923
|
+
FileStream: await Document.DEFAULT_INVOICE_TEMPLATE_HTML(),
|
|
924
|
+
FileExtension: 'html',
|
|
925
|
+
Title: `${this.DocType}-${this.DocNo}.html`,
|
|
926
|
+
Description: `HTML ${this.DocType}`,
|
|
927
|
+
IsEncryptedYN: 'N',
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
/* create HTML media of the generated HTML */
|
|
931
|
+
const HTMLInvoiceMedia = await media.postInternal(
|
|
932
|
+
await Document.DEFAULT_INVOICE_TEMPLATE_HTML(),
|
|
933
|
+
htmlPayload,
|
|
934
|
+
userId ?? 'System',
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
/* insert FileStream on pdfPayload */
|
|
938
|
+
const pdfPayload: InternalMediaDto = {
|
|
939
|
+
ObjectId: this.DocNo,
|
|
940
|
+
ObjectType: this.DocType,
|
|
941
|
+
Type: MediaType.Document,
|
|
942
|
+
FileName: `${this.DocType}-${this.DocNo}`,
|
|
943
|
+
FileStream: await this.DEFAULT_RECEIPT_TEMPLATE_PDF(customer),
|
|
944
|
+
FileExtension: 'pdf',
|
|
945
|
+
Title: `${this.DocType}-${this.DocNo}.pdf`,
|
|
946
|
+
Description: `PDF ${this.DocType}`,
|
|
947
|
+
IsEncryptedYN: 'N',
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
/* create PDF media from the converted HTML */
|
|
951
|
+
const PDFInvoiceMedia = await media.postInternal(
|
|
952
|
+
await this.DEFAULT_RECEIPT_TEMPLATE_PDF(customer),
|
|
953
|
+
pdfPayload,
|
|
954
|
+
userId ?? 'System',
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
return {
|
|
958
|
+
HTMLMedia: HTMLInvoiceMedia,
|
|
959
|
+
PDFMedia: PDFInvoiceMedia,
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
|
|
662
963
|
async generateCreditNote(userId?: string, customer?: FinanceCustomerBase) {
|
|
663
964
|
console.log(userId, customer, '<< generateCreditNote');
|
|
664
965
|
}
|