From fabc88c81ac33a6e559dc2b87b355f008dbc1958 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Tue, 3 Dec 2024 23:37:55 +0200 Subject: [PATCH] feat: add adjustment total in estimates, invoices, and receipts pdf templates --- .../Sales/Estimates/SaleEstimatesPdf.ts | 6 +-- .../src/services/Sales/Estimates/utils.ts | 5 ++- .../services/Sales/Invoices/SaleInvoicePdf.ts | 8 ++-- .../src/services/Sales/Invoices/utils.ts | 6 ++- .../Sales/Receipts/SaleReceiptsPdfService.ts | 10 +++-- .../src/services/Sales/Receipts/utils.ts | 9 ++--- .../Accounting/MakeJournal/utils.tsx | 1 + shared/pdf-templates/package.json | 2 +- .../src/components/EstimatePaperTemplate.tsx | 18 ++++++++- .../src/components/InvoicePaperTemplate.tsx | 39 +++++++++++++++---- .../src/components/ReceiptPaperTemplate.tsx | 16 ++++++++ .../renders/render-invoice-paper-template.tsx | 4 +- 12 files changed, 91 insertions(+), 33 deletions(-) diff --git a/packages/server/src/services/Sales/Estimates/SaleEstimatesPdf.ts b/packages/server/src/services/Sales/Estimates/SaleEstimatesPdf.ts index 65b965388..4cb6c8f3b 100644 --- a/packages/server/src/services/Sales/Estimates/SaleEstimatesPdf.ts +++ b/packages/server/src/services/Sales/Estimates/SaleEstimatesPdf.ts @@ -1,14 +1,12 @@ import { Inject, Service } from 'typedi'; import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy'; -import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable'; import { GetSaleEstimate } from './GetSaleEstimate'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { SaleEstimatePdfTemplate } from '../Invoices/SaleEstimatePdfTemplate'; import { transformEstimateToPdfTemplate } from './utils'; -import { EstimatePdfBrandingAttributes } from './constants'; import events from '@/subscribers/events'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; -import { renderEstimatePaperTemplateHtml } from '@bigcapital/pdf-templates'; +import { renderEstimatePaperTemplateHtml, EstimatePaperTemplateProps } from '@bigcapital/pdf-templates'; @Service() export class SaleEstimatesPdf { @@ -97,7 +95,7 @@ export class SaleEstimatesPdf { async getEstimateBrandingAttributes( tenantId: number, estimateId: number - ): Promise { + ): Promise { const { PdfTemplate } = this.tenancy.models(tenantId); const saleEstimate = await this.getSaleEstimate.getEstimate( tenantId, diff --git a/packages/server/src/services/Sales/Estimates/utils.ts b/packages/server/src/services/Sales/Estimates/utils.ts index c77029ba1..c5d100cd9 100644 --- a/packages/server/src/services/Sales/Estimates/utils.ts +++ b/packages/server/src/services/Sales/Estimates/utils.ts @@ -1,9 +1,9 @@ +import { EstimatePaperTemplateProps } from '@bigcapital/pdf-templates'; import { contactAddressTextFormat } from '@/utils/address-text-format'; -import { EstimatePdfBrandingAttributes } from './constants'; export const transformEstimateToPdfTemplate = ( estimate -): Partial => { +): Partial => { return { expirationDate: estimate.formattedExpirationDate, estimateNumebr: estimate.estimateNumber, @@ -17,6 +17,7 @@ export const transformEstimateToPdfTemplate = ( })), total: estimate.formattedSubtotal, subtotal: estimate.formattedSubtotal, + adjustment: estimate.adjustmentFormatted, customerNote: estimate.note, termsConditions: estimate.termsConditions, customerAddress: contactAddressTextFormat(estimate.customer), diff --git a/packages/server/src/services/Sales/Invoices/SaleInvoicePdf.ts b/packages/server/src/services/Sales/Invoices/SaleInvoicePdf.ts index 54ae28319..1c492b9ce 100644 --- a/packages/server/src/services/Sales/Invoices/SaleInvoicePdf.ts +++ b/packages/server/src/services/Sales/Invoices/SaleInvoicePdf.ts @@ -1,10 +1,12 @@ import { Inject, Service } from 'typedi'; -import { renderInvoicePaperTemplateHtml } from '@bigcapital/pdf-templates'; +import { + renderInvoicePaperTemplateHtml, + InvoicePaperTemplateProps, +} from '@bigcapital/pdf-templates'; import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy'; import { GetSaleInvoice } from './GetSaleInvoice'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { transformInvoiceToPdfTemplate } from './utils'; -import { InvoicePdfTemplateAttributes } from '@/interfaces'; import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; @@ -100,7 +102,7 @@ export class SaleInvoicePdf { async getInvoiceBrandingAttributes( tenantId: number, invoiceId: number - ): Promise { + ): Promise { const { PdfTemplate } = this.tenancy.models(tenantId); const invoice = await this.getInvoiceService.getSaleInvoice( diff --git a/packages/server/src/services/Sales/Invoices/utils.ts b/packages/server/src/services/Sales/Invoices/utils.ts index 2fcfce6f2..af8ba6145 100644 --- a/packages/server/src/services/Sales/Invoices/utils.ts +++ b/packages/server/src/services/Sales/Invoices/utils.ts @@ -1,6 +1,7 @@ import { pickBy } from 'lodash'; -import { InvoicePdfTemplateAttributes, ISaleInvoice } from '@/interfaces'; +import { ISaleInvoice } from '@/interfaces'; import { contactAddressTextFormat } from '@/utils/address-text-format'; +import { InvoicePaperTemplateProps } from '@bigcapital/pdf-templates'; export const mergePdfTemplateWithDefaultAttributes = ( brandingTemplate?: Record, @@ -18,7 +19,7 @@ export const mergePdfTemplateWithDefaultAttributes = ( export const transformInvoiceToPdfTemplate = ( invoice: ISaleInvoice -): Partial => { +): Partial => { return { dueDate: invoice.dueDateFormatted, dateIssue: invoice.invoiceDateFormatted, @@ -29,6 +30,7 @@ export const transformInvoiceToPdfTemplate = ( paymentMade: invoice.paymentAmountFormatted, dueAmount: invoice.dueAmountFormatted, discount: invoice.discountAmountFormatted, + adjustment: invoice.adjustmentFormatted, discountLabel: invoice.discountPercentageFormatted ? `Discount [${invoice.discountPercentageFormatted}]` : 'Discount', diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts index f8b6f57ea..2aab47659 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptsPdfService.ts @@ -1,13 +1,15 @@ import { Inject, Service } from 'typedi'; import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy'; +import { + renderReceiptPaperTemplateHtml, + ReceiptPaperTemplateProps, +} from '@bigcapital/pdf-templates'; import { GetSaleReceipt } from './GetSaleReceipt'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { SaleReceiptBrandingTemplate } from './SaleReceiptBrandingTemplate'; import { transformReceiptToBrandingTemplateAttributes } from './utils'; -import { ISaleReceiptBrandingTemplateAttributes } from '@/interfaces'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; -import { renderReceiptPaperTemplateHtml } from '@bigcapital/pdf-templates'; @Service() export class SaleReceiptsPdf { @@ -90,12 +92,12 @@ export class SaleReceiptsPdf { * Retrieves receipt branding attributes. * @param {number} tenantId * @param {number} receiptId - * @returns {Promise} + * @returns {Promise} */ public async getReceiptBrandingAttributes( tenantId: number, receiptId: number - ): Promise { + ): Promise { const { PdfTemplate } = this.tenancy.models(tenantId); const saleReceipt = await this.getSaleReceiptService.getSaleReceipt( diff --git a/packages/server/src/services/Sales/Receipts/utils.ts b/packages/server/src/services/Sales/Receipts/utils.ts index 2586c0b33..3d3bb8733 100644 --- a/packages/server/src/services/Sales/Receipts/utils.ts +++ b/packages/server/src/services/Sales/Receipts/utils.ts @@ -1,12 +1,10 @@ -import { - ISaleReceipt, - ISaleReceiptBrandingTemplateAttributes, -} from '@/interfaces'; +import { ISaleReceipt } from '@/interfaces'; import { contactAddressTextFormat } from '@/utils/address-text-format'; +import { ReceiptPaperTemplateProps } from '@bigcapital/pdf-templates'; export const transformReceiptToBrandingTemplateAttributes = ( saleReceipt: ISaleReceipt -): Partial => { +): Partial => { return { total: saleReceipt.totalFormatted, subtotal: saleReceipt.subtotalFormatted, @@ -23,6 +21,7 @@ export const transformReceiptToBrandingTemplateAttributes = ( discountLabel: saleReceipt.discountPercentageFormatted ? `Discount [${saleReceipt.discountPercentageFormatted}]` : 'Discount', + adjustment: saleReceipt.adjustmentFormatted, customerAddress: contactAddressTextFormat(saleReceipt.customer), }; }; diff --git a/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx b/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx index 38fa852e5..72d0e8023 100644 --- a/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx +++ b/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx @@ -239,6 +239,7 @@ export const useJournalTotals = () => { const totalDebit = safeSumBy(entries, 'debit'); const total = Math.max(totalCredit, totalDebit); + // Retrieves the formatted total money. const formattedTotal = React.useMemo( () => formattedAmount(total, currencyCode), diff --git a/shared/pdf-templates/package.json b/shared/pdf-templates/package.json index d636d9d37..6ebda5ccb 100644 --- a/shared/pdf-templates/package.json +++ b/shared/pdf-templates/package.json @@ -9,7 +9,7 @@ }, "main": "./dist/components.umd.js", "module": "./dist/components.es.js", - "types": "./dist/src/index.d.ts", + "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/src/index.d.ts", diff --git a/shared/pdf-templates/src/components/EstimatePaperTemplate.tsx b/shared/pdf-templates/src/components/EstimatePaperTemplate.tsx index 837ab30aa..e28a292a2 100644 --- a/shared/pdf-templates/src/components/EstimatePaperTemplate.tsx +++ b/shared/pdf-templates/src/components/EstimatePaperTemplate.tsx @@ -43,7 +43,7 @@ export interface EstimatePaperTemplateProps extends PaperTemplateProps { companyAddress?: string; billedToLabel?: string; - // Totals + // Total total?: string; showTotal?: boolean; totalLabel?: string; @@ -53,6 +53,11 @@ export interface EstimatePaperTemplateProps extends PaperTemplateProps { showDiscount?: boolean; discountLabel?: string; + // # Adjustment + adjustment?: string; + showAdjustment?: boolean; + adjustmentLabel?: string; + // # Subtotal subtotal?: string; showSubtotal?: boolean; @@ -117,6 +122,11 @@ export function EstimatePaperTemplate({ subtotalLabel = 'Subtotal', showSubtotal = true, + // # Adjustment + adjustment = '', + showAdjustment = true, + adjustmentLabel = 'Adjustment', + // # Customer Note showCustomerNote = true, customerNote = DefaultPdfTemplateStatement, @@ -240,6 +250,12 @@ export function EstimatePaperTemplate({ amount={discount} /> )} + {showAdjustment && adjustment && ( + + )} {showTotal && ( )} diff --git a/shared/pdf-templates/src/components/InvoicePaperTemplate.tsx b/shared/pdf-templates/src/components/InvoicePaperTemplate.tsx index f14561921..f2ff68e3a 100644 --- a/shared/pdf-templates/src/components/InvoicePaperTemplate.tsx +++ b/shared/pdf-templates/src/components/InvoicePaperTemplate.tsx @@ -1,3 +1,4 @@ +import { isEmpty } from 'lodash'; import { PaperTemplate, PaperTemplateProps, @@ -33,17 +34,21 @@ export interface InvoicePaperTemplateProps extends PaperTemplateProps { primaryColor?: string; secondaryColor?: string; + // Company showCompanyLogo?: boolean; companyLogoUri?: string; + // Invoice number showInvoiceNumber?: boolean; invoiceNumber?: string; invoiceNumberLabel?: string; + // Date of issue showDateIssue?: boolean; dateIssue?: string; dateIssueLabel?: string; + // Due date showDueDate?: boolean; dueDate?: string; dueDateLabel?: string; @@ -66,7 +71,7 @@ export interface InvoicePaperTemplateProps extends PaperTemplateProps { lineRateLabel?: string; lineTotalLabel?: string; - // Totals + // Total showTotal?: boolean; totalLabel?: string; total?: string; @@ -76,11 +81,17 @@ export interface InvoicePaperTemplateProps extends PaperTemplateProps { discountLabel?: string; discount?: string; + // Adjustment + showAdjustment?: boolean; + adjustmentLabel?: string; + adjustment?: string; + // Subtotal showSubtotal?: boolean; subtotalLabel?: string; subtotal?: string; + // Payment made showPaymentMade?: boolean; paymentMadeLabel?: string; paymentMade?: string; @@ -97,6 +108,7 @@ export interface InvoicePaperTemplateProps extends PaperTemplateProps { showTermsConditions?: boolean; termsConditions?: string; + // Statement statementLabel?: string; showStatement?: boolean; statement?: string; @@ -145,20 +157,24 @@ export function InvoicePaperTemplate({ totalLabel = 'Total', subtotalLabel = 'Subtotal', discountLabel = 'Discount', + adjustmentLabel = 'Adjustment', paymentMadeLabel = 'Payment Made', dueAmountLabel = 'Balance Due', // Totals showTotal = true, + total = '$662.75', + showSubtotal = true, showDiscount = true, showTaxes = true, showPaymentMade = true, showDueAmount = true, + showAdjustment = true, - total = '$662.75', subtotal = '630.00', discount = '0.00', + adjustment = '', paymentMade = '100.00', dueAmount = '$562.75', @@ -243,17 +259,18 @@ export function InvoicePaperTemplate({ accessor: (data) => ( {data.item} - + {data.description} ), thStyle: { width: '60%' }, }, - { label: lineQuantityLabel, accessor: 'quantity', align: 'right' }, + { + label: lineQuantityLabel, + accessor: 'quantity', + align: 'right', + }, { label: lineRateLabel, accessor: 'rate', align: 'right' }, { label: lineTotalLabel, accessor: 'total', align: 'right' }, ]} @@ -267,12 +284,18 @@ export function InvoicePaperTemplate({ border={PaperTemplateTotalBorder.Gray} /> )} - {showDiscount && ( + {showDiscount && !isEmpty(discount) && ( )} + {showAdjustment && !isEmpty(adjustment) && ( + + )} {showTaxes && ( <> {taxes.map((tax, index) => ( diff --git a/shared/pdf-templates/src/components/ReceiptPaperTemplate.tsx b/shared/pdf-templates/src/components/ReceiptPaperTemplate.tsx index 3144c15de..68a19c9e9 100644 --- a/shared/pdf-templates/src/components/ReceiptPaperTemplate.tsx +++ b/shared/pdf-templates/src/components/ReceiptPaperTemplate.tsx @@ -39,6 +39,11 @@ export interface ReceiptPaperTemplateProps extends PaperTemplateProps { showDiscount?: boolean; discountLabel?: string; + // # Adjustment + adjustment?: string; + showAdjustment?: boolean; + adjustmentLabel?: string; + // Total total?: string; showTotal?: boolean; @@ -111,6 +116,11 @@ export function ReceiptPaperTemplate({ discountLabel = 'Discount', showDiscount = true, + // # Adjustment + adjustment = '', + adjustmentLabel = 'Adjustment', + showAdjustment = true, + // # Subtotal subtotal = '1000/00', subtotalLabel = 'Subtotal', @@ -228,6 +238,12 @@ export function ReceiptPaperTemplate({ amount={discount} /> )} + {showAdjustment && adjustment && ( + + )} {showTotal && ( )} diff --git a/shared/pdf-templates/src/renders/render-invoice-paper-template.tsx b/shared/pdf-templates/src/renders/render-invoice-paper-template.tsx index 1ce9e2de1..84e035b29 100644 --- a/shared/pdf-templates/src/renders/render-invoice-paper-template.tsx +++ b/shared/pdf-templates/src/renders/render-invoice-paper-template.tsx @@ -8,8 +8,6 @@ export const renderInvoicePaperTemplateHtml = ( props: InvoicePaperTemplateProps ) => { return renderSSR( - + ); };