fix: credit note printing

This commit is contained in:
Ahmed Bouhuolia
2026-01-01 17:17:55 +02:00
parent ead4fc9b97
commit 0ef78a19fe
4 changed files with 302 additions and 11 deletions

View File

@@ -1,10 +1,10 @@
import { Inject, Injectable } from '@nestjs/common';
import { renderCreditNotePaperTemplateHtml } from '@bigcapital/pdf-templates';
import { GetCreditNoteService } from './GetCreditNote.service';
import { CreditNoteBrandingTemplate } from './CreditNoteBrandingTemplate.service';
import { transformCreditNoteToPdfTemplate } from '../utils';
import { CreditNote } from '../models/CreditNote';
import { ChromiumlyTenancy } from '@/modules/ChromiumlyTenancy/ChromiumlyTenancy.service';
import { TemplateInjectable } from '@/modules/TemplateInjectable/TemplateInjectable.service';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
import { CreditNotePdfTemplateAttributes } from '../types/CreditNotes.types';
@@ -15,7 +15,6 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
export class GetCreditNotePdf {
/**
* @param {ChromiumlyTenancy} chromiumlyTenancy - Chromiumly tenancy service.
* @param {TemplateInjectable} templateInjectable - Template injectable service.
* @param {GetCreditNote} getCreditNoteService - Get credit note service.
* @param {CreditNoteBrandingTemplate} creditNoteBrandingTemplate - Credit note branding template service.
* @param {EventEmitter2} eventPublisher - Event publisher service.
@@ -24,7 +23,6 @@ export class GetCreditNotePdf {
*/
constructor(
private readonly chromiumlyTenancy: ChromiumlyTenancy,
private readonly templateInjectable: TemplateInjectable,
private readonly getCreditNoteService: GetCreditNoteService,
private readonly creditNoteBrandingTemplate: CreditNoteBrandingTemplate,
private readonly eventPublisher: EventEmitter2,
@@ -36,23 +34,40 @@ export class GetCreditNotePdf {
private readonly pdfTemplateModel: TenantModelProxy<
typeof PdfTemplateModel
>,
) {}
) { }
/**
* Retrieves sale invoice pdf content.
* Retrieves credit note html content.
* @param {number} creditNoteId - Credit note id.
* @returns {Promise<string>}
*/
public async getCreditNoteHtml(creditNoteId: number): Promise<string> {
const brandingAttributes =
await this.getCreditNoteBrandingAttributes(creditNoteId);
// Map attributes to match the React component props
// The branding template returns companyLogoUri, but type may have companyLogo
const props = {
...brandingAttributes,
companyLogoUri:
(brandingAttributes as any).companyLogoUri ||
(brandingAttributes as any).companyLogo ||
'',
};
return renderCreditNotePaperTemplateHtml(props);
}
/**
* Retrieves credit note pdf content.
* @param {number} creditNoteId - Credit note id.
* @returns {Promise<[Buffer, string]>}
*/
public async getCreditNotePdf(
creditNoteId: number,
): Promise<[Buffer, string]> {
const brandingAttributes =
await this.getCreditNoteBrandingAttributes(creditNoteId);
const htmlContent = await this.templateInjectable.render(
'modules/credit-note-standard',
brandingAttributes,
);
const filename = await this.getCreditNoteFilename(creditNoteId);
const htmlContent = await this.getCreditNoteHtml(creditNoteId);
const document =
await this.chromiumlyTenancy.convertHtmlContent(htmlContent);

View File

@@ -0,0 +1,257 @@
import {
PaperTemplate,
PaperTemplateProps,
PaperTemplateTotalBorder,
} from './PaperTemplate';
import { Box } from '../lib/layout/Box';
import { Text } from '../lib/text/Text';
import { Stack } from '../lib/layout/Stack';
import { Group } from '../lib/layout/Group';
import {
DefaultPdfTemplateTerms,
DefaultPdfTemplateItemDescription,
DefaultPdfTemplateItemName,
DefaultPdfTemplateAddressBilledTo,
DefaultPdfTemplateAddressBilledFrom,
} from './_constants';
interface CreditNoteLine {
item?: string;
description?: string;
quantity?: string;
rate?: string;
total?: string;
}
export interface CreditNotePaperTemplateProps extends PaperTemplateProps {
primaryColor?: string;
secondaryColor?: string;
// Company
showCompanyLogo?: boolean;
companyLogoUri?: string;
companyName?: string;
// Credit Note number
showCreditNoteNumber?: boolean;
creditNoteNumebr?: string;
creditNoteNumberLabel?: string;
// Credit Note date
showCreditNoteDate?: boolean;
creditNoteDate?: string;
creditNoteDateLabel?: string;
// Address
showCustomerAddress?: boolean;
customerAddress?: string;
showCompanyAddress?: boolean;
companyAddress?: string;
billedToLabel?: string;
// Entries
lineItemLabel?: string;
lineQuantityLabel?: string;
lineRateLabel?: string;
lineTotalLabel?: string;
// Subtotal
showSubtotal?: boolean;
subtotalLabel?: string;
subtotal?: string;
// Total
showTotal?: boolean;
totalLabel?: string;
total?: string;
// Customer Note
showCustomerNote?: boolean;
customerNote?: string;
customerNoteLabel?: string;
// Terms & Conditions
showTermsConditions?: boolean;
termsConditions?: string;
termsConditionsLabel?: string;
lines?: Array<CreditNoteLine>;
}
export function CreditNotePaperTemplate({
// # Colors
primaryColor,
secondaryColor,
// # Company
companyName = 'Bigcapital Technology, Inc.',
showCompanyLogo = true,
companyLogoUri = '',
// # Credit Note number
creditNoteNumberLabel = 'Credit Note Number',
creditNoteNumebr = '346D3D40-0001',
showCreditNoteNumber = true,
// # Credit Note date
creditNoteDate = 'September 3, 2024',
creditNoteDateLabel = 'Credit Note Date',
showCreditNoteDate = true,
// Address
showCustomerAddress = true,
customerAddress = DefaultPdfTemplateAddressBilledTo,
showCompanyAddress = true,
companyAddress = DefaultPdfTemplateAddressBilledFrom,
billedToLabel = 'Billed To',
// Entries
lineItemLabel = 'Item',
lineQuantityLabel = 'Qty',
lineRateLabel = 'Rate',
lineTotalLabel = 'Total',
// Subtotal
subtotalLabel = 'Subtotal',
showSubtotal = true,
subtotal = '1000.00',
// Total
totalLabel = 'Total',
showTotal = true,
total = '$1000.00',
// Customer Note
showCustomerNote = true,
customerNote = '',
customerNoteLabel = 'Customer Note',
// Terms & Conditions
termsConditionsLabel = 'Terms & Conditions',
showTermsConditions = true,
termsConditions = DefaultPdfTemplateTerms,
lines = [
{
item: DefaultPdfTemplateItemName,
description: DefaultPdfTemplateItemDescription,
rate: '1',
quantity: '1000',
total: '$1000.00',
},
],
...props
}: CreditNotePaperTemplateProps) {
return (
<PaperTemplate
primaryColor={primaryColor}
secondaryColor={secondaryColor}
{...props}
>
<Stack spacing={24}>
<Group align="start" spacing={10}>
<Stack flex={1}>
<PaperTemplate.BigTitle title={'Credit Note'} />
<PaperTemplate.TermsList>
{showCreditNoteNumber && (
<PaperTemplate.TermsItem label={creditNoteNumberLabel}>
{creditNoteNumebr}
</PaperTemplate.TermsItem>
)}
{showCreditNoteDate && (
<PaperTemplate.TermsItem label={creditNoteDateLabel}>
{creditNoteDate}
</PaperTemplate.TermsItem>
)}
</PaperTemplate.TermsList>
</Stack>
{companyLogoUri && showCompanyLogo && (
<PaperTemplate.Logo logoUri={companyLogoUri} />
)}
</Group>
<PaperTemplate.AddressesGroup>
{showCompanyAddress && (
<PaperTemplate.Address>
<Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
</PaperTemplate.Address>
)}
{showCustomerAddress && (
<PaperTemplate.Address>
<strong>{billedToLabel}</strong>
<Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
</PaperTemplate.Address>
)}
</PaperTemplate.AddressesGroup>
<Stack spacing={0}>
<PaperTemplate.Table
columns={[
{
label: lineItemLabel,
accessor: (data) => (
<Stack spacing={2}>
<Text>{data.item}</Text>
{data.description && (
<Text color={'#5f6b7c'} fontSize={12}>
{data.description}
</Text>
)}
</Stack>
),
thStyle: { width: '60%' },
},
{
label: lineQuantityLabel,
accessor: 'quantity',
align: 'right',
},
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
]}
data={lines}
/>
<PaperTemplate.Totals>
{showSubtotal && (
<PaperTemplate.TotalLine
label={subtotalLabel}
amount={subtotal}
border={PaperTemplateTotalBorder.Gray}
/>
)}
{showTotal && (
<PaperTemplate.TotalLine
label={totalLabel}
amount={total}
border={PaperTemplateTotalBorder.Dark}
style={{ fontWeight: 500 }}
/>
)}
</PaperTemplate.Totals>
</Stack>
<Stack spacing={0}>
{showCustomerNote && customerNote && (
<PaperTemplate.Statement label={customerNoteLabel}>
{customerNote}
</PaperTemplate.Statement>
)}
{showTermsConditions && termsConditions && (
<PaperTemplate.Statement label={termsConditionsLabel}>
{termsConditions}
</PaperTemplate.Statement>
)}
</Stack>
</Stack>
</PaperTemplate>
);
}

View File

@@ -1,5 +1,6 @@
export * from './components/PaperTemplate';
export * from './components/InvoicePaperTemplate';
export * from './components/CreditNotePaperTemplate';
export * from './components/EstimatePaperTemplate';
export * from './components/ReceiptPaperTemplate';
export * from './components/PaymentReceivedPaperTemplate';
@@ -7,6 +8,7 @@ export * from './components/FinancialSheetTemplate';
export * from './components/ExportResourceTableTemplate';
export * from './renders/render-invoice-paper-template';
export * from './renders/render-credit-note-paper-template';
export * from './renders/render-estimate-paper-template';
export * from './renders/render-receipt-paper-template';
export * from './renders/render-payment-received-paper-template';

View File

@@ -0,0 +1,17 @@
import {
CreditNotePaperTemplate,
CreditNotePaperTemplateProps,
} from '../components/CreditNotePaperTemplate';
import { renderSSR } from './render-ssr';
/**
* Renders credit note paper template html.
* @param {CreditNotePaperTemplateProps} props
* @returns {string}
*/
export const renderCreditNotePaperTemplateHtml = (
props: CreditNotePaperTemplateProps
) => {
return renderSSR(<CreditNotePaperTemplate {...props} />);
};