@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="tm_right_footer">
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="tm_padd_15_20 tm_round_border">
191
- <p class="tm_mb5">
192
- <b class="tm_primary_color">Terms & Conditions:</b>
193
- </p>
194
- <ul class="tm_m0 tm_note_list tm_tnc_content">
195
- <li>
196
- All claims relating to quantity or shipping errors shall be
197
- waived by Buyer unless made in writing to Seller within thirty
198
- (30) days after delivery of goods to the address stated.
199
- </li>
200
- <li>
201
- Delivery dates are not guaranteed and Seller has no liability
202
- for damages that may be incurred due to any delay in shipment
203
- of goods hereunder. Taxes are excluded unless otherwise
204
- stated.
205
- </li>
206
- </ul>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomei/finance",
3
- "version": "0.4.19",
3
+ "version": "0.4.21",
4
4
  "description": "NestJS package for finance module",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -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 invoiceContent = `
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 invoiceHandle = await page.$('.tm_invoice_info');
355
- await invoiceHandle.evaluate((invoice, invoiceContent) => {
356
- invoice.innerHTML = invoiceContent;
357
- }, invoiceContent);
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.$('.tm_right_footer');
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>TBD</b></li>
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
  }