feat(server): add pdf template crud endpoints

This commit is contained in:
Ahmed Bouhuolia
2024-09-11 14:54:13 +02:00
parent 5b6270a184
commit c0769662bd
14 changed files with 542 additions and 2 deletions

View File

@@ -0,0 +1,45 @@
import { Inject, Service } from 'typedi';
import { ICreateInvoicePdfTemplateDTO } from './types';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class CreatePdfTemplate {
@Inject()
private tennacy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Creates a new pdf template.
* @param {number} tenantId
* @param {ICreateInvoicePdfTemplateDTO} invoiceTemplateDTO
*/
public createPdfTemplate(
tenantId: number,
templateName: string,
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO
) {
const { PdfTemplate } = this.tennacy.models(tenantId);
const resource = 'SaleInvoice';
const attributes = invoiceTemplateDTO;
return this.uow.withTransaction(tenantId, async (trx) => {
await PdfTemplate.query(trx).insert({
templateName,
resource,
attributes,
});
await this.eventPublisher.emitAsync(events.pdfTemplate.onInvoiceCreated, {
tenantId,
});
});
}
}

View File

@@ -0,0 +1,35 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class DeletePdfTemplate {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Deletes a pdf template.
* @param {number} tenantId
* @param {number} templateId
*/
public deletePdfTemplate(tenantId: number, templateId: number) {
const { PdfTemplate } = this.tenancy.models(tenantId);
return this.uow.withTransaction(tenantId, async (trx) => {
await PdfTemplate.query(trx).deleteById(templateId);
await this.eventPublisher.emitAsync(events.pdfTemplate.onDeleted, {
tenantId,
templateId,
});
});
}
}

View File

@@ -0,0 +1,45 @@
import { Inject, Service } from 'typedi';
import { IEditPdfTemplateDTO } from './types';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class EditPdfTemplate {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Edits an existing pdf template.
* @param {number} tenantId
* @param {number} templateId
* @param {IEditPdfTemplateDTO} editTemplateDTO
*/
public editPdfTemplate(
tenantId: number,
templateId: number,
editTemplateDTO: IEditPdfTemplateDTO
) {
const { PdfTemplate } = this.tenancy.models(tenantId);
return this.uow.withTransaction(tenantId, async (trx) => {
await PdfTemplate.query(trx)
.patch({
...editTemplateDTO,
})
.where('id', templateId);
await this.eventPublisher.emitAsync(events.pdfTemplate.onEdited, {
tenantId,
templateId,
});
});
}
}

View File

@@ -0,0 +1,29 @@
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
@Service()
export class GetPdfTemplate {
@Inject()
private tenancy: HasTenancyService;
/**
* Retrieves a pdf template by its ID.
* @param {number} tenantId - The ID of the tenant.
* @param {number} templateId - The ID of the pdf template to retrieve.
* @return {Promise<any>} - The retrieved pdf template.
*/
async getPdfTemplate(
tenantId: number,
templateId: number,
trx?: Knex.Transaction
): Promise<any> {
const { PdfTemplate } = this.tenancy.models(tenantId);
const template = await PdfTemplate.query(trx)
.findById(templateId)
.throwIfNotFound();
return template;
}
}

View File

@@ -0,0 +1,37 @@
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import HasTenancyService from '../Tenancy/TenancyService';
import { GetPdfTemplatesTransformer } from './GetPdfTemplatesTransformer';
import { Inject, Service } from 'typedi';
@Service()
export class GetPdfTemplates {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private transformInjectable: TransformerInjectable;
/**
* Retrieves a list of PDF templates for a specified tenant.
* @param {number} tenantId - The ID of the tenant for which to retrieve templates.
* @param {Object} [query] - Optional query parameters to filter the templates.
* @param {string} [query.resource] - The resource type to filter the templates by.
* @returns {Promise<any>} - A promise that resolves to the transformed list of PDF templates.
*/
async getPdfTemplates(tenantId: number, query?: { resource?: string }) {
const { PdfTemplate } = this.tenancy.models(tenantId);
const templates = await PdfTemplate.query().onBuild((q) => {
if (query?.resource) {
q.where('resource', query?.resource);
}
q.orderBy('createdAt', 'ASC');
});
return this.transformInjectable.transform(
tenantId,
templates,
new GetPdfTemplatesTransformer()
);
}
}

View File

@@ -0,0 +1,13 @@
import { Transformer } from '@/lib/Transformer/Transformer';
export class GetPdfTemplatesTransformer extends Transformer {
// Empty transformer with no additional methods or attributes
/**
* Exclude attributes.
* @returns {string[]}
*/
public excludeAttributes = (): string[] => {
return ['attributes'];
};
}

View File

@@ -0,0 +1,67 @@
import { Inject, Service } from 'typedi';
import { ICreateInvoicePdfTemplateDTO } from './types';
import { CreatePdfTemplate } from './CreatePdfTemplate';
import { DeletePdfTemplate } from './DeletePdfTemplate';
import { GetPdfTemplate } from './GetPdfTemplate';
import { GetPdfTemplates } from './GetPdfTemplates';
import { EditPdfTemplate } from './EditPdfTemplate';
@Service()
export class PdfTemplateApplication {
@Inject()
private createPdfTemplateService: CreatePdfTemplate;
@Inject()
private deletePdfTemplateService: DeletePdfTemplate;
@Inject()
private getPdfTemplateService: GetPdfTemplate;
@Inject()
private getPdfTemplatesService: GetPdfTemplates;
@Inject()
private editPdfTemplateService: EditPdfTemplate;
public async createPdfTemplate(
tenantId: number,
templateName: string,
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO
) {
return this.createPdfTemplateService.createPdfTemplate(
tenantId,
templateName,
invoiceTemplateDTO
);
}
public async editPdfTemplate(
tenantId: number,
templateId: number,
editTemplateDTO: IEditPdfTemplateDTO
) {
return this.editPdfTemplateService.editPdfTemplate(
tenantId,
templateId,
editTemplateDTO
);
}
public async deletePdfTemplate(tenantId: number, templateId: number) {
return this.deletePdfTemplateService.deletePdfTemplate(
tenantId,
templateId
);
}
public async getPdfTemplate(tenantId: number, templateId: number) {
return this.getPdfTemplateService.getPdfTemplate(tenantId, templateId);
}
public async getPdfTemplates(
tenantId: number,
query?: { resource?: string }
) {
return this.getPdfTemplatesService.getPdfTemplates(tenantId, query);
}
}

View File

@@ -0,0 +1,58 @@
export interface ICreateInvoicePdfTemplateDTO {
// Colors
primaryColor?: string;
secondaryColor?: string;
// Company Logo
showCompanyLogo?: boolean;
companyLogo?: string;
// Top details.
showInvoiceNumber?: boolean;
invoiceNumberLabel?: string;
showDateIssue?: boolean;
dateIssueLabel?: string;
showDueDate?: boolean;
dueDateLabel?: string;
// Company name
companyName?: string;
// Addresses
showBilledFromAddress?: boolean;
showBillingToAddress?: boolean;
billedToLabel?: string;
// Entries
itemNameLabel?: string;
itemDescriptionLabel?: string;
itemRateLabel?: string;
itemTotalLabel?: string;
// Totals
showSubtotal?: boolean;
subtotalLabel?: string;
showDiscount?: boolean;
discountLabel?: string;
showTaxes?: boolean;
showTotal?: boolean;
totalLabel?: string;
paymentMadeLabel?: string;
showPaymentMade?: boolean;
dueAmountLabel?: string;
showDueAmount?: boolean;
// Footer paragraphs.
termsConditionsLabel?: string;
showTermsConditions?: boolean;
statementLabel?: string;
showStatement?: boolean;
}