mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
feat: rendering pdf templates on the server-side
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import * as R from 'ramda';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit, sumBy } from 'lodash';
|
||||
import composeAsync from 'async/compose';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ICustomer, ISaleEstimate, ISaleEstimateDTO } from '@/interfaces';
|
||||
import { SaleEstimateValidators } from './SaleEstimateValidators';
|
||||
@@ -10,6 +11,7 @@ import { formatDateFields } from '@/utils';
|
||||
import moment from 'moment';
|
||||
import { SaleEstimateIncrement } from './SaleEstimateIncrement';
|
||||
import { assocItemEntriesDefaultIndex } from '@/services/Items/utils';
|
||||
import { BrandingTemplateDTOTransformer } from '@/services/PdfTemplate/BrandingTemplateDTOTransformer';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimateDTOTransformer {
|
||||
@@ -28,6 +30,9 @@ export class SaleEstimateDTOTransformer {
|
||||
@Inject()
|
||||
private estimateIncrement: SaleEstimateIncrement;
|
||||
|
||||
@Inject()
|
||||
private brandingTemplatesTransformer: BrandingTemplateDTOTransformer;
|
||||
|
||||
/**
|
||||
* Transform create DTO object ot model object.
|
||||
* @param {number} tenantId
|
||||
@@ -81,10 +86,18 @@ export class SaleEstimateDTOTransformer {
|
||||
deliveredAt: moment().toMySqlDateTime(),
|
||||
}),
|
||||
};
|
||||
const initialAsyncDTO = await composeAsync(
|
||||
// Assigns the default branding template id to the invoice DTO.
|
||||
this.brandingTemplatesTransformer.assocDefaultBrandingTemplate(
|
||||
tenantId,
|
||||
'SaleEstimate'
|
||||
)
|
||||
)(initialDTO);
|
||||
|
||||
return R.compose(
|
||||
this.branchDTOTransform.transformDTO<ISaleEstimate>(tenantId),
|
||||
this.warehouseDTOTransform.transformDTO<ISaleEstimate>(tenantId)
|
||||
)(initialDTO);
|
||||
)(initialAsyncDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,16 @@ 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';
|
||||
|
||||
@Service()
|
||||
export class SaleEstimatesPdf {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private chromiumlyTenancy: ChromiumlyTenancy;
|
||||
|
||||
@@ -14,25 +21,58 @@ export class SaleEstimatesPdf {
|
||||
@Inject()
|
||||
private getSaleEstimate: GetSaleEstimate;
|
||||
|
||||
@Inject()
|
||||
private estimatePdfTemplate: SaleEstimatePdfTemplate;
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice pdf content.
|
||||
* @param {number} tenantId -
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
*/
|
||||
public async getSaleEstimatePdf(tenantId: number, saleEstimateId: number) {
|
||||
const saleEstimate = await this.getSaleEstimate.getEstimate(
|
||||
const brandingAttributes = await this.getEstimateBrandingAttributes(
|
||||
tenantId,
|
||||
saleEstimateId
|
||||
);
|
||||
const htmlContent = await this.templateInjectable.render(
|
||||
tenantId,
|
||||
'modules/estimate-regular',
|
||||
{
|
||||
saleEstimate,
|
||||
}
|
||||
brandingAttributes
|
||||
);
|
||||
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent, {
|
||||
margins: { top: 0, bottom: 0, left: 0, right: 0 },
|
||||
});
|
||||
return this.chromiumlyTenancy.convertHtmlContent(tenantId, htmlContent);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} estimateId
|
||||
*/
|
||||
async getEstimateBrandingAttributes(
|
||||
tenantId: number,
|
||||
estimateId: number
|
||||
): Promise<EstimatePdfBrandingAttributes> {
|
||||
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||
const saleEstimate = await this.getSaleEstimate.getEstimate(
|
||||
tenantId,
|
||||
estimateId
|
||||
);
|
||||
// Retrieve the invoice template id of not found get the default template id.
|
||||
const templateId =
|
||||
saleEstimate.pdfTemplateId ??
|
||||
(
|
||||
await PdfTemplate.query().findOne({
|
||||
resource: 'SaleEstimate',
|
||||
default: true,
|
||||
})
|
||||
)?.id;
|
||||
const brandingTemplate =
|
||||
await this.estimatePdfTemplate.getEstimatePdfTemplate(
|
||||
tenantId,
|
||||
templateId
|
||||
);
|
||||
return {
|
||||
...brandingTemplate.attributes,
|
||||
...transformEstimateToPdfTemplate(saleEstimate),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,3 +173,122 @@ export const SaleEstimatesSampleData = [
|
||||
'Line Description': 'Qui suscipit ducimus qui qui.',
|
||||
},
|
||||
];
|
||||
|
||||
export const defaultEstimatePdfBrandingAttributes = {
|
||||
primaryColor: '#000',
|
||||
secondaryColor: '#000',
|
||||
showCompanyLogo: true,
|
||||
companyLogo: '',
|
||||
companyName: '',
|
||||
|
||||
billedToAddress: [
|
||||
'Bigcapital Technology, Inc.',
|
||||
'131 Continental Dr Suite 305 Newark,',
|
||||
'Delaware 19713',
|
||||
'United States',
|
||||
'+1 762-339-5634',
|
||||
'ahmed@bigcapital.app',
|
||||
],
|
||||
billedFromAddress: [
|
||||
'131 Continental Dr Suite 305 Newark,',
|
||||
'Delaware 19713',
|
||||
'United States',
|
||||
'+1 762-339-5634',
|
||||
'ahmed@bigcapital.app',
|
||||
],
|
||||
showBilledFromAddress: true,
|
||||
showBilledToAddress: true,
|
||||
billedToLabel: 'Billed To',
|
||||
|
||||
total: '$1000.00',
|
||||
totalLabel: 'Total',
|
||||
showTotal: true,
|
||||
|
||||
subtotal: '1000/00',
|
||||
subtotalLabel: 'Subtotal',
|
||||
showSubtotal: true,
|
||||
|
||||
showCustomerNote: true,
|
||||
customerNote:
|
||||
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||
customerNoteLabel: 'Customer Note',
|
||||
|
||||
showTermsConditions: true,
|
||||
termsConditions:
|
||||
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.',
|
||||
termsConditionsLabel: 'Terms & Conditions',
|
||||
|
||||
lines: [
|
||||
{
|
||||
item: 'Simply dummy text',
|
||||
description: 'Simply dummy text of the printing and typesetting',
|
||||
rate: '1',
|
||||
quantity: '1000',
|
||||
total: '$1000.00',
|
||||
},
|
||||
],
|
||||
showEstimateNumber: true,
|
||||
estimateNumberLabel: 'Estimate Number',
|
||||
estimateNumebr: '346D3D40-0001',
|
||||
|
||||
estimateDate: 'September 3, 2024',
|
||||
showEstimateDate: true,
|
||||
estimateDateLabel: 'Estimate Date',
|
||||
|
||||
expirationDateLabel: 'Expiration Date',
|
||||
showExpirationDate: true,
|
||||
expirationDate: 'September 3, 2024',
|
||||
};
|
||||
|
||||
|
||||
interface EstimatePdfBrandingLineItem {
|
||||
item: string;
|
||||
description: string;
|
||||
rate: string;
|
||||
quantity: string;
|
||||
total: string;
|
||||
}
|
||||
|
||||
export interface EstimatePdfBrandingAttributes {
|
||||
primaryColor: string;
|
||||
secondaryColor: string;
|
||||
showCompanyLogo: boolean;
|
||||
companyLogo: string;
|
||||
companyName: string;
|
||||
|
||||
billedToAddress: string[];
|
||||
billedFromAddress: string[];
|
||||
showBilledFromAddress: boolean;
|
||||
showBilledToAddress: boolean;
|
||||
billedToLabel: string;
|
||||
|
||||
total: string;
|
||||
totalLabel: string;
|
||||
showTotal: boolean;
|
||||
|
||||
subtotal: string;
|
||||
subtotalLabel: string;
|
||||
showSubtotal: boolean;
|
||||
|
||||
showCustomerNote: boolean;
|
||||
customerNote: string;
|
||||
customerNoteLabel: string;
|
||||
|
||||
showTermsConditions: boolean;
|
||||
termsConditions: string;
|
||||
termsConditionsLabel: string;
|
||||
|
||||
lines: EstimatePdfBrandingLineItem[];
|
||||
|
||||
showEstimateNumber: boolean;
|
||||
estimateNumberLabel: string;
|
||||
estimateNumebr: string;
|
||||
|
||||
estimateDate: string;
|
||||
showEstimateDate: boolean;
|
||||
estimateDateLabel: string;
|
||||
|
||||
expirationDateLabel: string;
|
||||
showExpirationDate: boolean;
|
||||
expirationDate: string;
|
||||
}
|
||||
7
packages/server/src/services/Sales/Estimates/utils.ts
Normal file
7
packages/server/src/services/Sales/Estimates/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { EstimatePdfBrandingAttributes } from './constants';
|
||||
|
||||
export const transformEstimateToPdfTemplate = (
|
||||
estimate
|
||||
): Partial<EstimatePdfBrandingAttributes> => {
|
||||
return {};
|
||||
};
|
||||
Reference in New Issue
Block a user