diff --git a/fonts/Roboto-Black.ttf b/fonts/Roboto-Black.ttf new file mode 100644 index 0000000..58fa175 Binary files /dev/null and b/fonts/Roboto-Black.ttf differ diff --git a/fonts/Roboto-BlackItalic.ttf b/fonts/Roboto-BlackItalic.ttf new file mode 100644 index 0000000..0a4dfd0 Binary files /dev/null and b/fonts/Roboto-BlackItalic.ttf differ diff --git a/fonts/Roboto-Bold.ttf b/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000..e64db79 Binary files /dev/null and b/fonts/Roboto-Bold.ttf differ diff --git a/fonts/Roboto-BoldItalic.ttf b/fonts/Roboto-BoldItalic.ttf new file mode 100644 index 0000000..5e39ae9 Binary files /dev/null and b/fonts/Roboto-BoldItalic.ttf differ diff --git a/fonts/Roboto-Italic.ttf b/fonts/Roboto-Italic.ttf new file mode 100644 index 0000000..65498ee Binary files /dev/null and b/fonts/Roboto-Italic.ttf differ diff --git a/fonts/Roboto-Light.ttf b/fonts/Roboto-Light.ttf new file mode 100644 index 0000000..a7e0284 Binary files /dev/null and b/fonts/Roboto-Light.ttf differ diff --git a/fonts/Roboto-LightItalic.ttf b/fonts/Roboto-LightItalic.ttf new file mode 100644 index 0000000..867b76d Binary files /dev/null and b/fonts/Roboto-LightItalic.ttf differ diff --git a/fonts/Roboto-Medium.ttf b/fonts/Roboto-Medium.ttf new file mode 100644 index 0000000..0707e15 Binary files /dev/null and b/fonts/Roboto-Medium.ttf differ diff --git a/fonts/Roboto-MediumItalic.ttf b/fonts/Roboto-MediumItalic.ttf new file mode 100644 index 0000000..4e3bf0d Binary files /dev/null and b/fonts/Roboto-MediumItalic.ttf differ diff --git a/fonts/Roboto-Regular.ttf b/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..2d116d9 Binary files /dev/null and b/fonts/Roboto-Regular.ttf differ diff --git a/fonts/Roboto-Thin.ttf b/fonts/Roboto-Thin.ttf new file mode 100644 index 0000000..ab68508 Binary files /dev/null and b/fonts/Roboto-Thin.ttf differ diff --git a/fonts/Roboto-ThinItalic.ttf b/fonts/Roboto-ThinItalic.ttf new file mode 100644 index 0000000..b2c3933 Binary files /dev/null and b/fonts/Roboto-ThinItalic.ttf differ diff --git a/image/logo.jpeg b/image/logo.jpeg new file mode 100644 index 0000000..d46fdff Binary files /dev/null and b/image/logo.jpeg differ diff --git a/package.json b/package.json index eeee924..b20c84e 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "moment": "^2.30.1", "nano": "^10.1.3", "nodemailer": "^6.9.14", + "pdfmake": "^0.2.10", "pg": "^8.11.5", "plop": "^4.0.1", "reflect-metadata": "^0.2.0", diff --git a/src/app.module.ts b/src/app.module.ts index 652dec6..920256f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -70,6 +70,7 @@ import { BannerModule } from './modules/web-information/banner/banner.module'; import { BannerModel } from './modules/web-information/banner/data/models/banner.model'; import { MailModule } from './modules/configuration/mail/mail.module'; import { PosLogModel } from './modules/configuration/log/data/models/pos-log.model'; +import { ExportModule } from './modules/configuration/export/export.module'; @Module({ imports: [ @@ -123,6 +124,7 @@ import { PosLogModel } from './modules/configuration/log/data/models/pos-log.mod CqrsModule, CouchModule, CronModule, + ExportModule, GoogleCalendarModule, LogModule, MailModule, diff --git a/src/database/migrations/1722693550579-add-column-to-transactions-table.ts b/src/database/migrations/1722693550579-add-column-to-transactions-table.ts new file mode 100644 index 0000000..a80690a --- /dev/null +++ b/src/database/migrations/1722693550579-add-column-to-transactions-table.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddColumnToTransactionsTable1722693550579 + implements MigrationInterface +{ + name = 'AddColumnToTransactionsTable1722693550579'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "transactions" ADD "booking_date_before" date`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "transactions" DROP COLUMN "booking_date_before"`, + ); + } +} diff --git a/src/modules/configuration/constant/infrastructure/constant.controller.ts b/src/modules/configuration/constant/infrastructure/constant.controller.ts index fd0e04b..06a851b 100644 --- a/src/modules/configuration/constant/infrastructure/constant.controller.ts +++ b/src/modules/configuration/constant/infrastructure/constant.controller.ts @@ -10,6 +10,7 @@ import { RefundType, } from 'src/modules/transaction/refund/constants'; import { GateType } from 'src/modules/web-information/gate/constants'; +import { InvoiceType } from '../../export/constants'; @ApiTags('configuration - constant') @Controller('v1/constant') @@ -64,4 +65,9 @@ export class ConstantController { async gateType(): Promise { return Object.values(GateType); } + + @Get('invoice-type') + async invoiceType(): Promise { + return Object.values(InvoiceType); + } } diff --git a/src/modules/configuration/export/constants.ts b/src/modules/configuration/export/constants.ts new file mode 100644 index 0000000..41fb902 --- /dev/null +++ b/src/modules/configuration/export/constants.ts @@ -0,0 +1,10 @@ +export enum InvoiceType { + BOOKING_INVOICE = 'this is your invoice', + PAYMENT_CONFIRMATION = 'payment confirmation', + INVOICE_EXPIRED = 'invoice has expired', + REFUND_REQUEST = 'your refund request', + REFUND_CONFIRMATION = 'refund confirmation', + BOOKING_DATE_CHANGE = 'booking date change', +} + +export const PhoneNumber = '088'; diff --git a/src/modules/configuration/export/domain/managers/pdf-make.manager.ts b/src/modules/configuration/export/domain/managers/pdf-make.manager.ts new file mode 100644 index 0000000..b33065f --- /dev/null +++ b/src/modules/configuration/export/domain/managers/pdf-make.manager.ts @@ -0,0 +1,55 @@ +import { Injectable } from '@nestjs/common'; +import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; +import { EventTopics } from 'src/core/strings/constants/interface.constants'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; +import { GeneratePdf } from '../templates/helpers/generate-pdf.helper'; + +@Injectable() +export class PdfMakeManager extends BaseCustomManager { + get entityTarget(): any { + return TransactionModel; + } + + get eventTopics(): EventTopics[] { + return []; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async process(): Promise { + try { + const transaction = await this.dataService.getOneByOptions({ + where: { + id: this.data.id, + }, + relations: ['items'], + }); + const banks = await this.dataServiceFirstOpt.getManyByOptions({ + where: { + status: STATUS.ACTIVE, + }, + }); + + this.result = GeneratePdf(transaction, this.data.invoice_type, banks); + } catch (error) { + console.log(error, 'generate pdf'); + } + return; + } + + async afterProcess(): Promise { + return; + } + + getResult() { + return this.result; + } +} diff --git a/src/modules/configuration/export/domain/templates/helpers/generate-pdf.helper.ts b/src/modules/configuration/export/domain/templates/helpers/generate-pdf.helper.ts new file mode 100644 index 0000000..aab5da3 --- /dev/null +++ b/src/modules/configuration/export/domain/templates/helpers/generate-pdf.helper.ts @@ -0,0 +1,43 @@ +import { InvoiceTempalte } from '../invoice.template'; +const PdfPrinter = require('pdfmake'); +const { PassThrough } = require('stream'); + +export async function GeneratePdf(transaction, invoiceType, banks) { + var fonts = { + Roboto: { + normal: './fonts/Roboto-Regular.ttf', + bold: './fonts/Roboto-Medium.ttf', + italics: './fonts/Roboto-Italic.ttf', + bolditalics: './fonts/Roboto-MediumItalic.ttf', + }, + }; + + const printer = new PdfPrinter(fonts); + + const docDefinition = InvoiceTempalte(transaction, invoiceType, banks); + + const createPdfBuffer = (docDefinition) => { + return new Promise((resolve, reject) => { + const pdfDoc = printer.createPdfKitDocument(docDefinition); + const chunks = []; + const stream = new PassThrough(); + + pdfDoc.pipe(stream); + pdfDoc.end(); + + stream.on('data', (chunk) => { + chunks.push(chunk); + }); + + stream.on('end', () => { + const pdfBuffer = Buffer.concat(chunks); + resolve(pdfBuffer); + }); + + stream.on('error', reject); + }); + }; + + const pdfBuffer = await createPdfBuffer(docDefinition); + return pdfBuffer; +} diff --git a/src/modules/configuration/export/domain/templates/helpers/invoice-mapping.helper.ts b/src/modules/configuration/export/domain/templates/helpers/invoice-mapping.helper.ts new file mode 100644 index 0000000..904f3c4 --- /dev/null +++ b/src/modules/configuration/export/domain/templates/helpers/invoice-mapping.helper.ts @@ -0,0 +1,443 @@ +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; +import { InvoiceType, PhoneNumber } from '../../../constants'; +import { PaymentMethodEntity } from 'src/modules/transaction/payment-method/domain/entities/payment-method.entity'; + +export function mappingHeader(transaction: TransactionEntity, invoiceType) { + return [ + { + stack: [ + { + alignment: 'center', + columns: [ + { + width: 100, + image: 'data', + }, + { + width: 'auto', + text: 'have', + fontSize: 50, + bold: true, + color: '#169f54', + padding: [0, 0, 0, 0], + margin: [0, 0, 0, 0], + alignment: 'center', + }, + { + width: 'auto', + text: 'fun', + fontSize: 50, + bold: true, + color: '#61c4eb', + padding: [0, 0, 0, 0], + margin: [0, 0, 0, 0], + // alignment: "center" + }, + ], + }, + { + text: invoiceType.toUpperCase(), + fontSize: 30, + alignment: 'center', + }, + { + columns: [ + { + text: transaction.invoice_code, + }, + { + text: new Date(transaction.booking_date).toDateString(), + alignment: 'right', + }, + ], + }, + ], + }, + { + width: 150, + text: 'Jl, Kolonel Masturi No.KM. 11, \n Kertawangi, Kec. Cisatua, \n Kab. Bandung Barat, \n Jawa Barat 40551 \n 0815-6380-8021', + // color: '#aaaaab', + // bold: true, + fontSize: 11, + margin: [0, 20, 0, 5], + }, + ]; +} + +export function mappingFooter() { + return [ + [ + { + text: 'we commits to prividing an educative, \n playful, and purposeful environment\n this is advantageous for all ages', + border: [false, true, false, false], + margin: [0, 5, 0, 5], + alignment: 'right', + }, + { + text: 'Thank \n You', + border: [false, true, false, false], + fontSize: 20, + bold: true, + color: '#169f54', + margin: [0, 5, 0, 5], + }, + ], + ]; +} + +export function mappingPrice( + transaction: TransactionEntity, + invoiceType: InvoiceType, +) { + const result = []; + const totalData = [ + InvoiceType.REFUND_CONFIRMATION, + InvoiceType.REFUND_REQUEST, + ].includes(invoiceType) + ? transaction['refund'].refund_total + : Number(transaction.payment_total).toLocaleString('id-ID', { + style: 'currency', + currency: 'IDR', + }); + const subTotalData = [ + InvoiceType.REFUND_CONFIRMATION, + InvoiceType.REFUND_REQUEST, + ].includes(invoiceType) + ? transaction['refund'].refund_total + : Number(transaction.payment_sub_total).toLocaleString('id-ID', { + style: 'currency', + currency: 'IDR', + }); + + const sub_total = [ + { + text: 'SUBTOTAL', + alignment: 'right', + bold: true, + margin: [0, 5, 0, 5], + }, + { + text: subTotalData, + margin: [0, 5, 0, 5], + }, + ]; + result.push(sub_total); + + if (Number(transaction.payment_discount_total ?? 0) > 0) { + const discount = [ + { + text: 'DISCOUNT', + bold: true, + alignment: 'right', + margin: [0, 5, 0, 5], + }, + { + text: Number(transaction.payment_discount_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + margin: [0, 5, 0, 5], + }, + ]; + result.push(discount); + } + + const total = [ + { + text: 'TOTAL', + bold: true, + border: [false, false, false, true], + alignment: 'right', + margin: [0, 5, 0, 5], + }, + { + text: totalData, + border: [false, false, false, true], + margin: [0, 5, 0, 5], + }, + ]; + result.push(total); + + return result; +} + +export function mappingItem(transaction, invoiceType: InvoiceType) { + const header = [ + { + text: 'ITEM', + border: [false, true, false, false], + margin: [0, 5, 0, 5], + bold: true, + textTransform: 'uppercase', + }, + { + text: 'QTY', + alignment: 'center', + border: [false, true, false, false], + margin: [0, 5, 0, 5], + bold: true, + textTransform: 'uppercase', + }, + { + text: 'PRICE', + alignment: 'center', + border: [false, true, false, false], + margin: [0, 5, 0, 5], + bold: true, + textTransform: 'uppercase', + }, + { + text: 'AMOUNT', + border: [false, true, false, false], + alignment: 'center', + bold: true, + margin: [0, 5, 0, 5], + textTransform: 'uppercase', + }, + ]; + + const result = []; + + if ( + [InvoiceType.REFUND_CONFIRMATION, InvoiceType.REFUND_REQUEST].includes( + invoiceType, + ) + ) { + transaction.refund?.refund_items + ?.filter((item) => Number(item.qty_refund) > 0) + .forEach((item) => { + const dataRow = [ + { + text: item.transaction_item.item_name, + margin: [0, 5, 0, 5], + alignment: 'left', + }, + { + text: item.qty_refund, + margin: [0, 5, 0, 5], + alignment: 'center', + }, + { + text: Number(item.transaction_item.total_price).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + margin: [0, 5, 0, 5], + alignment: 'right', + }, + { + text: Number(item.refund_total).toLocaleString('id-ID', { + style: 'currency', + currency: 'IDR', + }), + alignment: 'right', + margin: [0, 5, 0, 5], + }, + ]; + result.push(dataRow); + }); + } else { + transaction.items.forEach((item) => { + const dataRow = [ + { + text: item.item_name, + margin: [0, 5, 0, 5], + alignment: 'left', + }, + { + text: item.qty, + margin: [0, 5, 0, 5], + alignment: 'center', + }, + { + text: Number(item.item_price).toLocaleString('id-ID', { + style: 'currency', + currency: 'IDR', + }), + margin: [0, 5, 0, 5], + alignment: 'right', + }, + { + text: Number(item.total_price).toLocaleString('id-ID', { + style: 'currency', + currency: 'IDR', + }), + alignment: 'right', + margin: [0, 5, 0, 5], + }, + ]; + result.push(dataRow); + }); + } + + const body = [header, ...result]; + + return body; +} + +export function mappingBody( + transaction: TransactionEntity, + invoiceType: InvoiceType, +) { + // booking date change information + if (invoiceType == InvoiceType.BOOKING_DATE_CHANGE) { + return [ + "Great news! We've successfully updated your booking date as per your request. \n We're exited to accommodate your new plans and ensure everything goes smoothly \n\n", + 'Here are your updated booking details:', + { + text: `\n\n Original Booking Date: ${new Date( + transaction['booking_date_before'], + ).toDateString()} \n New Booking Date: ${new Date( + transaction.booking_date, + ).toDateString()} \n\n`, + bold: true, + }, + "Here's a quick recap of your order :", + ]; + } + + // booking invoice + else if (invoiceType == InvoiceType.BOOKING_INVOICE) { + return [ + "Thank you for choosing us! We're absolutely thrilled and can't wait to embark on this exiting day with you. See you soon for fun times ahead!", + ]; + } else if (invoiceType == InvoiceType.PAYMENT_CONFIRMATION) { + // booking payment confirmation + return [ + 'We are exited to inform you that your payment has been successfully received! \n', + 'Attached to this email, you will find your confirmation receipt. \n', + 'Please keep this safe as you will need to show it at the entrance upon your arrival. \n', + "It's your golden ticket to all the fun and excitement awaiting you \n\n", + "Here's a quick recap: \n", + ]; + } + + // expired information invoice + // else if (invoiceType == InvoiceType.INVOICE_EXPIRED) { + + // return [ + // "We hope this message finds you well!", + // "Uh-oh! it looks like your invoice, dated 15 Juli, has officially expired as of 15 Juli. But no worries, we can fix this together \n", + // "To keep the goof times rolling, our friendly support team is just a call away at \n", + // "0564645 \n\n", + // "Here are the detail of the expired invoice: " + + // ] + // } + + // refund information + else if (invoiceType == InvoiceType.REFUND_REQUEST) { + return [ + "We'ew trully sorry for any inconvenience that led to this request. \n", + "We've received your refund request for: \n", + ]; + } + + // refund confirmation + else if (invoiceType == InvoiceType.REFUND_CONFIRMATION) { + return [ + 'Good news! \n', + "We've successfully processed your refund for: \n", + ]; + } +} + +export function mappingBodyBottom( + transaction: TransactionEntity, + invoiceType: InvoiceType, + banks: PaymentMethodEntity[], +) { + if (invoiceType == InvoiceType.BOOKING_DATE_CHANGE) { + // booking date change information + return [ + "For your convenience, we've attached a new confirmation receipt reflecting these changes \n", + 'Please be sure to bring this updated receipt with you on the new date \n\n', + 'If you have any questions or need further assistance, our friendly support team is just a call away at \n\n', + PhoneNumber, + "\nThank you and we can't wait to see you and make sure you hove an amazing time!", + ]; + } else if (invoiceType == InvoiceType.BOOKING_INVOICE) { + // booking invoice + return [ + 'Just a friendly reminder that your invoice will expire on 24 Agustus 2024 \n', + 'To keep things running smoothly, please ensure your payment is completed before this data. \n', + '\nFor youe convenience, here is a list of our account details \n\n', + { + text: [ + banks.forEach((bank) => { + return { + text: `${bank.issuer_name} ${bank.account_number} a/n ${bank.account_name} \n`, + bold: true, + }; + }), + ], + }, + "\n Once you've made the payment, please kindly email or send the proof of payment so we can proceed with your booking promptly\n", + 'If you have any questions or need assistance, feel free to reach out to our support team at\n', + PhoneNumber, + ]; + } + + // booking payment confirmation + else if (invoiceType == InvoiceType.PAYMENT_CONFIRMATION) { + return [ + 'If you have any questions or need assistance, feel free to reach out to our support team at\n', + PhoneNumber, + "\nDon't forget to bring a smile and your confirmation receipt (attached) for a smooth entry. \n", + "We can't wait to see you and ensure you have an amazing time with us!", + ]; + } + + // expired information invoice + else if (invoiceType == InvoiceType.INVOICE_EXPIRED) { + return []; + } + + // refund information + else if (invoiceType == InvoiceType.REFUND_REQUEST) { + return [ + "Your satisfaction is important to us, and we're commited to resolving this as quickly as possible.\n", + 'Our team is already on it and will process your refund request promptly. \n', + "We'll keep you updated and notify you once the refund has been processed. \n", + "If you have any questions or need futher assistance, don't hestitate to reach out us at \n", + PhoneNumber, + '\n\n', + 'Thank you for your patience and understanding \n', + 'We appreciate your feedback and here to make things right!', + ]; + } + + // refund confirmation + else if (invoiceType == InvoiceType.REFUND_CONFIRMATION) { + return [ + 'Here are the details of your refund: \n\n', + { + text: `Transaction Number: ${transaction.invoice_code} \n`, + }, + { + text: `Refund Processed: ${transaction['refund'].refund_date}\n`, + }, + { + text: `Bank Account: ${transaction['refund'].bank_name} \n`, + }, + { + text: `Account Number: ${transaction['refund'].bank_account_number} \n`, + }, + { + text: `Account Name: ${transaction['refund'].bank_account_name} \n`, + }, + "\n We hope this helps make things righ, and we're he to assist if you need anything else. \n", + 'You should see the refund reflected in your account within 3 Business days \n\n', + 'Thank you for your patience and understanding\n', + 'If you have any questions or need assistance, feel free to reach out to our support team at \n', + PhoneNumber, + "\nWe're alyways here to help!", + ]; + } +} diff --git a/src/modules/configuration/export/domain/templates/invoice.template.ts b/src/modules/configuration/export/domain/templates/invoice.template.ts new file mode 100644 index 0000000..ba639dc --- /dev/null +++ b/src/modules/configuration/export/domain/templates/invoice.template.ts @@ -0,0 +1,215 @@ +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; +import { + mappingBody, + mappingBodyBottom, + mappingFooter, + mappingHeader, + mappingItem, + mappingPrice, +} from './helpers/invoice-mapping.helper'; +import { InvoiceType } from '../../constants'; +import { PaymentMethodEntity } from 'src/modules/transaction/payment-method/domain/entities/payment-method.entity'; +import * as fs from 'fs'; + +export function InvoiceTempalte( + transaction: TransactionEntity, + invoiceType: InvoiceType, + banks: PaymentMethodEntity[], +) { + const filePath = './image/logo.jpeg'; + const imageBase64 = fs.readFileSync(filePath).toString('base64'); + const imageUrl = `data:image/png;base64,${imageBase64}`; + + return { + content: [ + { + alignment: 'justify', + columns: mappingHeader(transaction, invoiceType), + }, + + '\n\n', + + // tipe booking date change tidak ada + { + columns: [ + { + text: `Dear, \n ${transaction.customer_name} \n ${ + transaction.customer_phone + } \n Booking Date: ${new Date( + transaction.booking_date, + ).toDateString()}`, + bold: true, + }, + ], + }, + + '\n', + { + text: mappingBody(transaction, invoiceType), + }, + '\n', + + { + layout: { + defaultBorder: false, + hLineWidth: function (i, node) { + return 1; + }, + vLineWidth: function (i, node) { + return 1; + }, + hLineColor: function (i, node) { + return i === 0 ? '#000000' : '#eaeaea'; + }, + vLineColor: function (i, node) { + return '#eaeaea'; + }, + hLineStyle: function (i, node) { + // if (i === 0 || i === node.table.body.length) { + return null; + //} + }, + // vLineStyle: function (i, node) { return {dash: { length: 10, space: 4 }}; }, + paddingLeft: function (i, node) { + return 10; + }, + paddingRight: function (i, node) { + return 10; + }, + paddingTop: function (i, node) { + return 3; + }, + paddingBottom: function (i, node) { + return 3; + }, + fillColor: function (rowIndex, node, columnIndex) { + return '#fff'; + }, + }, + table: { + widths: ['*', 'auto', 'auto', 100], + body: mappingItem(transaction, invoiceType), + }, + }, + '\n', + { + layout: { + defaultBorder: false, + hLineWidth: function (i, node) { + return 1; + }, + vLineWidth: function (i, node) { + return 1; + }, + hLineColor: function (i, node) { + return i === 3 ? '#000000' : '#eaeaea'; + }, + vLineColor: function (i, node) { + return '#eaeaea'; + }, + hLineStyle: function (i, node) { + // if (i === 0 || i === node.table.body.length) { + return null; + //} + }, + // vLineStyle: function (i, node) { return {dash: { length: 10, space: 4 }}; }, + paddingLeft: function (i, node) { + return 10; + }, + paddingRight: function (i, node) { + return 10; + }, + paddingTop: function (i, node) { + return 3; + }, + paddingBottom: function (i, node) { + return i === 2 ? 20 : 3; + }, + fillColor: function (rowIndex, node, columnIndex) { + return '#fff'; + }, + }, + table: { + headerRows: 1, + widths: ['*', 100], + body: mappingPrice(transaction, invoiceType), + }, + }, + + '\n', + { + text: mappingBodyBottom(transaction, invoiceType, banks), + }, + '\n', + + { + layout: { + defaultBorder: false, + hLineWidth: function (i, node) { + return 1; + }, + vLineWidth: function (i, node) { + return 1; + }, + hLineColor: function (i, node) { + return i === 0 ? '#000000' : '#eaeaea'; + }, + vLineColor: function (i, node) { + return '#eaeaea'; + }, + hLineStyle: function (i, node) { + // if (i === 0 || i === node.table.body.length) { + return null; + //} + }, + // vLineStyle: function (i, node) { return {dash: { length: 10, space: 4 }}; }, + paddingLeft: function (i, node) { + return 10; + }, + paddingRight: function (i, node) { + return 10; + }, + paddingTop: function (i, node) { + return 20; + }, + paddingBottom: function (i, node) { + return 3; + }, + fillColor: function (rowIndex, node, columnIndex) { + return '#fff'; + }, + }, + table: { + widths: ['*', 200], + body: mappingFooter(), + }, + }, + ], + styles: { + notesTitle: { + fontSize: 10, + bold: true, + margin: [0, 50, 0, 3], + }, + notesText: { + fontSize: 10, + }, + tableExample: { + margin: [0, 5, 0, 15], + headerRows: 1, + widths: ['*', 100], + }, + tableHeader: { + bold: true, + fontSize: 13, + color: 'black', + }, + }, + defaultStyle: { + columnGap: 20, + }, + images: { + data: imageUrl, + }, + }; +} diff --git a/src/modules/configuration/export/export.module.ts b/src/modules/configuration/export/export.module.ts new file mode 100644 index 0000000..e4ed0a6 --- /dev/null +++ b/src/modules/configuration/export/export.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { PdfMakeManager } from './domain/managers/pdf-make.manager'; +import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; +import { PaymentMethodDataService } from 'src/modules/transaction/payment-method/data/services/payment-method-data.service'; +import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; +import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model'; +import { PaymentMethodModel } from 'src/modules/transaction/payment-method/data/models/payment-method.model'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { CqrsModule } from '@nestjs/cqrs'; +import { TransactionTaxModel } from 'src/modules/transaction/transaction/data/models/transaction-tax.model'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature([TransactionModel], CONNECTION_NAME.DEFAULT), + CqrsModule, + ], + controllers: [], + providers: [PdfMakeManager], +}) +export class ExportModule {} diff --git a/src/modules/configuration/export/infrastructure/export.controller.ts b/src/modules/configuration/export/infrastructure/export.controller.ts new file mode 100644 index 0000000..75034c5 --- /dev/null +++ b/src/modules/configuration/export/infrastructure/export.controller.ts @@ -0,0 +1,16 @@ +import { Controller, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Public } from 'src/core/guards'; + +@ApiTags('export') +@Controller('export') +@Public(true) +export class ExportController { + constructor() {} + + // @Post('pdf/example') + // async exportPdf( + // ): Promise { + // return PdfMaker(); + // } +} diff --git a/src/modules/configuration/mail/domain/email-template/change-date-information.html b/src/modules/configuration/mail/domain/email-template/change-date-information.html index e69de29..c3827f5 100644 --- a/src/modules/configuration/mail/domain/email-template/change-date-information.html +++ b/src/modules/configuration/mail/domain/email-template/change-date-information.html @@ -0,0 +1,404 @@ + + + + + + + Email Confirmation + + + + + + + + + + +
  +
+ + + + + + +
+ + + + +
+

Dear,

+

{{customer_name}}

+

{{customer_phone}}

+ +

Great News! We've successfully updated your booking date as per your request.

+

We're excited to accommodate your new plans and ensure evertyhing goes smoothly.

+ +

Here are your updated booking details

+ Original Booking Date: {{booking_date_before}} + New Booking Date: {{booking_date}} + +

For yout convenience, we've attached a new confirmation receipt reflecting these changes.

+

Please be sure to bring this updated receipt with you on the new date

+ +

To keep the good times rolling, our friendly support team is just a call away at

+ {{phone_cs}} +
+ +

Thank you and we can't wait to see you and make sure you have an amazing time!

+ +

+ Best Regrads,
+ WEplayground +
+
+
+
 
+ + + \ No newline at end of file diff --git a/src/modules/configuration/mail/domain/email-template/payment-confirmation-bank.html b/src/modules/configuration/mail/domain/email-template/invoice-bank.html similarity index 84% rename from src/modules/configuration/mail/domain/email-template/payment-confirmation-bank.html rename to src/modules/configuration/mail/domain/email-template/invoice-bank.html index daa023e..e20d983 100644 --- a/src/modules/configuration/mail/domain/email-template/payment-confirmation-bank.html +++ b/src/modules/configuration/mail/domain/email-template/invoice-bank.html @@ -4,7 +4,7 @@ - Email Ibunda + Email Confirmation + + + + + + + + + +
  +
+ + + + + + +
+ + + + +
+

Dear,

+

{{customer_name}}

+

{{customer_phone}}

+ +

We hope this message finds you well!

+ +

Uh-oh! it looks like your invoice, dated {{invoice_date}}, has officially expired as of {{expired_date}}

+

But no worries, we can fix this together!

+ +

To keep the good times rolling, our friendly support team is just a call away at

+ {{phone_cs}} + +

Here are the details of the expired invoice:

+

Booking Date: {{booking_date}}

+

Total Invoice: {{total_payment}}

+ +

+ Best Regrads,
+ WEplayground +
+
+
+
 
+ + + \ No newline at end of file diff --git a/src/modules/configuration/mail/domain/email-template/payment-confirmation-midtrans.html b/src/modules/configuration/mail/domain/email-template/invoice-midtrans.html similarity index 84% rename from src/modules/configuration/mail/domain/email-template/payment-confirmation-midtrans.html rename to src/modules/configuration/mail/domain/email-template/invoice-midtrans.html index b1cc797..589f005 100644 --- a/src/modules/configuration/mail/domain/email-template/payment-confirmation-midtrans.html +++ b/src/modules/configuration/mail/domain/email-template/invoice-midtrans.html @@ -4,7 +4,7 @@ - Email Ibunda + Email Confirmation + + + + + + + + + +
  +
+ + + + + + +
+ + + + +
+

Dear,

+

{{customer_name}}

+

{{customer_phone}}

+ +

We are excited to inform you that your payment has been successfully received!

+

Attached to this email, you will find your confirmatin receipt

+

Please keep this safe as you will need to show it at the entrance upon your arrival

+

It's your golden ticket to all the fun and excitement awaiting you!

+ +
+

Here's a quick recap:

+ +

Booking Date: {{booking_date}}

+

Invoice Code: {{invoice_code}}

+

Payment Date: {{payment_date}}

+

Payment Code: {{payment_code}}

+

Payment Via: {{payment_via}}

+

Account No: {{account_no}}

+

On Behalf Of: {{account_name}}

+
+ +

If you have any questions or need assistance, feel free to reach out to our support team at

+ {{phone_cs}} +
+ +

Font forget to bring a smile and your confirmation receipt (attached) for a smooth entry

+

We can't wait to see you and ensure you have an amazing time with us!

+ +

+ Best Regrads,
+ WEplayground +
+
+
+
 
+ + + \ No newline at end of file diff --git a/src/modules/configuration/mail/domain/email-template/refund-confirmation.html b/src/modules/configuration/mail/domain/email-template/refund-confirmation.html index e69de29..a938560 100644 --- a/src/modules/configuration/mail/domain/email-template/refund-confirmation.html +++ b/src/modules/configuration/mail/domain/email-template/refund-confirmation.html @@ -0,0 +1,415 @@ + + + + + + + Email Confirmation + + + + + + + + + + +
  +
+ + + + + + +
+ + + + +
+

Dear,

+

{{customer_name}}

+

{{customer_phone}}

+ +

Good News!

+

We've successfully processed your refund for:

+ +

Here are the details of your refund:

+

Transaction Date: {{booking_date}}

+

Transaction Code: {{invoice_code}}

+

Total Refund: {{refund.refund_total}}

+

{{{refund_items}}}

+ +

Transaction Number: {{invoice_code}}

+

Refund Processed Date: {{refund.refund_date}}

+

Bank Account: {{refund.bank_name}}

+

Account Number: {{refund.bank_account_number}}

+

Account Name: {{refund.bank_account_name}}

+ +

We hope this helps make things right, and we're here to assist if you need anything else

+

You should see the refund in your account within 3 business days

+ + +

Thank you for your patience and understanding

+

If you have any questions or need further assistance, don't hesitate to reach out us at

+ {{phone_cs}} + +
+ +

Thank you and we can't wait to see you and make sure you have an amazing time!

+ +

+ Best Regrads,
+ WEplayground +
+
+
+
 
+ + + \ No newline at end of file diff --git a/src/modules/configuration/mail/domain/email-template/refund-request.html b/src/modules/configuration/mail/domain/email-template/refund-request.html index e69de29..1c36420 100644 --- a/src/modules/configuration/mail/domain/email-template/refund-request.html +++ b/src/modules/configuration/mail/domain/email-template/refund-request.html @@ -0,0 +1,409 @@ + + + + + + + Email Confirmation + + + + + + + + + + +
  +
+ + + + + + +
+ + + + +
+

Dear,

+

{{customer_name}}

+

{{customer_phone}}

+ +

We're trully sorry for any inconvenience that led to this request

+

We've received your refund request for :

+ +

Transaction Date: {{booking_date}}

+

Transaction Code: {{invoice_code}}

+

Refund Code: {{refund.code}}

+

Total Refund: {{refund.refund_total}}

+

{{{refund_items}}}

+ +

Your satisfaction is important to us, and we're commited to resolving this as quickly as possible

+

Our team is already on it and will process your refund request promptly

+

We'll keep you updated and notify you once the refund has been processed

+ +

If you have any questions or need assistance, feel free to reach out to our support team at

+ {{phone_cs}} + +
+ +

Thank you for your patience and understanding

+

We appriciate your feedback and are here to make things right

+ +

+ Best Regrads,
+ WEplayground +
+
+
+
 
+ + + \ No newline at end of file diff --git a/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts b/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts index e3fb8ad..30955b4 100644 --- a/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts +++ b/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts @@ -4,9 +4,19 @@ import { PaymentMethodDataService } from 'src/modules/transaction/payment-method import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; import { TransactionChangeStatusEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-change-status.event'; import { sendEmail } from '../helpers/send-email.helper'; -import { TransactionPaymentType } from 'src/modules/transaction/transaction/constants'; +import { InvoiceType } from 'src/modules/configuration/export/constants'; +import { GeneratePdf } from 'src/modules/configuration/export/domain/templates/helpers/generate-pdf.helper'; +import { TransactionUpdatedEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-updated.event'; +import { RefundChangeStatusEvent } from 'src/modules/transaction/refund/domain/entities/event/refund-change-status.event'; +import { RefundCreatedEvent } from 'src/modules/transaction/refund/domain/entities/event/refund-created.event'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; -@EventsHandler(TransactionChangeStatusEvent) +@EventsHandler( + TransactionChangeStatusEvent, + TransactionUpdatedEvent, + RefundChangeStatusEvent, + RefundCreatedEvent, +) export class PaymentTransactionHandler implements IEventHandler { @@ -16,46 +26,252 @@ export class PaymentTransactionHandler ) {} async handle(event: TransactionChangeStatusEvent) { - const data_id = event.data.id; - const old_data = event.data.old; - const current_data = event.data.data; - let payments = []; + try { + const old_data = event.data.old; + const current_data = event.data.data; + const data_id = current_data.transaction_id ?? event.data.id; + const from_refund = event.data.module == TABLE_NAME.REFUND; - if ( - old_data.status == STATUS.DRAFT && - current_data.status == STATUS.PENDING && - current_data.payment_type != TransactionPaymentType.COUNTER && - !!current_data.customer_email - ) { - if (current_data.payment_type != TransactionPaymentType.MIDTRANS) { - payments = await this.paymentService.getManyByOptions({ - where: { - status: STATUS.ACTIVE, - }, - }); - } + const payments = await this.paymentService.getManyByOptions({ + where: { + status: STATUS.ACTIVE, + }, + }); const transaction = await this.dataService.getOneByOptions({ where: { id: data_id, }, - relations: ['items'], + relations: [ + 'items', + 'refunds', + 'refunds.refund_items', + 'refunds.refund_items.transaction_item', + ], }); - try { + Object.assign(transaction, { + booking_date: new Date(transaction.booking_date).toDateString(), + booking_date_before: new Date( + transaction.booking_date_before, + ).toDateString(), + email: transaction.customer_email, + payment_methods: payments, + }); + + const refund = transaction?.['refunds']?.find( + (refund) => ![STATUS.CANCEL].includes(refund.status), + ); + if (refund) { + Object.assign(refund, { + refund_date: new Date(refund?.refund_date).toDateString(), + request_date: new Date(refund?.request_date).toDateString(), + refund_total: Number(refund?.refund_total).toLocaleString('id-ID', { + style: 'currency', + currency: 'IDR', + }), + }); + + Object.assign(transaction, { + refund: refund, + }); + } + + if (transaction?.['refund']?.refund_items.length > 0) { + Object.assign(transaction, { + refund_items: ` +

Refund Items:

+
    + ${transaction?.['refund']?.refund_items + ?.filter((item) => Number(item.qty_refund) > 0) + .map((item) => { + return ` +
  • ${item.qty_refund} ${item.transaction_item.item_name}
  • + `; + })} +
`, + }); + } + + if (!transaction.customer_email) return; + + // refund request + if ( + from_refund && + transaction['refund'] && + [STATUS.DRAFT].includes(transaction['refund'].status) + ) { + const pdf = await GeneratePdf( + transaction, + InvoiceType.REFUND_REQUEST, + payments, + ); sendEmail( [ { ...transaction, - email: transaction.customer_email, - payment_methods: payments, + payment_total: Number(transaction.payment_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), }, ], - 'Payment Confirmation', + InvoiceType.REFUND_REQUEST, + pdf, ); - } catch (error) { - console.log(error); } + + // refund confirmation + else if ( + from_refund && + transaction['refund'] && + transaction['refund'].status == STATUS.REFUNDED + ) { + const pdf = await GeneratePdf( + transaction, + InvoiceType.REFUND_CONFIRMATION, + payments, + ); + sendEmail( + [ + { + ...transaction, + payment_total: Number(transaction.payment_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + }, + ], + InvoiceType.REFUND_CONFIRMATION, + pdf, + ); + } + + // payment settled + else if ( + !from_refund && + old_data.status != current_data.status && + [STATUS.ACTIVE, STATUS.SETTLED].includes(current_data.status) + ) { + const pdf = await GeneratePdf( + transaction, + InvoiceType.PAYMENT_CONFIRMATION, + payments, + ); + sendEmail( + [ + { + ...transaction, + payment_total: Number(transaction.payment_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + }, + ], + InvoiceType.PAYMENT_CONFIRMATION, + pdf, + ); + } + + // payment confirm to pending + else if ( + !from_refund && + old_data.status != current_data.status && + [STATUS.PENDING].includes(current_data.status) + ) { + const pdf = await GeneratePdf( + transaction, + InvoiceType.BOOKING_INVOICE, + payments, + ); + sendEmail( + [ + { + ...transaction, + payment_total: Number(transaction.payment_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + }, + ], + InvoiceType.BOOKING_INVOICE, + pdf, + ); + } + + // payment expired + else if ( + !from_refund && + old_data.status != current_data.status && + [STATUS.PENDING].includes(current_data.status) + ) { + const pdf = await GeneratePdf( + transaction, + InvoiceType.INVOICE_EXPIRED, + payments, + ); + sendEmail( + [ + { + ...transaction, + payment_total: Number(transaction.payment_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + }, + ], + InvoiceType.INVOICE_EXPIRED, + pdf, + ); + } + + // change booking date + else if ( + !from_refund && + old_data.booking_date != current_data.booking_date && + [STATUS.SETTLED, STATUS.ACTIVE, STATUS.PENDING].includes( + current_data.status, + ) + ) { + const pdf = await GeneratePdf( + transaction, + InvoiceType.BOOKING_DATE_CHANGE, + payments, + ); + sendEmail( + [ + { + ...transaction, + payment_total: Number(transaction.payment_total).toLocaleString( + 'id-ID', + { + style: 'currency', + currency: 'IDR', + }, + ), + }, + ], + InvoiceType.BOOKING_DATE_CHANGE, + pdf, + ); + } + } catch (error) { + console.log(error); } } } diff --git a/src/modules/configuration/mail/domain/helpers/send-email.helper.ts b/src/modules/configuration/mail/domain/helpers/send-email.helper.ts index bae519c..0166d3a 100644 --- a/src/modules/configuration/mail/domain/helpers/send-email.helper.ts +++ b/src/modules/configuration/mail/domain/helpers/send-email.helper.ts @@ -3,8 +3,9 @@ import * as handlebars from 'handlebars'; import * as path from 'path'; import * as fs from 'fs'; import { TransactionPaymentType } from 'src/modules/transaction/transaction/constants'; +import { InvoiceType } from 'src/modules/configuration/export/constants'; -export async function sendEmail(receivers, subject) { +export async function sendEmail(receivers, invoiceType, attachment?) { const smtpTransport = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: process.env.EMAIL_POST, @@ -13,13 +14,10 @@ export async function sendEmail(receivers, subject) { pass: process.env.EMAIL_TOKEN, }, }); - let templateName = 'payment-confirmation-bank'; for (const receiver of receivers) { try { - if (receiver.payment_type == TransactionPaymentType.MIDTRANS) - templateName = 'payment-confirmation-midtrans'; - + const templateName = getTemplate(receiver.payment_type, invoiceType); let templatePath = path.join( __dirname, `../email-template/${templateName}.html`, @@ -33,9 +31,15 @@ export async function sendEmail(receivers, subject) { const emailContext = { from: process.env.EMAIL_SENDER ?? 'no-reply@weplayground.app', to: receiver.email, - subject: subject, + subject: invoiceType, html: htmlToSend, attachDataUrls: true, + attachments: [ + { + filename: `${invoiceType}.pdf`, + content: attachment, + }, + ], }; await new Promise((f) => setTimeout(f, 2000)); @@ -52,3 +56,24 @@ export async function sendEmail(receivers, subject) { } } } + +function getTemplate(transactionType, invoiceType) { + if (invoiceType == InvoiceType.BOOKING_DATE_CHANGE) { + return 'change-date-information'; + } else if (invoiceType == InvoiceType.INVOICE_EXPIRED) { + return 'invoice-expired'; + } else if (invoiceType == InvoiceType.REFUND_REQUEST) { + return 'refund-request'; + } else if (invoiceType == InvoiceType.REFUND_CONFIRMATION) { + return 'refund-confirmation'; + } else if (invoiceType == InvoiceType.PAYMENT_CONFIRMATION) { + return 'payment-confirmation'; + } else if ( + invoiceType == InvoiceType.BOOKING_INVOICE && + transactionType != TransactionPaymentType.MIDTRANS + ) { + return 'invoice-bank'; + } else if (invoiceType == InvoiceType.BOOKING_INVOICE) { + return 'invoice-midtrans'; + } +} diff --git a/src/modules/configuration/mail/mail.module.ts b/src/modules/configuration/mail/mail.module.ts index c45d49c..412b0c3 100644 --- a/src/modules/configuration/mail/mail.module.ts +++ b/src/modules/configuration/mail/mail.module.ts @@ -9,6 +9,7 @@ import { TransactionModel } from 'src/modules/transaction/transaction/data/model import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; import { PaymentTransactionHandler } from './domain/handlers/payment-transaction.handler'; import { MailTemplateController } from './infrastructure/mail.controller'; +import { PdfMakeManager } from '../export/domain/managers/pdf-make.manager'; @Module({ imports: [ @@ -21,6 +22,8 @@ import { MailTemplateController } from './infrastructure/mail.controller'; ], controllers: [MailTemplateController], providers: [ + PdfMakeManager, + PaymentTransactionHandler, PaymentMethodDataService, TransactionDataService, diff --git a/src/modules/transaction/transaction/data/models/transaction.model.ts b/src/modules/transaction/transaction/data/models/transaction.model.ts index 565b543..58bdba7 100644 --- a/src/modules/transaction/transaction/data/models/transaction.model.ts +++ b/src/modules/transaction/transaction/data/models/transaction.model.ts @@ -80,6 +80,9 @@ export class TransactionModel @Column('date', { name: 'booking_date', nullable: true }) booking_date: Date; + @Column('date', { name: 'booking_date_before', nullable: true }) + booking_date_before: Date; + @Column('date', { name: 'settlement_date', nullable: true }) settlement_date: Date; diff --git a/src/modules/transaction/transaction/domain/entities/transaction.entity.ts b/src/modules/transaction/transaction/domain/entities/transaction.entity.ts index 4b1ba0f..aa1814b 100644 --- a/src/modules/transaction/transaction/domain/entities/transaction.entity.ts +++ b/src/modules/transaction/transaction/domain/entities/transaction.entity.ts @@ -30,6 +30,7 @@ export interface TransactionEntity extends BaseStatusEntity { no_of_group: number; booking_date: Date; // tnaggal untuk booking + booking_date_before: Date; // tnaggal untuk booking settlement_date: Date; // tanggal status berubah menjadi settlement invoice_date: Date; // tanggal invoice terkirim diff --git a/src/modules/transaction/transaction/domain/usecases/managers/update-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/update-transaction.manager.ts index 5fb66e4..c124b17 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/update-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/update-transaction.manager.ts @@ -19,6 +19,13 @@ export class UpdateTransactionManager extends BaseUpdateManager { mappingRevertTransaction(this.data, TransactionType.ADMIN); + const changeDate = this.data.booking_date != this.oldData.booking_date; + + if (changeDate) { + Object.assign(this.data, { + booking_date_before: this.oldData.booking_date, + }); + } return; } diff --git a/src/modules/transaction/transaction/domain/usecases/transaction-data.orchestrator.ts b/src/modules/transaction/transaction/domain/usecases/transaction-data.orchestrator.ts index 257b3a8..f8c002c 100644 --- a/src/modules/transaction/transaction/domain/usecases/transaction-data.orchestrator.ts +++ b/src/modules/transaction/transaction/domain/usecases/transaction-data.orchestrator.ts @@ -15,7 +15,9 @@ import { BatchCancelTransactionManager } from './managers/batch-cancel-transacti import { ConfirmDataTransactionManager } from './managers/confirm-data-transaction.manager'; import { BatchConfirmDataTransactionManager } from './managers/batch-confirm-data-transaction.manager'; import { MidtransService } from 'src/modules/configuration/midtrans/data/services/midtrans.service'; -// import { InvoiceTransactionManager } from './managers/download-invoice-transaction.manager'; +import { PdfMakeManager } from 'src/modules/configuration/export/domain/managers/pdf-make.manager'; +import { PaymentMethodDataService } from 'src/modules/transaction/payment-method/data/services/payment-method-data.service'; +import { InvoiceType } from 'src/modules/configuration/export/constants'; @Injectable() export class TransactionDataOrchestrator { @@ -30,8 +32,9 @@ export class TransactionDataOrchestrator { private batchDeleteManager: BatchDeleteTransactionManager, private cancelManager: CancelTransactionManager, private batchCancelManager: BatchCancelTransactionManager, - // private invoiceManager: InvoiceTransactionManager, + private invoiceManager: PdfMakeManager, private serviceData: TransactionDataService, + private paymentMethodService: PaymentMethodDataService, private midtransService: MidtransService, ) {} @@ -49,14 +52,19 @@ export class TransactionDataOrchestrator { return this.updateManager.getResult(); } - // async invoice(dataId): Promise { - // this.invoiceManager.setData({ - // id: dataId, - // }); - // this.invoiceManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); - // await this.invoiceManager.execute(); - // return this.invoiceManager.getResult(); - // } + async invoice(dataId, invoiceType): Promise { + this.invoiceManager.setData({ + id: dataId, + invoice_type: invoiceType, + }); + this.invoiceManager.setService( + this.serviceData, + TABLE_NAME.TRANSACTION, + this.paymentMethodService, + ); + await this.invoiceManager.execute(); + return this.invoiceManager.getResult(); + } async delete(dataId): Promise { this.deleteManager.setData(dataId); diff --git a/src/modules/transaction/transaction/infrastructure/dto/donwload-pdf.dto.ts b/src/modules/transaction/transaction/infrastructure/dto/donwload-pdf.dto.ts new file mode 100644 index 0000000..b0d522f --- /dev/null +++ b/src/modules/transaction/transaction/infrastructure/dto/donwload-pdf.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, ValidateIf } from 'class-validator'; +import { InvoiceType } from 'src/modules/configuration/export/constants'; + +export class DownloadPdfDto { + @ApiProperty({ + type: 'string', + required: false, + description: `Select ${Object.values(InvoiceType)}`, + }) + @ValidateIf((body) => body.invoice_type) + @IsEnum(InvoiceType, { + message: `invoice type must be a valid enum ${JSON.stringify( + Object.values(InvoiceType), + )}`, + }) + invoice_type: InvoiceType; +} diff --git a/src/modules/transaction/transaction/infrastructure/transaction-data.controller.ts b/src/modules/transaction/transaction/infrastructure/transaction-data.controller.ts index 06e97dc..d9c7269 100644 --- a/src/modules/transaction/transaction/infrastructure/transaction-data.controller.ts +++ b/src/modules/transaction/transaction/infrastructure/transaction-data.controller.ts @@ -6,7 +6,9 @@ import { Patch, Post, Put, + Res, } from '@nestjs/common'; +import { Response } from 'express'; import { TransactionDataOrchestrator } from '../domain/usecases/transaction-data.orchestrator'; import { TransactionDto } from './dto/transaction.dto'; import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; @@ -15,6 +17,7 @@ import { TransactionEntity } from '../domain/entities/transaction.entity'; import { BatchResult } from 'src/core/response/domain/ok-response.interface'; import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'; import { Public } from 'src/core/guards'; +import { DownloadPdfDto } from './dto/donwload-pdf.dto'; @ApiTags(`${MODULE_NAME.TRANSACTION.split('-').join(' ')} - data`) @Controller(`v1/${MODULE_NAME.TRANSACTION}`) @@ -28,10 +31,18 @@ export class TransactionDataController { return await this.orchestrator.create(data); } - // @Put('/:id/invoice/download') - // async invoiceDownload(@Param('id') dataId: string): Promise { - // return await this.orchestrator.invoice(dataId); - // } + @Put('/:id/invoice/download') + async invoiceDownload( + @Param('id') dataId: string, + @Body() body: DownloadPdfDto, + @Res() res: Response, + ): Promise { + const data = await this.orchestrator.invoice(dataId, body.invoice_type); + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader('Content-Disposition', 'attachment; filename=invoice.pdf'); + res.send(data); + return res; + } @Put('/batch-delete') async batchDeleted(@Body() body: BatchIdsDto): Promise { diff --git a/src/modules/transaction/transaction/transaction.module.ts b/src/modules/transaction/transaction/transaction.module.ts index a626078..0965400 100644 --- a/src/modules/transaction/transaction/transaction.module.ts +++ b/src/modules/transaction/transaction/transaction.module.ts @@ -32,6 +32,9 @@ import { TaxModel } from '../tax/data/models/tax.model'; import { SettledTransactionHandler } from './domain/usecases/handlers/settled-transaction.handler'; import { RefundUpdatedHandler } from './domain/usecases/handlers/refund-update.handler'; import { MidtransCallbackHandler } from './domain/usecases/handlers/midtrans-transaction-callback.handler'; +import { PdfMakeManager } from 'src/modules/configuration/export/domain/managers/pdf-make.manager'; +import { PaymentMethodDataService } from '../payment-method/data/services/payment-method-data.service'; +import { PaymentMethodModel } from '../payment-method/data/models/payment-method.model'; @Module({ imports: [ @@ -43,6 +46,7 @@ import { MidtransCallbackHandler } from './domain/usecases/handlers/midtrans-tra TransactionTaxModel, TaxModel, SalesPriceFormulaModel, + PaymentMethodModel, ], CONNECTION_NAME.DEFAULT, ), @@ -64,6 +68,7 @@ import { MidtransCallbackHandler } from './domain/usecases/handlers/midtrans-tra BatchDeleteTransactionManager, BatchConfirmTransactionManager, CancelTransactionManager, + PdfMakeManager, BatchCancelTransactionManager, ConfirmDataTransactionManager, BatchConfirmDataTransactionManager, @@ -72,6 +77,7 @@ import { MidtransCallbackHandler } from './domain/usecases/handlers/midtrans-tra TransactionReadService, TaxDataService, SalesPriceFormulaDataService, + PaymentMethodDataService, TransactionDataOrchestrator, TransactionReadOrchestrator, diff --git a/yarn.lock b/yarn.lock index 640a173..b77f4e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -429,6 +429,43 @@ lodash.isundefined "^3.0.1" lodash.uniq "^4.5.0" +"@foliojs-fork/fontkit@^1.9.1": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz#94241c195bc6204157bc84c33f34bdc967eca9c3" + integrity sha512-IfB5EiIb+GZk+77TRB86AHroVaqfq8JRFlUbz0WEwsInyCG0epX2tCPOy+UfaWPju30DeVoUAXfzWXmhn753KA== + dependencies: + "@foliojs-fork/restructure" "^2.0.2" + brotli "^1.2.0" + clone "^1.0.4" + deep-equal "^1.0.0" + dfa "^1.2.0" + tiny-inflate "^1.0.2" + unicode-properties "^1.2.2" + unicode-trie "^2.0.0" + +"@foliojs-fork/linebreak@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@foliojs-fork/linebreak/-/linebreak-1.1.2.tgz#32fee03d5431fa73284373439e172e451ae1e2da" + integrity sha512-ZPohpxxbuKNE0l/5iBJnOAfUaMACwvUIKCvqtWGKIMv1lPYoNjYXRfhi9FeeV9McBkBLxsMFWTVVhHJA8cyzvg== + dependencies: + base64-js "1.3.1" + unicode-trie "^2.0.0" + +"@foliojs-fork/pdfkit@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@foliojs-fork/pdfkit/-/pdfkit-0.14.0.tgz#ed1868050edd2904284655f8dcddd56b49576c98" + integrity sha512-nMOiQAv6id89MT3tVTCgc7HxD5ZMANwio2o5yvs5sexQkC0KI3BLaLakpsrHmFfeGFAhqPmZATZGbJGXTUebpg== + dependencies: + "@foliojs-fork/fontkit" "^1.9.1" + "@foliojs-fork/linebreak" "^1.1.1" + crypto-js "^4.2.0" + png-js "^1.0.0" + +"@foliojs-fork/restructure@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4" + integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA== + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -1899,7 +1936,12 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -2008,6 +2050,13 @@ breadth-filter@^2.0.0: dependencies: object.entries "^1.0.4" +brotli@^1.2.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" + integrity sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg== + dependencies: + base64-js "^1.1.2" + browserslist@^4.21.10, browserslist@^4.22.2: version "4.23.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.2.tgz#244fe803641f1c19c28c48c4b6ec9736eb3d32ed" @@ -2085,7 +2134,7 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -call-bind@^1.0.7: +call-bind@^1.0.2, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -2331,7 +2380,7 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone@^1.0.2: +clone@^1.0.2, clone@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== @@ -2565,6 +2614,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + dayjs@^1.11.9, dayjs@^1.8.34: version "1.11.11" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" @@ -2599,6 +2653,18 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== +deep-equal@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" + integrity sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg== + dependencies: + is-arguments "^1.1.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + object-is "^1.1.5" + object-keys "^1.1.1" + regexp.prototype.flags "^1.5.1" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2691,6 +2757,11 @@ dezalgo@^1.0.4: asap "^2.0.0" wrappy "1" +dfa@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" + integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -3469,6 +3540,11 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -3762,6 +3838,13 @@ has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-tostringtag@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -3857,6 +3940,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -4004,6 +4094,14 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -4023,6 +4121,13 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.2" +is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -4092,6 +4197,14 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" @@ -5429,6 +5542,14 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5612,6 +5733,11 @@ package-json-from-dist@^1.0.0: resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== +pako@^0.2.5: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== + pako@~1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -5749,6 +5875,16 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pdfmake@^0.2.10: + version "0.2.10" + resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.10.tgz#a8a0ee8a5acca8f5d728e0dfe4db8be5f1b9ec6b" + integrity sha512-doipFnmE1UHSk+Z3wfQuVweVQqx2pE/Ns2G5gCqZmWwqjDj+mZHnZYH/ryXWoIfD+iVdZUAutgI/VHkTCN+Xrw== + dependencies: + "@foliojs-fork/linebreak" "^1.1.1" + "@foliojs-fork/pdfkit" "^0.14.0" + iconv-lite "^0.6.3" + xmldoc "^1.1.2" + pg-cloudflare@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" @@ -5881,6 +6017,11 @@ pluralize@8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +png-js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" + integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== + postgres-array@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" @@ -6100,6 +6241,16 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== +regexp.prototype.flags@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + relative-microtime@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/relative-microtime/-/relative-microtime-2.0.0.tgz#cceed2af095ecd72ea32011279c79e5fcc7de29b" @@ -6247,11 +6398,16 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -6345,6 +6501,16 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + setimmediate@^1.0.5, setimmediate@~1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -6765,6 +6931,11 @@ tiny-emitter@^2.1.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== +tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" + integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== + title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -7029,11 +7200,27 @@ unicode-byte-truncate@^1.0.0: is-integer "^1.0.6" unicode-substring "^0.1.0" +unicode-properties@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/unicode-properties/-/unicode-properties-1.4.1.tgz#96a9cffb7e619a0dc7368c28da27e05fc8f9be5f" + integrity sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg== + dependencies: + base64-js "^1.3.0" + unicode-trie "^2.0.0" + unicode-substring@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/unicode-substring/-/unicode-substring-0.1.0.tgz#6120ce3c390385dbcd0f60c32b9065c4181d4b36" integrity sha512-36Xaw9wXi7MB/3/EQZZHkZyyiRNa9i3k9YtPAz2KfqMVH2xutdXyMHn4Igarmnvr+wOrfWa/6njhY+jPpXN2EQ== +unicode-trie@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-2.0.0.tgz#8fd8845696e2e14a8b67d78fa9e0dd2cad62fec8" + integrity sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ== + dependencies: + pako "^0.2.5" + tiny-inflate "^1.0.0" + universalify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" @@ -7327,6 +7514,13 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmldoc@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.3.0.tgz#7823225b096c74036347c9ec5924d06b6a3cebab" + integrity sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng== + dependencies: + sax "^1.2.4" + xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"