mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
refactor: mail templates
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
CreateSaleEstimateDto,
|
||||
EditSaleEstimateDto,
|
||||
} from './dtos/SaleEstimate.dto';
|
||||
import { GetSaleEstimateMailStateService } from './queries/GetSaleEstimateMailState.service';
|
||||
|
||||
@Injectable()
|
||||
export class SaleEstimatesApplication {
|
||||
@@ -33,6 +34,7 @@ export class SaleEstimatesApplication {
|
||||
private readonly sendEstimateMailService: SendSaleEstimateMail,
|
||||
private readonly getSaleEstimateStateService: GetSaleEstimateState,
|
||||
private readonly saleEstimatesPdfService: GetSaleEstimatePdf,
|
||||
private readonly getSaleEstimateMailStateService: GetSaleEstimateMailStateService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -172,4 +174,23 @@ export class SaleEstimatesApplication {
|
||||
public getSaleEstimateState() {
|
||||
return this.getSaleEstimateStateService.getSaleEstimateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the sale estimate mail state.
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<SaleEstimateMailOptions>}
|
||||
*/
|
||||
public getSaleEstimateMailState(saleEstimateId: number) {
|
||||
return this.getSaleEstimateMailStateService.getEstimateMailState(
|
||||
saleEstimateId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the HTML content of the given sale estimate.
|
||||
* @param {number} saleEstimateId
|
||||
*/
|
||||
public getSaleEstimateHtml(saleEstimateId: number) {
|
||||
return this.saleEstimatesPdfService.saleEstimateHtml(saleEstimateId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ export class SaleEstimatesController {
|
||||
}
|
||||
|
||||
@Get(':id/mail')
|
||||
@ApiOperation({ summary: 'Retrieves the sale estimate mail details.' })
|
||||
@ApiOperation({ summary: 'Retrieves the sale estimate mail state.' })
|
||||
@ApiParam({
|
||||
name: 'id',
|
||||
required: true,
|
||||
@@ -218,7 +218,9 @@ export class SaleEstimatesController {
|
||||
public getSaleEstimateMail(
|
||||
@Param('id', ParseIntPipe) saleEstimateId: number,
|
||||
) {
|
||||
return this.saleEstimatesApplication.getSaleEstimateMail(saleEstimateId);
|
||||
return this.saleEstimatesApplication.getSaleEstimateMailState(
|
||||
saleEstimateId,
|
||||
);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -243,6 +245,11 @@ export class SaleEstimatesController {
|
||||
'Content-Length': pdfContent.length,
|
||||
});
|
||||
res.send(pdfContent);
|
||||
} else if (acceptHeader.includes(AcceptType.ApplicationTextHtml)) {
|
||||
const htmlContent =
|
||||
await this.saleEstimatesApplication.getSaleEstimateHtml(estimateId);
|
||||
|
||||
return { htmlContent };
|
||||
} else {
|
||||
return this.saleEstimatesApplication.getSaleEstimate(estimateId);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ import { PdfTemplatesModule } from '../PdfTemplate/PdfTemplates.module';
|
||||
import { SendSaleEstimateMailQueue } from './types/SaleEstimates.types';
|
||||
import { SaleEstimatesExportable } from './SaleEstimatesExportable';
|
||||
import { SaleEstimatesImportable } from './SaleEstimatesImportable';
|
||||
import { GetSaleEstimateMailStateService } from './queries/GetSaleEstimateMailState.service';
|
||||
import { GetSaleEstimateMailTemplateService } from './queries/GetSaleEstimateMailTemplate.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -78,11 +80,15 @@ import { SaleEstimatesImportable } from './SaleEstimatesImportable';
|
||||
GetSaleEstimatePdf,
|
||||
SaleEstimatePdfTemplate,
|
||||
SaleEstimatesExportable,
|
||||
SaleEstimatesImportable
|
||||
SaleEstimatesImportable,
|
||||
GetSaleEstimateMailStateService,
|
||||
GetSaleEstimateMailTemplateService
|
||||
],
|
||||
exports: [
|
||||
SaleEstimatesExportable,
|
||||
SaleEstimatesImportable
|
||||
SaleEstimatesImportable,
|
||||
GetSaleEstimateMailStateService,
|
||||
GetSaleEstimateMailTemplateService
|
||||
]
|
||||
})
|
||||
export class SaleEstimatesModule {}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { SaleEstimateMailOptions } from '../types/SaleEstimates.types';
|
||||
import { Mail } from '@/modules/Mail/Mail';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { GetSaleEstimateMailTemplateService } from '../queries/GetSaleEstimateMailTemplate.service';
|
||||
|
||||
@Injectable()
|
||||
export class SendSaleEstimateMail {
|
||||
@@ -38,6 +39,7 @@ export class SendSaleEstimateMail {
|
||||
private readonly estimatePdf: GetSaleEstimatePdf,
|
||||
private readonly getSaleEstimateService: GetSaleEstimate,
|
||||
private readonly contactMailNotification: ContactMailNotification,
|
||||
private readonly getEstimateMailTemplate: GetSaleEstimateMailTemplateService,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly mailTransporter: MailTransporter,
|
||||
|
||||
@@ -78,7 +80,12 @@ export class SendSaleEstimateMail {
|
||||
*/
|
||||
public formatterArgs = async (estimateId: number) => {
|
||||
const estimate = await this.getSaleEstimateService.getEstimate(estimateId);
|
||||
return transformEstimateToMailDataArgs(estimate);
|
||||
const commonArgs = await this.contactMailNotification.getCommonFormatArgs();
|
||||
|
||||
return {
|
||||
...commonArgs,
|
||||
...transformEstimateToMailDataArgs(estimate),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -129,9 +136,35 @@ export class SendSaleEstimateMail {
|
||||
mailOptions,
|
||||
formatterArgs,
|
||||
);
|
||||
return { ...formattedOptions };
|
||||
// Retrieves the estimate mail template.
|
||||
const message = await this.getEstimateMailTemplate.getMailTemplate(
|
||||
saleEstimateId,
|
||||
{
|
||||
message: formattedOptions.message,
|
||||
preview: formattedOptions.message,
|
||||
},
|
||||
);
|
||||
return { ...formattedOptions, message };
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted mail options.
|
||||
* @param {number} saleEstimateId
|
||||
* @param {SaleEstimateMailOptionsDTO} messageOptions
|
||||
* @returns {Promise<SaleEstimateMailOptions>}
|
||||
*/
|
||||
public async getFormattedMailOptions(
|
||||
saleEstimateId: number,
|
||||
messageOptions: SaleEstimateMailOptionsDTO,
|
||||
): Promise<SaleEstimateMailOptions> {
|
||||
const defaultMessageOptions = await this.getMailOptions(saleEstimateId);
|
||||
const parsedMessageOptions = mergeAndValidateMailOptions(
|
||||
defaultMessageOptions,
|
||||
messageOptions,
|
||||
);
|
||||
return this.formatMailOptions(saleEstimateId, parsedMessageOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the mail notification of the given sale estimate.
|
||||
* @param {number} saleEstimateId - Sale estimate id.
|
||||
@@ -142,16 +175,9 @@ export class SendSaleEstimateMail {
|
||||
saleEstimateId: number,
|
||||
messageOptions: SaleEstimateMailOptionsDTO,
|
||||
): Promise<void> {
|
||||
const localMessageOpts = await this.getMailOptions(saleEstimateId);
|
||||
// Overrides and validates the given mail options.
|
||||
const parsedMessageOptions = mergeAndValidateMailOptions(
|
||||
localMessageOpts,
|
||||
messageOptions,
|
||||
) as SaleEstimateMailOptions;
|
||||
|
||||
const formattedOptions = await this.formatMailOptions(
|
||||
const formattedOptions = await this.getFormattedMailOptions(
|
||||
saleEstimateId,
|
||||
parsedMessageOptions,
|
||||
messageOptions,
|
||||
);
|
||||
const mail = new Mail()
|
||||
.setSubject(formattedOptions.subject)
|
||||
@@ -173,7 +199,6 @@ export class SendSaleEstimateMail {
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const eventPayload = {
|
||||
saleEstimateId,
|
||||
messageOptions,
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
export const DEFAULT_ESTIMATE_REMINDER_MAIL_SUBJECT =
|
||||
'Estimate {Estimate Number} is awaiting your approval';
|
||||
export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `<p>Dear {Customer Name}</p>
|
||||
<p>Thank you for your business, You can view or print your estimate from attachements.</p>
|
||||
<p>
|
||||
Estimate <strong>#{Estimate Number}</strong><br />
|
||||
Expiration Date : <strong>{Estimate Expiration Date}</strong><br />
|
||||
Amount : <strong>{Estimate Amount}</strong></br />
|
||||
</p>
|
||||
export const DEFAULT_ESTIMATE_REMINDER_MAIL_CONTENT = `Hi {Customer Name},
|
||||
|
||||
<p>
|
||||
<i>Regards</i><br />
|
||||
<i>{Company Name}</i>
|
||||
</p>
|
||||
`;
|
||||
Here's estimate # {Estimate Number} for {Estimate Amount}
|
||||
|
||||
This estimate is valid until {Estimate Expiration Date}, and we’re happy to discuss any adjustments you or questions may have.
|
||||
|
||||
Please find your estimate attached to this email for your reference.
|
||||
|
||||
If you have any questions, please let us know.
|
||||
|
||||
Thanks,
|
||||
{Company Name}`;
|
||||
|
||||
export const ERRORS = {
|
||||
SALE_ESTIMATE_NOT_FOUND: 'SALE_ESTIMATE_NOT_FOUND',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as moment from 'moment';
|
||||
import { Model } from 'objection';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.decorator';
|
||||
import { ImportableModel } from '@/modules/Import/decorators/Import.decorator';
|
||||
@@ -307,12 +306,6 @@ export class SaleEstimate extends TenantBaseModel {
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Model settings.
|
||||
*/
|
||||
// static get meta() {
|
||||
// return SaleEstimateSettings;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Retrieve the default custom views, roles and columns.
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
|
||||
export class GetEstimateMailTemplateAttributesTransformer extends Transformer {
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'companyLogoUri',
|
||||
'companyName',
|
||||
|
||||
'estimateAmount',
|
||||
|
||||
'primaryColor',
|
||||
|
||||
'estimateAmount',
|
||||
'estimateMessage',
|
||||
|
||||
'dueDate',
|
||||
'dueDateLabel',
|
||||
|
||||
'estimateNumber',
|
||||
'estimateNumberLabel',
|
||||
|
||||
'total',
|
||||
'totalLabel',
|
||||
|
||||
'subtotal',
|
||||
'subtotalLabel',
|
||||
|
||||
'dueAmount',
|
||||
'dueAmountLabel',
|
||||
|
||||
'viewEstimateButtonLabel',
|
||||
'viewEstimateButtonUrl',
|
||||
|
||||
'items',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclude all attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Company logo uri.
|
||||
* @returns {string}
|
||||
*/
|
||||
public companyLogoUri(): string {
|
||||
return this.options.brandingTemplate?.companyLogoUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Company name.
|
||||
* @returns {string}
|
||||
*/
|
||||
public companyName(): string {
|
||||
return this.context.organization.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary color.
|
||||
* @returns {string}
|
||||
*/
|
||||
public primaryColor(): string {
|
||||
return this.options?.brandingTemplate?.attributes?.primaryColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate number.
|
||||
* @returns {string}
|
||||
*/
|
||||
public estimateNumber(): string {
|
||||
return this.options.estimate.estimateNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate number label.
|
||||
* @returns {string}
|
||||
*/
|
||||
public estimateNumberLabel(): string {
|
||||
return 'Estimate No: {estimateNumber}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiration date.
|
||||
* @returns {string}
|
||||
*/
|
||||
public expirationDate(): string {
|
||||
return this.options.estimate.formattedExpirationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiration date label.
|
||||
* @returns {string}
|
||||
*/
|
||||
public expirationDateLabel(): string {
|
||||
return 'Expiration Date: {expirationDate}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate total.
|
||||
*/
|
||||
public total(): string {
|
||||
return this.options.estimate.formattedAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate total label.
|
||||
* @returns {string}
|
||||
*/
|
||||
public totalLabel(): string {
|
||||
return 'Total';
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate subtotal.
|
||||
*/
|
||||
public subtotal(): string {
|
||||
return this.options.estimate.formattedAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate subtotal label.
|
||||
* @returns {string}
|
||||
*/
|
||||
public subtotalLabel(): string {
|
||||
return 'Subtotal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate mail items attributes.
|
||||
*/
|
||||
public items(): any[] {
|
||||
return this.item(
|
||||
this.options.estimate.entries,
|
||||
new GetEstimateMailTemplateEntryAttributesTransformer(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetEstimateMailTemplateEntryAttributesTransformer extends Transformer {
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['label', 'quantity', 'rate', 'total'];
|
||||
};
|
||||
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
public label(entry): string {
|
||||
return entry?.item?.name;
|
||||
}
|
||||
|
||||
public quantity(entry): string {
|
||||
return entry?.quantity;
|
||||
}
|
||||
|
||||
public rate(entry): string {
|
||||
return entry?.rateFormatted;
|
||||
}
|
||||
|
||||
public total(entry): string {
|
||||
return entry?.totalFormatted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { SaleEstimate } from '../models/SaleEstimate';
|
||||
import { SendSaleEstimateMail } from '../commands/SendSaleEstimateMail';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { GetSaleEstimateMailStateTransformer } from './GetSaleEstimateMailState.transformer';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleEstimateMailStateService {
|
||||
constructor(
|
||||
private readonly estimateMail: SendSaleEstimateMail,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@Inject(SaleEstimate.name)
|
||||
private readonly saleEstimateModel: TenantModelProxy<typeof SaleEstimate>
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the estimate mail state of the given sale estimate.
|
||||
* Estimate mail state includes the mail options, branding attributes and the estimate details.
|
||||
* @param {number} saleEstimateId
|
||||
* @returns {Promise<SaleEstimateMailState>}
|
||||
*/
|
||||
async getEstimateMailState(
|
||||
saleEstimateId: number
|
||||
) {
|
||||
const saleEstimate = await this.saleEstimateModel().query()
|
||||
.findById(saleEstimateId)
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('pdfTemplate')
|
||||
.throwIfNotFound();
|
||||
|
||||
const mailOptions = await this.estimateMail.getMailOptions(
|
||||
saleEstimateId
|
||||
);
|
||||
const transformed = await this.transformer.transform(
|
||||
saleEstimate,
|
||||
new GetSaleEstimateMailStateTransformer(),
|
||||
{
|
||||
mailOptions,
|
||||
}
|
||||
);
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { ItemEntryTransformer } from '@/modules/TransactionItemEntry/ItemEntry.transformer';
|
||||
import { SaleEstimateTransfromer } from './SaleEstimate.transformer';
|
||||
|
||||
export class GetSaleEstimateMailStateTransformer extends SaleEstimateTransfromer {
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'estimateDate',
|
||||
'estimateDateFormatted',
|
||||
|
||||
'expirationDate',
|
||||
'expirationDateFormatted',
|
||||
|
||||
'total',
|
||||
'totalFormatted',
|
||||
|
||||
'subtotal',
|
||||
'subtotalFormatted',
|
||||
|
||||
'estimateNumber',
|
||||
'entries',
|
||||
|
||||
'companyName',
|
||||
'companyLogoUri',
|
||||
|
||||
'primaryColor',
|
||||
'customerName',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the customer name of the invoice.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected customerName = (invoice) => {
|
||||
return invoice.customer.displayName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the company name.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected companyName = () => {
|
||||
return this.context.organization.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the company logo uri.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
protected companyLogoUri = (invoice) => {
|
||||
return invoice.pdfTemplate?.companyLogoUri || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the primary color.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected primaryColor = (invoice) => {
|
||||
return invoice.pdfTemplate?.attributes?.primaryColor || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the estimate number.
|
||||
*/
|
||||
protected estimateDateFormatted = (estimate) => {
|
||||
return this.formattedEstimateDate(estimate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the expiration date of the estimate.
|
||||
* @param estimate
|
||||
* @returns {string}
|
||||
*/
|
||||
protected expirationDateFormatted = (estimate) => {
|
||||
return this.formattedExpirationDate(estimate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total amount of the estimate.
|
||||
* @param estimate
|
||||
* @returns
|
||||
*/
|
||||
protected total(estimate) {
|
||||
return estimate.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the subtotal amount of the estimate.
|
||||
* @param estimate
|
||||
* @returns {number}
|
||||
*/
|
||||
protected subtotal(estimate) {
|
||||
return estimate.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted total of the estimate.
|
||||
* @param estimate
|
||||
* @returns {string}
|
||||
*/
|
||||
protected totalFormatted(estimate) {
|
||||
return this.formatMoney(estimate.amount, {
|
||||
currencyCode: estimate.currencyCode,
|
||||
money: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted subtotal of the estimate.
|
||||
* @param estimate
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalFormatted = (estimate) => {
|
||||
return this.formatNumber(estimate.amount, { money: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the estimate entries.
|
||||
* @param invoice
|
||||
* @returns {Array}
|
||||
*/
|
||||
protected entries = (invoice) => {
|
||||
return this.item(
|
||||
invoice.entries,
|
||||
new GetSaleEstimateMailStateEntryTransformer(),
|
||||
{
|
||||
currencyCode: invoice.currencyCode,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges the mail options with the invoice object.
|
||||
*/
|
||||
public transform = (object: any) => {
|
||||
return {
|
||||
...this.options.mailOptions,
|
||||
...object,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class GetSaleEstimateMailStateEntryTransformer extends ItemEntryTransformer {
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Item name.
|
||||
* @param entry
|
||||
* @returns
|
||||
*/
|
||||
public name = (entry) => {
|
||||
return entry.item.name;
|
||||
};
|
||||
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'name',
|
||||
'quantity',
|
||||
'unitPrice',
|
||||
'unitPriceFormatted',
|
||||
'total',
|
||||
'totalFormatted',
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import {
|
||||
renderEstimateEmailTemplate,
|
||||
EstimatePaymentEmailProps,
|
||||
} from '@bigcapital/email-components';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { GetSaleEstimate } from './GetSaleEstimate.service';
|
||||
import { GetPdfTemplateService } from '@/modules/PdfTemplate/queries/GetPdfTemplate.service';
|
||||
import { GetEstimateMailTemplateAttributesTransformer } from './GetEstimateMailTemplateAttributes.transformer';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleEstimateMailTemplateService {
|
||||
constructor(
|
||||
private readonly getEstimateService: GetSaleEstimate,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
private readonly getBrandingTemplate: GetPdfTemplateService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the mail template attributes of the given estimate.
|
||||
* Estimate template attributes are composed of the estimate and branding template attributes.
|
||||
* @param {number} estimateId - Estimate id.
|
||||
* @returns {Promise<EstimatePaymentEmailProps>}
|
||||
*/
|
||||
public async getMailTemplateAttributes(
|
||||
estimateId: number,
|
||||
): Promise<EstimatePaymentEmailProps> {
|
||||
const estimate = await this.getEstimateService.getEstimate(estimateId);
|
||||
const brandingTemplate = await this.getBrandingTemplate.getPdfTemplate(
|
||||
estimate.pdfTemplateId,
|
||||
);
|
||||
const mailTemplateAttributes = await this.transformer.transform(
|
||||
estimate,
|
||||
new GetEstimateMailTemplateAttributesTransformer(),
|
||||
{
|
||||
estimate,
|
||||
brandingTemplate,
|
||||
},
|
||||
);
|
||||
return mailTemplateAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rertieves the mail template html content.
|
||||
* @param {number} tenantId
|
||||
* @param {number} estimateId
|
||||
* @param overrideAttributes
|
||||
* @returns
|
||||
*/
|
||||
public async getMailTemplate(
|
||||
estimateId: number,
|
||||
overrideAttributes?: Partial<any>,
|
||||
): Promise<string> {
|
||||
const attributes = await this.getMailTemplateAttributes(estimateId);
|
||||
const mergedAttributes = {
|
||||
...attributes,
|
||||
...overrideAttributes,
|
||||
};
|
||||
return renderEstimateEmailTemplate(mergedAttributes);
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,17 @@ import { GetSaleEstimate } from './GetSaleEstimate.service';
|
||||
import { transformEstimateToPdfTemplate } from '../utils';
|
||||
import { EstimatePdfBrandingAttributes } from '../constants';
|
||||
import { SaleEstimatePdfTemplate } from '@/modules/SaleInvoices/queries/SaleEstimatePdfTemplate.service';
|
||||
import { TemplateInjectable } from '@/modules/TemplateInjectable/TemplateInjectable.service';
|
||||
import { ChromiumlyTenancy } from '@/modules/ChromiumlyTenancy/ChromiumlyTenancy.service';
|
||||
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
|
||||
import { events } from '@/common/events/events';
|
||||
import { SaleEstimate } from '../models/SaleEstimate';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { renderEstimatePaperTemplateHtml } from '@bigcapital/pdf-templates';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleEstimatePdf {
|
||||
constructor(
|
||||
private readonly chromiumlyTenancy: ChromiumlyTenancy,
|
||||
private readonly templateInjectable: TemplateInjectable,
|
||||
private readonly getSaleEstimate: GetSaleEstimate,
|
||||
private readonly estimatePdfTemplate: SaleEstimatePdfTemplate,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
@@ -29,22 +28,29 @@ export class GetSaleEstimatePdf {
|
||||
private readonly saleEstimateModel: TenantModelProxy<typeof SaleEstimate>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve sale estimate html content.
|
||||
* @param {number} invoiceId -
|
||||
*/
|
||||
public async saleEstimateHtml(estimateId: number): Promise<string> {
|
||||
const brandingAttributes =
|
||||
await this.getEstimateBrandingAttributes(estimateId);
|
||||
|
||||
return renderEstimatePaperTemplateHtml({ ...brandingAttributes });
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice pdf content.
|
||||
* @param {number} tenantId -
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
* @param {ISaleInvoice} saleInvoice - Sale estimate id.
|
||||
*/
|
||||
public async getSaleEstimatePdf(
|
||||
saleEstimateId: number,
|
||||
): Promise<[Buffer, string]> {
|
||||
const filename = await this.getSaleEstimateFilename(saleEstimateId);
|
||||
const brandingAttributes =
|
||||
await this.getEstimateBrandingAttributes(saleEstimateId);
|
||||
|
||||
const htmlContent = await this.templateInjectable.render(
|
||||
'modules/estimate-regular',
|
||||
brandingAttributes,
|
||||
);
|
||||
// Retrieves the sale estimate html.
|
||||
const htmlContent = await this.saleEstimateHtml(saleEstimateId);
|
||||
|
||||
const content =
|
||||
await this.chromiumlyTenancy.convertHtmlContent(htmlContent);
|
||||
const eventPayload = { saleEstimateId };
|
||||
@@ -72,7 +78,6 @@ export class GetSaleEstimatePdf {
|
||||
|
||||
/**
|
||||
* Retrieves the given estimate branding attributes.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} estimateId - Estimate id.
|
||||
* @returns {Promise<EstimatePdfBrandingAttributes>}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user