mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
import { SaleInvoiceTaxEntryTransformer } from './SaleInvoiceTaxEntry.transformer';
|
||||
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
|
||||
import { contactAddressTextFormat } from '@/utils/address-text-format';
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
import { ItemEntryTransformer } from '@/modules/TransactionItemEntry/ItemEntry.transformer';
|
||||
import { GetPdfTemplateTransformer } from '@/modules/PdfTemplate/queries/GetPdfTemplate.transformer';
|
||||
|
||||
export class GetInvoicePaymentLinkMetaTransformer extends SaleInvoiceTransformer {
|
||||
/**
|
||||
* Exclude these attributes from payment link object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Included attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'customerName',
|
||||
'dueAmount',
|
||||
'dueDateFormatted',
|
||||
'invoiceDateFormatted',
|
||||
'total',
|
||||
'totalFormatted',
|
||||
'totalLocalFormatted',
|
||||
'subtotal',
|
||||
'subtotalFormatted',
|
||||
'subtotalLocalFormatted',
|
||||
'dueAmount',
|
||||
'dueAmountFormatted',
|
||||
'paymentAmount',
|
||||
'paymentAmountFormatted',
|
||||
'dueDate',
|
||||
'dueDateFormatted',
|
||||
'invoiceNo',
|
||||
'invoiceMessage',
|
||||
'termsConditions',
|
||||
'entries',
|
||||
'taxes',
|
||||
'organization',
|
||||
'isReceivable',
|
||||
'hasStripePaymentMethod',
|
||||
'formattedCustomerAddress',
|
||||
'brandingTemplate',
|
||||
];
|
||||
};
|
||||
|
||||
public customerName(invoice) {
|
||||
return invoice.customer.displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the organization metadata for the payment link.
|
||||
* @returns
|
||||
*/
|
||||
public organization(invoice) {
|
||||
return this.item(
|
||||
this.context.organization,
|
||||
new GetPaymentLinkOrganizationMetaTransformer()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the branding template for the payment link.
|
||||
* @param {} invoice
|
||||
* @returns
|
||||
*/
|
||||
public brandingTemplate(invoice) {
|
||||
return this.item(
|
||||
invoice.pdfTemplate,
|
||||
new GetInvoicePaymentLinkBrandingTemplate()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the entries of the sale invoice.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {}
|
||||
*/
|
||||
protected entries = (invoice) => {
|
||||
return this.item(
|
||||
invoice.entries,
|
||||
new GetInvoicePaymentLinkEntryMetaTransformer(),
|
||||
{
|
||||
currencyCode: invoice.currencyCode,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the sale invoice entries.
|
||||
* @returns {}
|
||||
*/
|
||||
protected taxes = (invoice) => {
|
||||
return this.item(
|
||||
invoice.taxes,
|
||||
new GetInvoicePaymentLinkTaxEntryTransformer(),
|
||||
{
|
||||
subtotal: invoice.subtotal,
|
||||
isInclusiveTax: invoice.isInclusiveTax,
|
||||
currencyCode: invoice.currencyCode,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
protected isReceivable(invoice) {
|
||||
return invoice.dueAmount > 0;
|
||||
}
|
||||
|
||||
protected hasStripePaymentMethod(invoice) {
|
||||
return invoice.paymentMethods.some(
|
||||
(paymentMethod) => paymentMethod.paymentIntegration.service === 'Stripe'
|
||||
);
|
||||
}
|
||||
|
||||
get customerAddressFormat() {
|
||||
return `{ADDRESS_1}
|
||||
{ADDRESS_2}
|
||||
{CITY} {STATE} {POSTAL_CODE}
|
||||
{COUNTRY}
|
||||
{PHONE}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the formatted customer address.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedCustomerAddress(invoice) {
|
||||
return contactAddressTextFormat(
|
||||
invoice.customer,
|
||||
this.customerAddressFormat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetPaymentLinkOrganizationMetaTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to item entry object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'primaryColor',
|
||||
'name',
|
||||
'address',
|
||||
'logoUri',
|
||||
'addressTextFormatted',
|
||||
];
|
||||
};
|
||||
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted text of organization address.
|
||||
* @returns {string}
|
||||
*/
|
||||
public addressTextFormatted() {
|
||||
return this.context.organization.addressTextFormatted;
|
||||
}
|
||||
}
|
||||
|
||||
class GetInvoicePaymentLinkEntryMetaTransformer extends ItemEntryTransformer {
|
||||
/**
|
||||
* Include these attributes to item entry object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'quantity',
|
||||
'quantityFormatted',
|
||||
'rate',
|
||||
'rateFormatted',
|
||||
'total',
|
||||
'totalFormatted',
|
||||
'itemName',
|
||||
'description',
|
||||
];
|
||||
};
|
||||
|
||||
public itemName(entry) {
|
||||
return entry.item.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude these attributes from payment link object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
}
|
||||
|
||||
class GetInvoicePaymentLinkTaxEntryTransformer extends SaleInvoiceTaxEntryTransformer {
|
||||
/**
|
||||
* Included attributes.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['name', 'taxRateCode', 'taxRateAmount', 'taxRateAmountFormatted'];
|
||||
};
|
||||
}
|
||||
|
||||
class GetInvoicePaymentLinkBrandingTemplate extends GetPdfTemplateTransformer {
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['companyLogoUri', 'primaryColor'];
|
||||
};
|
||||
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
primaryColor = (template) => {
|
||||
return template.attributes?.primaryColor;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
InvoicePaymentEmailProps,
|
||||
renderInvoicePaymentEmail,
|
||||
} from '@bigcapital/email-components';
|
||||
import { GetSaleInvoice } from './GetSaleInvoice.service';
|
||||
import { GetPdfTemplateService } from '@/modules/PdfTemplate/queries/GetPdfTemplate.service';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { GetInvoicePaymentMailAttributesTransformer } from './GetInvoicePaymentMailAttributes.transformer';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class GetInvoicePaymentMail {
|
||||
constructor(
|
||||
private readonly getSaleInvoiceService: GetSaleInvoice,
|
||||
private readonly getBrandingTemplate: GetPdfTemplateService,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the mail template attributes of the given invoice.
|
||||
* Invoice template attributes are composed of the invoice and branding template attributes.
|
||||
* @param {number} invoiceId - Invoice id.
|
||||
*/
|
||||
public async getMailTemplateAttributes(invoiceId: number) {
|
||||
const invoice = await this.getSaleInvoiceService.getSaleInvoice(invoiceId);
|
||||
const brandingTemplate = await this.getBrandingTemplate.getPdfTemplate(
|
||||
invoice.pdfTemplateId,
|
||||
);
|
||||
const mailTemplateAttributes = await this.transformer.transform(
|
||||
invoice,
|
||||
new GetInvoicePaymentMailAttributesTransformer(),
|
||||
{
|
||||
invoice,
|
||||
brandingTemplate,
|
||||
},
|
||||
);
|
||||
return mailTemplateAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mail template html content.
|
||||
* @param {number} invoiceId - Invoice id.
|
||||
*/
|
||||
public async getMailTemplate(
|
||||
invoiceId: number,
|
||||
overrideAttributes?: Partial<InvoicePaymentEmailProps>,
|
||||
): Promise<string> {
|
||||
const attributes = await this.getMailTemplateAttributes(invoiceId);
|
||||
const mergedAttributes = { ...attributes, ...overrideAttributes };
|
||||
|
||||
return renderInvoicePaymentEmail(mergedAttributes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { Transformer } from "@/modules/Transformer/Transformer";
|
||||
|
||||
export class GetInvoicePaymentMailAttributesTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to item entry object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'companyLogoUri',
|
||||
'companyName',
|
||||
|
||||
'invoiceAmount',
|
||||
|
||||
'primaryColor',
|
||||
|
||||
'invoiceAmount',
|
||||
'invoiceMessage',
|
||||
|
||||
'dueDate',
|
||||
'dueDateLabel',
|
||||
|
||||
'invoiceNumber',
|
||||
'invoiceNumberLabel',
|
||||
|
||||
'total',
|
||||
'totalLabel',
|
||||
|
||||
'dueAmount',
|
||||
'dueAmountLabel',
|
||||
|
||||
'viewInvoiceButtonLabel',
|
||||
'viewInvoiceButtonUrl',
|
||||
|
||||
'items',
|
||||
];
|
||||
};
|
||||
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
public companyLogoUri(): string {
|
||||
return this.options.brandingTemplate?.companyLogoUri;
|
||||
}
|
||||
|
||||
public companyName(): string {
|
||||
return this.context.organization.name;
|
||||
}
|
||||
|
||||
public invoiceAmount(): string {
|
||||
return this.options.invoice.totalFormatted;
|
||||
}
|
||||
|
||||
public primaryColor(): string {
|
||||
return this.options?.brandingTemplate?.attributes?.primaryColor;
|
||||
}
|
||||
|
||||
public invoiceMessage(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public dueDate(): string {
|
||||
return this.options?.invoice?.dueDateFormatted;
|
||||
}
|
||||
|
||||
public dueDateLabel(): string {
|
||||
return 'Due {dueDate}';
|
||||
}
|
||||
|
||||
public invoiceNumber(): string {
|
||||
return this.options?.invoice?.invoiceNo;
|
||||
}
|
||||
|
||||
public invoiceNumberLabel(): string {
|
||||
return 'Invoice # {invoiceNumber}';
|
||||
}
|
||||
|
||||
public total(): string {
|
||||
return this.options.invoice?.totalFormatted;
|
||||
}
|
||||
|
||||
public totalLabel(): string {
|
||||
return 'Total';
|
||||
}
|
||||
|
||||
public dueAmount(): string {
|
||||
return this.options?.invoice.dueAmountFormatted;
|
||||
}
|
||||
|
||||
public dueAmountLabel(): string {
|
||||
return 'Due Amount';
|
||||
}
|
||||
|
||||
public viewInvoiceButtonLabel(): string {
|
||||
return 'View Invoice';
|
||||
}
|
||||
|
||||
public viewInvoiceButtonUrl(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public items(): Array<any> {
|
||||
return this.item(
|
||||
this.options.invoice?.entries,
|
||||
new GetInvoiceMailTemplateItemAttrsTransformer()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetInvoiceMailTemplateItemAttrsTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to item entry object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['quantity', 'label', 'rate'];
|
||||
};
|
||||
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
public quantity(entry): string {
|
||||
return entry?.quantity;
|
||||
}
|
||||
|
||||
public label(entry): string {
|
||||
return entry?.item?.name;
|
||||
}
|
||||
|
||||
public rate(entry): string {
|
||||
return entry?.rateFormatted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { InvoicePaymentTransactionTransformer } from './InvoicePaymentTransaction.transformer';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { PaymentReceivedEntry } from '@/modules/PaymentReceived/models/PaymentReceivedEntry';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetInvoicePaymentsService {
|
||||
constructor(
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@Inject(PaymentReceivedEntry.name)
|
||||
private readonly paymentReceivedEntryModel: TenantModelProxy<
|
||||
typeof PaymentReceivedEntry
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve the invoice associated payments transactions.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} invoiceId - Invoice id.
|
||||
*/
|
||||
public getInvoicePayments = async (invoiceId: number) => {
|
||||
const paymentsEntries = await this.paymentReceivedEntryModel()
|
||||
.query()
|
||||
.where('invoiceId', invoiceId)
|
||||
.withGraphJoined('payment.depositAccount')
|
||||
.withGraphJoined('invoice')
|
||||
.orderBy('payment:paymentDate', 'ASC');
|
||||
|
||||
return this.transformer.transform(
|
||||
paymentsEntries,
|
||||
new InvoicePaymentTransactionTransformer(),
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
|
||||
import { CommandSaleInvoiceValidators } from '../commands/CommandSaleInvoiceValidators.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleInvoice {
|
||||
constructor(
|
||||
private transformer: TransformerInjectable,
|
||||
private validators: CommandSaleInvoiceValidators,
|
||||
private eventPublisher: EventEmitter2,
|
||||
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice with associated entries.
|
||||
* @param {Number} saleInvoiceId -
|
||||
* @param {ISystemUser} authorizedUser -
|
||||
* @return {Promise<ISaleInvoice>}
|
||||
*/
|
||||
public async getSaleInvoice(saleInvoiceId: number): Promise<SaleInvoice> {
|
||||
const saleInvoice = await this.saleInvoiceModel()
|
||||
.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('entries.tax')
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('branch')
|
||||
.withGraphFetched('taxes.taxRate')
|
||||
.withGraphFetched('attachments')
|
||||
.withGraphFetched('paymentMethods');
|
||||
|
||||
// Validates the given sale invoice existance.
|
||||
this.validators.validateInvoiceExistance(saleInvoice);
|
||||
|
||||
const transformed = await this.transformer.transform(
|
||||
saleInvoice,
|
||||
new SaleInvoiceTransformer(),
|
||||
);
|
||||
const eventPayload = {
|
||||
saleInvoiceId,
|
||||
};
|
||||
// Triggers the `onSaleInvoiceItemViewed` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.saleInvoice.onViewed,
|
||||
eventPayload,
|
||||
);
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export class GetSaleInvoiceMailReminder {
|
||||
public getInvoiceMailReminder(tenantId: number, saleInvoiceId: number) {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { GetSaleInvoiceMailStateTransformer } from './GetSaleInvoiceMailState.transformer';
|
||||
import { SendSaleInvoiceMailCommon } from '../commands/SendInvoiceInvoiceMailCommon.service';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { SaleInvoiceMailState } from '../SaleInvoice.types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleInvoiceMailState {
|
||||
constructor(
|
||||
private transformer: TransformerInjectable,
|
||||
private invoiceMail: SendSaleInvoiceMailCommon,
|
||||
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 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} saleInvoiceId - Sale invoice id.
|
||||
* @returns {Promise<SaleInvoiceMailState>}
|
||||
*/
|
||||
public async getInvoiceMailState(
|
||||
saleInvoiceId: number,
|
||||
): Promise<SaleInvoiceMailState> {
|
||||
const saleInvoice = await this.saleInvoiceModel()
|
||||
.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('customer')
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('pdfTemplate')
|
||||
.throwIfNotFound();
|
||||
|
||||
const mailOptions =
|
||||
await this.invoiceMail.getInvoiceMailOptions(saleInvoiceId);
|
||||
|
||||
// Transforms the sale invoice mail state.
|
||||
const transformed = await this.transformer.transform(
|
||||
saleInvoice,
|
||||
new GetSaleInvoiceMailStateTransformer(),
|
||||
{
|
||||
mailOptions,
|
||||
},
|
||||
);
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import { ItemEntryTransformer } from '@/modules/TransactionItemEntry/ItemEntry.transformer';
|
||||
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
|
||||
|
||||
export class GetSaleInvoiceMailStateTransformer extends SaleInvoiceTransformer {
|
||||
/**
|
||||
* Exclude these attributes from user object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Included attributes.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'invoiceDate',
|
||||
'invoiceDateFormatted',
|
||||
|
||||
'dueDate',
|
||||
'dueDateFormatted',
|
||||
|
||||
'dueAmount',
|
||||
'dueAmountFormatted',
|
||||
|
||||
'total',
|
||||
'totalFormatted',
|
||||
|
||||
'subtotal',
|
||||
'subtotalFormatted',
|
||||
|
||||
'invoiceNo',
|
||||
|
||||
'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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the primary color.
|
||||
* @returns {string}
|
||||
*/
|
||||
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',
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
|
||||
import { ISaleInvocieState } from '../SaleInvoice.types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleInvoiceState {
|
||||
constructor(
|
||||
@Inject(PdfTemplateModel.name)
|
||||
private pdfTemplateModel: TenantModelProxy<typeof PdfTemplateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the create/edit invoice state.
|
||||
* @return {Promise<ISaleInvoice>}
|
||||
*/
|
||||
public async getSaleInvoiceState(): Promise<ISaleInvocieState> {
|
||||
const defaultPdfTemplate = await this.pdfTemplateModel()
|
||||
.query()
|
||||
.findOne({ resource: 'SaleInvoice' })
|
||||
.modify('default');
|
||||
|
||||
return {
|
||||
defaultTemplateId: defaultPdfTemplate?.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import * as R from 'ramda';
|
||||
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
|
||||
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { ISalesInvoicesFilter } from '../SaleInvoice.types';
|
||||
import { Knex } from 'knex';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleInvoicesService {
|
||||
constructor(
|
||||
private readonly dynamicListService: DynamicListService,
|
||||
private readonly transformer: TransformerInjectable,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve sales invoices filterable and paginated list.
|
||||
* @param {ISalesInvoicesFilter} filterDTO -
|
||||
* @returns {Promise<{ salesInvoices: SaleInvoice[]; pagination: IPaginationMeta; filterMeta: IFilterMeta; }>}
|
||||
*/
|
||||
public async getSaleInvoices(filterDTO: ISalesInvoicesFilter): Promise<{
|
||||
salesInvoices: SaleInvoice[];
|
||||
pagination: IPaginationMeta;
|
||||
filterMeta: IFilterMeta;
|
||||
}> {
|
||||
// Parses stringified filter roles.
|
||||
const filter = this.parseListFilterDTO(filterDTO);
|
||||
|
||||
// Dynamic list service.
|
||||
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||
SaleInvoice,
|
||||
filter,
|
||||
);
|
||||
const { results, pagination } = await SaleInvoice.query()
|
||||
.onBuild((builder) => {
|
||||
builder.withGraphFetched('entries.item');
|
||||
builder.withGraphFetched('customer');
|
||||
|
||||
dynamicFilter.buildQuery()(builder);
|
||||
filterDTO?.filterQuery?.(builder as any);
|
||||
})
|
||||
.pagination(filter.page - 1, filter.pageSize);
|
||||
|
||||
// Retrieves the transformed sale invoices.
|
||||
const salesInvoices = await this.transformer.transform(
|
||||
results,
|
||||
new SaleInvoiceTransformer(),
|
||||
);
|
||||
|
||||
return {
|
||||
salesInvoices,
|
||||
pagination,
|
||||
filterMeta: dynamicFilter.getResponseMeta(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the sale invoice list filter DTO.
|
||||
* @param filterDTO
|
||||
* @returns
|
||||
*/
|
||||
private parseListFilterDTO(filterDTO) {
|
||||
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class GetSaleInvoicesPayable {
|
||||
constructor(
|
||||
@Inject(SaleInvoice.name)
|
||||
private readonly saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve due sales invoices.
|
||||
* @param {number} customerId - Customer id.
|
||||
*/
|
||||
public async getPayableInvoices(
|
||||
customerId?: number,
|
||||
): Promise<Array<SaleInvoice>> {
|
||||
const salesInvoices = await this.saleInvoiceModel()
|
||||
.query()
|
||||
.onBuild((query) => {
|
||||
query.modify('dueInvoices');
|
||||
query.modify('delivered');
|
||||
|
||||
if (customerId) {
|
||||
query.where('customer_id', customerId);
|
||||
}
|
||||
});
|
||||
return salesInvoices;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Transformer } from "../../Transformer/Transformer";
|
||||
|
||||
export class InvoicePaymentTransactionTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale credit note object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['formattedPaymentAmount', 'formattedPaymentDate'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted invoice amount.
|
||||
* @param {ICreditNote} credit
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedPaymentAmount = (entry): string => {
|
||||
return this.formatNumber(entry.paymentAmount, {
|
||||
currencyCode: entry.payment.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Formatted payment date.
|
||||
* @param entry
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedPaymentDate = (entry): string => {
|
||||
return this.formatDate(entry.payment.paymentDate);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entry
|
||||
* @returns
|
||||
*/
|
||||
public transform = (entry) => {
|
||||
return {
|
||||
invoiceId: entry.invoiceId,
|
||||
paymentReceiveId: entry.paymentReceiveId,
|
||||
|
||||
paymentDate: entry.payment.paymentDate,
|
||||
formattedPaymentDate: entry.formattedPaymentDate,
|
||||
|
||||
paymentAmount: entry.paymentAmount,
|
||||
formattedPaymentAmount: entry.formattedPaymentAmount,
|
||||
currencyCode: entry.payment.currencyCode,
|
||||
|
||||
paymentNumber: entry.payment.paymentReceiveNo,
|
||||
paymentReferenceNo: entry.payment.referenceNo,
|
||||
|
||||
invoiceNumber: entry.invoice.invoiceNo,
|
||||
invoiceReferenceNo: entry.invoice.referenceNo,
|
||||
|
||||
depositAccountId: entry.payment.depositAccountId,
|
||||
depositAccountName: entry.payment.depositAccount.name,
|
||||
depositAccountSlug: entry.payment.depositAccount.slug,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { mergePdfTemplateWithDefaultAttributes } from '../utils';
|
||||
import { GetPdfTemplateService } from '@/modules/PdfTemplate/queries/GetPdfTemplate.service';
|
||||
import { GetOrganizationBrandingAttributesService } from '@/modules/PdfTemplate/queries/GetOrganizationBrandingAttributes.service';
|
||||
import { defaultEstimatePdfBrandingAttributes } from '@/modules/SaleEstimates/constants';
|
||||
|
||||
@Injectable()
|
||||
export class SaleEstimatePdfTemplate {
|
||||
constructor(
|
||||
private readonly getPdfTemplateService: GetPdfTemplateService,
|
||||
private readonly getOrgBrandingAttrs: GetOrganizationBrandingAttributesService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the estimate pdf template.
|
||||
* @param {number} invoiceTemplateId
|
||||
* @returns
|
||||
*/
|
||||
public async getEstimatePdfTemplate(estimateTemplateId: number) {
|
||||
const template =
|
||||
await this.getPdfTemplateService.getPdfTemplate(estimateTemplateId);
|
||||
// Retreives the organization branding attributes.
|
||||
const commonOrgBrandingAttrs =
|
||||
await this.getOrgBrandingAttrs.getOrganizationBrandingAttributes();
|
||||
|
||||
// Merge the default branding attributes with organization attrs.
|
||||
const orgainizationBrandingAttrs = {
|
||||
...defaultEstimatePdfBrandingAttributes,
|
||||
...commonOrgBrandingAttrs,
|
||||
};
|
||||
const brandingTemplateAttrs = {
|
||||
...template.attributes,
|
||||
companyLogoUri: template.companyLogoUri,
|
||||
};
|
||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||
brandingTemplateAttrs,
|
||||
orgainizationBrandingAttrs,
|
||||
);
|
||||
return {
|
||||
...template,
|
||||
attributes,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { ItemEntryTransformer } from '../../TransactionItemEntry/ItemEntry.transformer';
|
||||
import { AttachmentTransformer } from '../../Attachments/Attachment.transformer';
|
||||
import { SaleInvoiceTaxEntryTransformer } from './SaleInvoiceTaxEntry.transformer';
|
||||
|
||||
export class SaleInvoiceTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'invoiceDateFormatted',
|
||||
'dueDateFormatted',
|
||||
'createdAtFormatted',
|
||||
'dueAmountFormatted',
|
||||
'paymentAmountFormatted',
|
||||
'balanceAmountFormatted',
|
||||
'exchangeRateFormatted',
|
||||
'subtotalFormatted',
|
||||
'subtotalLocalFormatted',
|
||||
'subtotalExludingTaxFormatted',
|
||||
'taxAmountWithheldFormatted',
|
||||
'taxAmountWithheldLocalFormatted',
|
||||
'totalFormatted',
|
||||
'totalLocalFormatted',
|
||||
'taxes',
|
||||
'entries',
|
||||
'attachments',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted invoice date.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {String}
|
||||
*/
|
||||
protected invoiceDateFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatDate(invoice.invoiceDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted due date.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected dueDateFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatDate(invoice.dueDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the formatted created at date.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected createdAtFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatDate(invoice.createdAt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted invoice due amount.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected dueAmountFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.dueAmount, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted payment amount.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected paymentAmountFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.paymentAmount, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the formatted invoice balance.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected balanceAmountFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.balanceAmount, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the formatted exchange rate.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected exchangeRateFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.exchangeRate, { money: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted subtotal in base currency.
|
||||
* (Tax inclusive if the tax inclusive is enabled)
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.subtotal, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
money: false,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted subtotal in foreign currency.
|
||||
* (Tax inclusive if the tax inclusive is enabled)
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalLocalFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.subtotalLocal, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted subtotal excluding tax in foreign currency.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalExludingTaxFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.subtotalExludingTax, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted tax amount withheld in foreign currency.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected taxAmountWithheldFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.taxAmountWithheld, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted tax amount withheld in base currency.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected taxAmountWithheldLocalFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.taxAmountWithheldLocal, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted total in foreign currency.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected totalFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.total, {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves formatted total in base currency.
|
||||
* @param invoice
|
||||
* @returns {string}
|
||||
*/
|
||||
protected totalLocalFormatted = (invoice: SaleInvoice): string => {
|
||||
return this.formatNumber(invoice.totalLocal, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the taxes lines of sale invoice.
|
||||
* @param {ISaleInvoice} invoice
|
||||
*/
|
||||
protected taxes = (invoice: SaleInvoice) => {
|
||||
return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), {
|
||||
subtotal: invoice.subtotal,
|
||||
isInclusiveTax: invoice.isInclusiveTax,
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the entries of the sale invoice.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns {}
|
||||
*/
|
||||
protected entries = (invoice: SaleInvoice) => {
|
||||
return this.item(invoice.entries, new ItemEntryTransformer(), {
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the sale invoice attachments.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @returns
|
||||
*/
|
||||
protected attachments = (invoice: SaleInvoice) => {
|
||||
return this.item(invoice.attachments, new AttachmentTransformer());
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { renderInvoicePaperTemplateHtml } from '@bigcapital/pdf-templates';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { GetSaleInvoice } from './GetSaleInvoice.service';
|
||||
import { transformInvoiceToPdfTemplate } from '../utils';
|
||||
import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate.service';
|
||||
import { ChromiumlyTenancy } from '@/modules/ChromiumlyTenancy/ChromiumlyTenancy.service';
|
||||
import { SaleInvoice } from '../models/SaleInvoice';
|
||||
import { PdfTemplateModel } from '@/modules/PdfTemplate/models/PdfTemplate';
|
||||
import { events } from '@/common/events/events';
|
||||
import { InvoicePdfTemplateAttributes } from '../SaleInvoice.types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class SaleInvoicePdf {
|
||||
constructor(
|
||||
private chromiumlyTenancy: ChromiumlyTenancy,
|
||||
private getInvoiceService: GetSaleInvoice,
|
||||
private invoiceBrandingTemplateService: SaleInvoicePdfTemplate,
|
||||
private eventPublisher: EventEmitter2,
|
||||
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||
|
||||
@Inject(PdfTemplateModel.name)
|
||||
private pdfTemplateModel: TenantModelProxy<typeof PdfTemplateModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice html content.
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
public async getSaleInvoiceHtml(invoiceId: number): Promise<string> {
|
||||
const brandingAttributes =
|
||||
await this.getInvoiceBrandingAttributes(invoiceId);
|
||||
|
||||
return renderInvoicePaperTemplateHtml({
|
||||
...brandingAttributes,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve sale invoice pdf content.
|
||||
* @param {ISaleInvoice} saleInvoice -
|
||||
* @returns {Promise<[Buffer, string]>}
|
||||
*/
|
||||
public async getSaleInvoicePdf(invoiceId: number): Promise<[Buffer, string]> {
|
||||
const filename = await this.getInvoicePdfFilename(invoiceId);
|
||||
|
||||
const htmlContent = await this.getSaleInvoiceHtml(invoiceId);
|
||||
|
||||
// Converts the given html content to pdf document.
|
||||
const buffer = await this.chromiumlyTenancy.convertHtmlContent(htmlContent);
|
||||
const eventPayload = { saleInvoiceId: invoiceId };
|
||||
|
||||
// Triggers the `onSaleInvoicePdfViewed` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.saleInvoice.onPdfViewed,
|
||||
eventPayload,
|
||||
);
|
||||
return [buffer, filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the filename pdf document of the given invoice.
|
||||
* @param {number} invoiceId
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
private async getInvoicePdfFilename(invoiceId: number): Promise<string> {
|
||||
const invoice = await this.saleInvoiceModel().query().findById(invoiceId);
|
||||
return `Invoice-${invoice.invoiceNo}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the branding attributes of the given sale invoice.
|
||||
* @param {number} invoiceId
|
||||
* @returns {Promise<InvoicePdfTemplateAttributes>}
|
||||
*/
|
||||
private async getInvoiceBrandingAttributes(
|
||||
invoiceId: number,
|
||||
): Promise<InvoicePdfTemplateAttributes> {
|
||||
const invoice = await this.getInvoiceService.getSaleInvoice(invoiceId);
|
||||
|
||||
// Retrieve the invoice template id or get the default template id if not found.
|
||||
const templateId =
|
||||
invoice.pdfTemplateId ??
|
||||
(
|
||||
await this.pdfTemplateModel().query().findOne({
|
||||
resource: 'SaleInvoice',
|
||||
default: true,
|
||||
})
|
||||
)?.id;
|
||||
|
||||
// Get the branding template attributes.
|
||||
const brandingTemplate =
|
||||
await this.invoiceBrandingTemplateService.getInvoicePdfTemplate(
|
||||
templateId,
|
||||
);
|
||||
|
||||
// Merge the branding template attributes with the invoice.
|
||||
return {
|
||||
...brandingTemplate.attributes,
|
||||
...transformInvoiceToPdfTemplate(invoice),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { mergePdfTemplateWithDefaultAttributes } from '../utils';
|
||||
import { defaultInvoicePdfTemplateAttributes } from '../constants';
|
||||
import { GetOrganizationBrandingAttributesService } from '@/modules/PdfTemplate/queries/GetOrganizationBrandingAttributes.service';
|
||||
import { GetPdfTemplateService } from '@/modules/PdfTemplate/queries/GetPdfTemplate.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class SaleInvoicePdfTemplate {
|
||||
constructor(
|
||||
private readonly getPdfTemplateService: GetPdfTemplateService,
|
||||
private readonly getOrgBrandingAttributes: GetOrganizationBrandingAttributesService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the invoice pdf template.
|
||||
* @param {number} invoiceTemplateId
|
||||
* @returns
|
||||
*/
|
||||
async getInvoicePdfTemplate(invoiceTemplateId: number) {
|
||||
const template =
|
||||
await this.getPdfTemplateService.getPdfTemplate(invoiceTemplateId);
|
||||
// Retrieves the organization branding attributes.
|
||||
const commonOrgBrandingAttrs =
|
||||
await this.getOrgBrandingAttributes.getOrganizationBrandingAttributes();
|
||||
|
||||
const organizationBrandingAttrs = {
|
||||
...defaultInvoicePdfTemplateAttributes,
|
||||
...commonOrgBrandingAttrs,
|
||||
};
|
||||
const brandingTemplateAttrs = {
|
||||
...template.attributes,
|
||||
companyLogoUri: template.companyLogoUri,
|
||||
};
|
||||
const attributes = mergePdfTemplateWithDefaultAttributes(
|
||||
brandingTemplateAttrs,
|
||||
organizationBrandingAttrs,
|
||||
);
|
||||
return {
|
||||
...template,
|
||||
attributes,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
|
||||
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '../../TaxRates/utils';
|
||||
|
||||
export class SaleInvoiceTaxEntryTransformer extends Transformer {
|
||||
/**
|
||||
* Included attributes.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'name',
|
||||
'taxRateCode',
|
||||
'taxRate',
|
||||
'taxRateId',
|
||||
'taxRateAmount',
|
||||
'taxRateAmountFormatted',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclude attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve tax rate code.
|
||||
* @param taxEntry
|
||||
* @returns {string}
|
||||
*/
|
||||
protected taxRateCode = (taxEntry) => {
|
||||
return taxEntry.taxRate.code;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve tax rate id.
|
||||
* @param taxEntry
|
||||
* @returns {number}
|
||||
*/
|
||||
protected taxRate = (taxEntry) => {
|
||||
return taxEntry.taxAmount || taxEntry.taxRate.rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve tax rate name.
|
||||
* @param taxEntry
|
||||
* @returns {string}
|
||||
*/
|
||||
protected name = (taxEntry) => {
|
||||
return taxEntry.taxRate.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve tax rate amount.
|
||||
* @param taxEntry
|
||||
*/
|
||||
protected taxRateAmount = (taxEntry) => {
|
||||
const taxRate = this.taxRate(taxEntry);
|
||||
|
||||
return this.options.isInclusiveTax
|
||||
? getInclusiveTaxAmount(this.options.subtotal, taxRate)
|
||||
: getExlusiveTaxAmount(this.options.subtotal, taxRate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted tax rate amount.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected taxRateAmountFormatted = (taxEntry) => {
|
||||
return this.formatNumber(this.taxRateAmount(taxEntry), {
|
||||
currencyCode: this.options.currencyCode,
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user