mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
feat: send invoice receipt preview
This commit is contained in:
@@ -179,7 +179,7 @@ export default class SaleInvoicesController extends BaseController {
|
||||
'/:id/mail',
|
||||
[
|
||||
...this.specificSaleInvoiceValidation,
|
||||
|
||||
|
||||
body('subject').isString().optional({ nullable: true }),
|
||||
body('message').isString().optional({ nullable: true }),
|
||||
|
||||
@@ -201,7 +201,7 @@ export default class SaleInvoicesController extends BaseController {
|
||||
this.handleServiceErrors
|
||||
);
|
||||
router.get(
|
||||
'/:id/mail',
|
||||
'/:id/mail/state',
|
||||
[...this.specificSaleInvoiceValidation],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.getSaleInvoiceMail.bind(this)),
|
||||
@@ -789,7 +789,7 @@ export default class SaleInvoicesController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default mail options of the given sale invoice.
|
||||
* Retrieves the mail state of the given sale invoice.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
@@ -803,7 +803,7 @@ export default class SaleInvoicesController extends BaseController {
|
||||
const { id: invoiceId } = req.params;
|
||||
|
||||
try {
|
||||
const data = await this.saleInvoiceApplication.getSaleInvoiceMail(
|
||||
const data = await this.saleInvoiceApplication.getSaleInvoiceMailState(
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
|
||||
@@ -238,6 +238,30 @@ export interface SaleInvoiceMailOptions extends CommonMailOptions {
|
||||
formatArgs?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface SaleInvoiceMailState extends SaleInvoiceMailOptions {
|
||||
invoiceNo: string;
|
||||
|
||||
invoiceDate: string;
|
||||
invoiceDateFormatted: string;
|
||||
|
||||
dueDate: string;
|
||||
dueDateFormatted: string;
|
||||
|
||||
total: number;
|
||||
totalFormatted: string;
|
||||
|
||||
subtotal: number;
|
||||
subtotalFormatted: number;
|
||||
|
||||
companyName: string;
|
||||
companyLogoUri: string;
|
||||
|
||||
customerName: string;
|
||||
|
||||
// # Invoice entries
|
||||
entries?: Array<{ label: string; total: string; quantity: string | number }>;
|
||||
}
|
||||
|
||||
export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
|
||||
attachInvoice?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { SaleInvoiceMailOptions, SaleInvoiceMailState } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { Inject } from 'typedi';
|
||||
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { GetSaleInvoiceMailStateTransformer } from './GetSaleInvoiceMailStateTransformer';
|
||||
|
||||
export class GetSaleInvoiceMailState {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private invoiceMail: SendSaleInvoiceMailCommon;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieves the invoice mail state of the given sale invoice.
|
||||
* Invoice mail state includes the mail options, branding attributes and the invoice details.
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @returns {Promise<SaleInvoiceMailState>}
|
||||
*/
|
||||
async getInvoiceMailState(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number
|
||||
): Promise<SaleInvoiceMailState> {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
const saleInvoice = await SaleInvoice.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('pdfTemplate')
|
||||
.throwIfNotFound();
|
||||
|
||||
const mailOptions = await this.invoiceMail.getInvoiceMailOptions(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
// Transforms the sale invoice mail state.
|
||||
const transformed = await this.transformer.transform(
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
new GetSaleInvoiceMailStateTransformer(),
|
||||
{
|
||||
mailOptions,
|
||||
}
|
||||
);
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { SaleInvoiceTransformer } from './SaleInvoiceTransformer';
|
||||
import { ItemEntryTransformer } from './ItemEntryTransformer';
|
||||
|
||||
export class GetSaleInvoiceMailStateTransformer extends SaleInvoiceTransformer {
|
||||
/**
|
||||
* Exclude these attributes from user object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'invoiceDate',
|
||||
'invoiceDateFormatted',
|
||||
|
||||
'dueDate',
|
||||
'dueDateFormatted',
|
||||
|
||||
'dueAmount',
|
||||
'dueAmountFormatted',
|
||||
|
||||
'total',
|
||||
'totalFormatted',
|
||||
|
||||
'subtotal',
|
||||
'subtotalFormatted',
|
||||
|
||||
'invoiceNo',
|
||||
|
||||
'entries',
|
||||
|
||||
'companyName',
|
||||
'companyLogoUri',
|
||||
|
||||
'primaryColor',
|
||||
];
|
||||
};
|
||||
|
||||
protected companyName = () => {
|
||||
return this.context.organization.name;
|
||||
};
|
||||
|
||||
protected companyLogoUri = (invoice) => {
|
||||
return invoice.pdfTemplate?.attributes?.companyLogoUri;
|
||||
};
|
||||
|
||||
protected primaryColor = (invoice) => {
|
||||
return invoice.pdfTemplate?.attributes?.primaryColor;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param invoice
|
||||
* @returns
|
||||
*/
|
||||
protected entries = (invoice) => {
|
||||
return this.item(
|
||||
invoice.entries,
|
||||
new GetSaleInvoiceMailStateEntryTransformer(),
|
||||
{
|
||||
currencyCode: invoice.currencyCode,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges the mail options with the invoice object.
|
||||
*/
|
||||
public transform = (object: any) => {
|
||||
return {
|
||||
...this.options.mailOptions,
|
||||
...object,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class GetSaleInvoiceMailStateEntryTransformer extends ItemEntryTransformer {
|
||||
/**
|
||||
* Exclude these attributes from user object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
public name = (entry) => {
|
||||
return entry.item.name;
|
||||
};
|
||||
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'name',
|
||||
'quantity',
|
||||
'quantityFormatted',
|
||||
'rate',
|
||||
'rateFormatted',
|
||||
'total',
|
||||
'totalFormatted',
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ISystemUser,
|
||||
ITenantUser,
|
||||
InvoiceNotificationType,
|
||||
SaleInvoiceMailState,
|
||||
SendInvoiceMailDTO,
|
||||
} from '@/interfaces';
|
||||
import { Inject, Service } from 'typedi';
|
||||
@@ -29,6 +30,8 @@ import { SendInvoiceMailReminder } from './SendSaleInvoiceMailReminder';
|
||||
import { SendSaleInvoiceMail } from './SendSaleInvoiceMail';
|
||||
import { GetSaleInvoiceMailReminder } from './GetSaleInvoiceMailReminder';
|
||||
import { GetSaleInvoiceState } from './GetSaleInvoiceState';
|
||||
import { GetSaleInvoiceBrandTemplate } from './GetSaleInvoiceBrandTemplate';
|
||||
import { GetSaleInvoiceMailState } from './GetSaleInvoiceMailState';
|
||||
|
||||
@Service()
|
||||
export class SaleInvoiceApplication {
|
||||
@@ -72,7 +75,7 @@ export class SaleInvoiceApplication {
|
||||
private sendSaleInvoiceMailService: SendSaleInvoiceMail;
|
||||
|
||||
@Inject()
|
||||
private getSaleInvoiceReminderService: GetSaleInvoiceMailReminder;
|
||||
private getSaleInvoiceMailStateService: GetSaleInvoiceMailState;
|
||||
|
||||
@Inject()
|
||||
private getSaleInvoiceStateService: GetSaleInvoiceState;
|
||||
@@ -361,10 +364,10 @@ export class SaleInvoiceApplication {
|
||||
* Retrieves the default mail options of the given sale invoice.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceid
|
||||
* @returns {Promise<SendInvoiceMailDTO>}
|
||||
* @returns {Promise<SaleInvoiceMailState>}
|
||||
*/
|
||||
public getSaleInvoiceMail(tenantId: number, saleInvoiceid: number) {
|
||||
return this.sendSaleInvoiceMailService.getMailOption(
|
||||
public getSaleInvoiceMailState(tenantId: number, saleInvoiceid: number) {
|
||||
return this.getSaleInvoiceMailStateService.getInvoiceMailState(
|
||||
tenantId,
|
||||
saleInvoiceid
|
||||
);
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import Mail from '@/lib/Mail';
|
||||
import {
|
||||
ISaleInvoiceMailSend,
|
||||
SaleInvoiceMailOptions,
|
||||
SendInvoiceMailDTO,
|
||||
} from '@/interfaces';
|
||||
import { ISaleInvoiceMailSend, SendInvoiceMailDTO } from '@/interfaces';
|
||||
import { SaleInvoicePdf } from './SaleInvoicePdf';
|
||||
import { SendSaleInvoiceMailCommon } from './SendInvoiceInvoiceMailCommon';
|
||||
import {
|
||||
DEFAULT_INVOICE_MAIL_CONTENT,
|
||||
DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||
} from './constants';
|
||||
import {
|
||||
parseMailOptions,
|
||||
validateRequiredMailOptions,
|
||||
@@ -58,26 +50,6 @@ export class SendSaleInvoiceMail {
|
||||
} as ISaleInvoiceMailSend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mail options of the given sale invoice.
|
||||
* @param {number} tenantId
|
||||
* @param {number} saleInvoiceId
|
||||
* @returns {Promise<SaleInvoiceMailOptions>}
|
||||
*/
|
||||
public async getMailOption(
|
||||
tenantId: number,
|
||||
saleInvoiceId: number,
|
||||
defaultSubject: string = DEFAULT_INVOICE_MAIL_SUBJECT,
|
||||
defaultMessage: string = DEFAULT_INVOICE_MAIL_CONTENT
|
||||
): Promise<SaleInvoiceMailOptions> {
|
||||
return this.invoiceMail.getInvoiceMailOptions(
|
||||
tenantId,
|
||||
saleInvoiceId,
|
||||
defaultSubject,
|
||||
defaultMessage
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the mail invoice.
|
||||
* @param {number} tenantId
|
||||
@@ -90,7 +62,7 @@ export class SendSaleInvoiceMail {
|
||||
saleInvoiceId: number,
|
||||
messageOptions: SendInvoiceMailDTO
|
||||
) {
|
||||
const defaultMessageOptions = await this.getMailOption(
|
||||
const defaultMessageOptions = await this.invoiceMail.getInvoiceMailOptions(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user