mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
46
packages/server/src/modules/Export/Export.controller.ts
Normal file
46
packages/server/src/modules/Export/Export.controller.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Response } from 'express';
|
||||
import { convertAcceptFormatToFormat } from './_utils';
|
||||
import { Controller, Headers, Query, Res } from '@nestjs/common';
|
||||
import { ExportQuery } from './dtos/ExportQuery.dto';
|
||||
import { ExportResourceService } from './ExportService';
|
||||
import { AcceptType } from '@/constants/accept-type';
|
||||
|
||||
@Controller('/export')
|
||||
export class ExportController {
|
||||
constructor(private readonly exportResourceApp: ExportResourceService) {}
|
||||
|
||||
async export(
|
||||
@Query() query: ExportQuery,
|
||||
@Res() res: Response,
|
||||
@Headers('accept') acceptHeader: string,
|
||||
) {
|
||||
const applicationFormat = convertAcceptFormatToFormat(acceptType);
|
||||
|
||||
const data = await this.exportResourceApp.export(
|
||||
query.resource,
|
||||
applicationFormat,
|
||||
);
|
||||
// Retrieves the csv format.
|
||||
if (acceptHeader.includes(AcceptType.ApplicationCsv)) {
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
|
||||
return res.send(data);
|
||||
// Retrieves the xlsx format.
|
||||
} else if (acceptHeader.includes(AcceptType.ApplicationXlsx)) {
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
);
|
||||
return res.send(data);
|
||||
// Retrieve the pdf format.
|
||||
} else if (acceptHeader.includes(AcceptType.ApplicationPdf)) {
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Length': data.length,
|
||||
});
|
||||
res.send(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
packages/server/src/modules/Export/Export.module.ts
Normal file
12
packages/server/src/modules/Export/Export.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ExportController } from './Export.controller';
|
||||
import { ExportResourceService } from './ExportService';
|
||||
import { ExportPdf } from './ExportPdf';
|
||||
import { ExportAls } from './ExportAls';
|
||||
import { ExportApplication } from './ExportApplication';
|
||||
|
||||
@Module({
|
||||
providers: [ExportResourceService, ExportPdf, ExportAls, ExportApplication],
|
||||
controllers: [ExportController],
|
||||
})
|
||||
export class ExportModule {}
|
||||
48
packages/server/src/modules/Export/ExportAls.ts
Normal file
48
packages/server/src/modules/Export/ExportAls.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AsyncLocalStorage } from 'async_hooks';
|
||||
|
||||
@Injectable()
|
||||
export class ExportAls {
|
||||
private als: AsyncLocalStorage<Map<string, any>>;
|
||||
|
||||
constructor() {
|
||||
this.als = new AsyncLocalStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a callback function within the context of a new AsyncLocalStorage store.
|
||||
* @param callback The function to be executed within the AsyncLocalStorage context.
|
||||
* @returns The result of the callback function.
|
||||
*/
|
||||
public run<T>(callback: () => T): T {
|
||||
return this.als.run<T>(new Map(), () => {
|
||||
this.markAsExport();
|
||||
|
||||
return callback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current AsyncLocalStorage store.
|
||||
* @returns The current store or undefined if not in a valid context.
|
||||
*/
|
||||
public getStore(): Map<string, any> | undefined {
|
||||
return this.als.getStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current context as an export operation.
|
||||
* @param flag Boolean flag to set or unset the export status. Defaults to true.
|
||||
*/
|
||||
public markAsExport(flag: boolean = true): void {
|
||||
const store = this.getStore();
|
||||
store?.set('isExport', flag);
|
||||
}
|
||||
/**
|
||||
* Checks if the current context is an export operation.
|
||||
* @returns {boolean} True if the context is an export operation, false otherwise.
|
||||
*/
|
||||
public get isExport(): boolean {
|
||||
return !!this.getStore()?.get('isExport');
|
||||
}
|
||||
}
|
||||
22
packages/server/src/modules/Export/ExportApplication.ts
Normal file
22
packages/server/src/modules/Export/ExportApplication.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ExportResourceService } from './ExportService';
|
||||
import { ExportFormat } from './common';
|
||||
|
||||
@Injectable()
|
||||
export class ExportApplication {
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor(
|
||||
private readonly exportResource: ExportResourceService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Exports the given resource to csv, xlsx or pdf format.
|
||||
* @param {string} reosurce
|
||||
* @param {ExportFormat} format
|
||||
*/
|
||||
public export(resource: string, format: ExportFormat) {
|
||||
return this.exportResource.export(resource, format);
|
||||
}
|
||||
}
|
||||
43
packages/server/src/modules/Export/ExportPdf.ts
Normal file
43
packages/server/src/modules/Export/ExportPdf.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ChromiumlyTenancy } from '../ChromiumlyTenancy/ChromiumlyTenancy.service';
|
||||
import { TemplateInjectable } from '../TemplateInjectable/TemplateInjectable.service';
|
||||
import { mapPdfRows } from './utils';
|
||||
|
||||
@Injectable()
|
||||
export class ExportPdf {
|
||||
constructor(
|
||||
private readonly templateInjectable: TemplateInjectable,
|
||||
private readonly chromiumlyTenancy: ChromiumlyTenancy,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Generates the pdf table sheet for the given data and columns.
|
||||
* @param {} columns
|
||||
* @param {Record<string, string>} data
|
||||
* @param {string} sheetTitle
|
||||
* @param {string} sheetDescription
|
||||
* @returns
|
||||
*/
|
||||
public async pdf(
|
||||
columns: { accessor: string },
|
||||
data: Record<string, any>,
|
||||
sheetTitle: string = '',
|
||||
sheetDescription: string = ''
|
||||
) {
|
||||
const rows = mapPdfRows(columns, data);
|
||||
|
||||
const htmlContent = await this.templateInjectable.render(
|
||||
'modules/export-resource-table',
|
||||
{
|
||||
table: { rows, columns },
|
||||
sheetTitle,
|
||||
sheetDescription,
|
||||
}
|
||||
);
|
||||
// Convert the HTML content to PDF
|
||||
return this.chromiumlyTenancy.convertHtmlContent(htmlContent, {
|
||||
margins: { top: 0.2, bottom: 0.2, left: 0.2, right: 0.2 },
|
||||
landscape: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
50
packages/server/src/modules/Export/ExportRegistery.ts
Normal file
50
packages/server/src/modules/Export/ExportRegistery.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
// @ts-nocheck
|
||||
import { camelCase, upperFirst } from 'lodash';
|
||||
import { Exportable } from './Exportable';
|
||||
|
||||
export class ExportableRegistry {
|
||||
private static instance: ExportableRegistry;
|
||||
private exportables: Record<string, Exportable>;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
constructor() {
|
||||
this.exportables = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets singleton instance of registry.
|
||||
* @returns {ExportableRegistry}
|
||||
*/
|
||||
public static getInstance(): ExportableRegistry {
|
||||
if (!ExportableRegistry.instance) {
|
||||
ExportableRegistry.instance = new ExportableRegistry();
|
||||
}
|
||||
return ExportableRegistry.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given importable service.
|
||||
* @param {string} resource
|
||||
* @param {Exportable} importable
|
||||
*/
|
||||
public registerExportable(resource: string, importable: Exportable): void {
|
||||
const _resource = this.sanitizeResourceName(resource);
|
||||
this.exportables[_resource] = importable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the importable service instance of the given resource name.
|
||||
* @param {string} name
|
||||
* @returns {Exportable}
|
||||
*/
|
||||
public getExportable(name: string): Exportable {
|
||||
const _name = this.sanitizeResourceName(name);
|
||||
return this.exportables[_name];
|
||||
}
|
||||
|
||||
private sanitizeResourceName(resource: string) {
|
||||
return upperFirst(camelCase(resource));
|
||||
}
|
||||
}
|
||||
75
packages/server/src/modules/Export/ExportResources.ts
Normal file
75
packages/server/src/modules/Export/ExportResources.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
// import Container, { Service } from 'typedi';
|
||||
// import { AccountsExportable } from '../Accounts/AccountsExportable';
|
||||
// import { ExportableRegistry } from './ExportRegistery';
|
||||
// import { ItemsExportable } from '../Items/ItemsExportable';
|
||||
// import { CustomersExportable } from '../Contacts/Customers/CustomersExportable';
|
||||
// import { VendorsExportable } from '../Contacts/Vendors/VendorsExportable';
|
||||
// import { ExpensesExportable } from '../Expenses/ExpensesExportable';
|
||||
// import { SaleInvoicesExportable } from '../Sales/Invoices/SaleInvoicesExportable';
|
||||
// import { SaleEstimatesExportable } from '../Sales/Estimates/SaleEstimatesExportable';
|
||||
// import { SaleReceiptsExportable } from '../Sales/Receipts/SaleReceiptsExportable';
|
||||
// import { BillsExportable } from '../Purchases/Bills/BillsExportable';
|
||||
// import { PaymentsReceivedExportable } from '../Sales/PaymentReceived/PaymentsReceivedExportable';
|
||||
// import { BillPaymentExportable } from '../Purchases/BillPayments/BillPaymentExportable';
|
||||
// import { ManualJournalsExportable } from '../ManualJournals/ManualJournalExportable';
|
||||
// import { CreditNotesExportable } from '../CreditNotes/CreditNotesExportable';
|
||||
// import { VendorCreditsExportable } from '../Purchases/VendorCredits/VendorCreditsExportable';
|
||||
// import { ItemCategoriesExportable } from '../ItemCategories/ItemCategoriesExportable';
|
||||
// import { TaxRatesExportable } from '../TaxRates/TaxRatesExportable';
|
||||
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { ExportableRegistry } from "./ExportRegistery";
|
||||
import { AccountsExportable } from "../Accounts/AccountsExportable.service";
|
||||
|
||||
@Injectable()
|
||||
export class ExportableResources {
|
||||
constructor(
|
||||
private readonly exportRegistry: ExportableRegistry,
|
||||
) {
|
||||
this.boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Importable instances.
|
||||
*/
|
||||
private importables = [
|
||||
{ resource: 'Account', exportable: AccountsExportable },
|
||||
// { resource: 'Item', exportable: ItemsExportable },
|
||||
// { resource: 'ItemCategory', exportable: ItemCategoriesExportable },
|
||||
// { resource: 'Customer', exportable: CustomersExportable },
|
||||
// { resource: 'Vendor', exportable: VendorsExportable },
|
||||
// { resource: 'Expense', exportable: ExpensesExportable },
|
||||
// { resource: 'SaleInvoice', exportable: SaleInvoicesExportable },
|
||||
// { resource: 'SaleEstimate', exportable: SaleEstimatesExportable },
|
||||
// { resource: 'SaleReceipt', exportable: SaleReceiptsExportable },
|
||||
// { resource: 'Bill', exportable: BillsExportable },
|
||||
// { resource: 'PaymentReceive', exportable: PaymentsReceivedExportable },
|
||||
// { resource: 'BillPayment', exportable: BillPaymentExportable },
|
||||
// { resource: 'ManualJournal', exportable: ManualJournalsExportable },
|
||||
// { resource: 'CreditNote', exportable: CreditNotesExportable },
|
||||
// { resource: 'VendorCredit', exportable: VendorCreditsExportable },
|
||||
// { resource: 'TaxRate', exportable: TaxRatesExportable },
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public get registry() {
|
||||
return ExportableResources.registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots all the registered importables.
|
||||
*/
|
||||
public boot() {
|
||||
if (!ExportableResources.registry) {
|
||||
const instance = ExportableRegistry.getInstance();
|
||||
|
||||
this.importables.forEach((importable) => {
|
||||
const importableInstance = Container.get(importable.exportable);
|
||||
instance.registerExportable(importable.resource, importableInstance);
|
||||
});
|
||||
ExportableResources.registry = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
221
packages/server/src/modules/Export/ExportService.ts
Normal file
221
packages/server/src/modules/Export/ExportService.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
// @ts-nocheck
|
||||
import xlsx from 'xlsx';
|
||||
import * as R from 'ramda';
|
||||
import { get } from 'lodash';
|
||||
import { sanitizeResourceName } from '../Import/_utils';
|
||||
import { ExportableResources } from './ExportResources';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { Errors, ExportFormat } from './common';
|
||||
import { IModelMeta, IModelMetaColumn } from '@/interfaces';
|
||||
import { flatDataCollections, getDataAccessor } from './utils';
|
||||
import { ExportPdf } from './ExportPdf';
|
||||
import { ExportAls } from './ExportAls';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class ExportResourceService {
|
||||
constructor(
|
||||
private readonly exportAls: ExportAls,
|
||||
private readonly exportPdf: ExportPdf,
|
||||
private readonly exportableResources: ExportableResources,
|
||||
private readonly resourceService: ResourceService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} resourceName
|
||||
* @param {ExportFormat} format
|
||||
* @returns
|
||||
*/
|
||||
public async export(
|
||||
resourceName: string,
|
||||
format: ExportFormat = ExportFormat.Csv
|
||||
) {
|
||||
return this.exportAls.run(() =>
|
||||
this.exportAlsRun(resourceName, format)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the given resource data through csv, xlsx or pdf.
|
||||
* @param {string} resourceName - Resource name.
|
||||
* @param {ExportFormat} format - File format.
|
||||
*/
|
||||
public async exportAlsRun(
|
||||
resourceName: string,
|
||||
format: ExportFormat = ExportFormat.Csv
|
||||
) {
|
||||
const resource = sanitizeResourceName(resourceName);
|
||||
const resourceMeta = this.getResourceMeta(tenantId, resource);
|
||||
const resourceColumns = this.resourceService.getResourceColumns(
|
||||
tenantId,
|
||||
resource
|
||||
);
|
||||
this.validateResourceMeta(resourceMeta);
|
||||
|
||||
const data = await this.getExportableData(tenantId, resource);
|
||||
const transformed = this.transformExportedData(tenantId, resource, data);
|
||||
|
||||
// Returns the csv, xlsx format.
|
||||
if (format === ExportFormat.Csv || format === ExportFormat.Xlsx) {
|
||||
const exportableColumns = this.getExportableColumns(resourceColumns);
|
||||
const workbook = this.createWorkbook(transformed, exportableColumns);
|
||||
|
||||
return this.exportWorkbook(workbook, format);
|
||||
// Returns the pdf format.
|
||||
} else if (format === ExportFormat.Pdf) {
|
||||
const printableColumns = this.getPrintableColumns(resourceMeta);
|
||||
|
||||
return this.exportPdf.pdf(
|
||||
tenantId,
|
||||
printableColumns,
|
||||
transformed,
|
||||
resourceMeta?.print?.pageTitle
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves metadata for a specific resource.
|
||||
* @param {number} tenantId - The tenant identifier.
|
||||
* @param {string} resource - The name of the resource.
|
||||
* @returns The metadata of the resource.
|
||||
*/
|
||||
private getResourceMeta(resource: string) {
|
||||
return this.resourceService.getResourceMeta(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the resource metadata is exportable.
|
||||
* @param {any} resourceMeta - The metadata of the resource.
|
||||
* @throws {ServiceError} If the resource is not exportable or lacks columns.
|
||||
*/
|
||||
private validateResourceMeta(resourceMeta: any) {
|
||||
if (!resourceMeta.exportable || !resourceMeta.columns) {
|
||||
throw new ServiceError(Errors.RESOURCE_NOT_EXPORTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the exported data based on the resource metadata.
|
||||
* If the resource metadata specifies a flattening attribute (`exportFlattenOn`),
|
||||
* the data will be flattened based on this attribute using the `flatDataCollections` utility function.
|
||||
* @param {string} resource - The name of the resource.
|
||||
* @param {Array<Record<string, any>>} data - The original data to be transformed.
|
||||
* @returns {Array<Record<string, any>>} - The transformed data.
|
||||
*/
|
||||
private transformExportedData(
|
||||
resource: string,
|
||||
data: Array<Record<string, any>>
|
||||
): Array<Record<string, any>> {
|
||||
const resourceMeta = this.getResourceMeta(resource);
|
||||
|
||||
return R.when<Array<Record<string, any>>, Array<Record<string, any>>>(
|
||||
R.always(Boolean(resourceMeta.exportFlattenOn)),
|
||||
(data) => flatDataCollections(data, resourceMeta.exportFlattenOn),
|
||||
data
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Fetches exportable data for a given resource.
|
||||
* @param {number} tenantId - The tenant identifier.
|
||||
* @param {string} resource - The name of the resource.
|
||||
* @returns A promise that resolves to the exportable data.
|
||||
*/
|
||||
private async getExportableData(resource: string) {
|
||||
const exportable =
|
||||
this.exportableResources.registry.getExportable(resource);
|
||||
|
||||
return exportable.exportable({});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts columns that are marked as exportable from the resource metadata.
|
||||
* @param {IModelMeta} resourceMeta - The metadata of the resource.
|
||||
* @returns An array of exportable columns.
|
||||
*/
|
||||
private getExportableColumns(resourceColumns: any) {
|
||||
const processColumns = (
|
||||
columns: { [key: string]: IModelMetaColumn },
|
||||
parent = ''
|
||||
) => {
|
||||
return Object.entries(columns)
|
||||
.filter(([_, value]) => value.exportable !== false)
|
||||
.flatMap(([key, value]) => {
|
||||
if (value.type === 'collection' && value.collectionOf === 'object') {
|
||||
return processColumns(value.columns, key);
|
||||
} else {
|
||||
const group = parent;
|
||||
return [
|
||||
{
|
||||
name: value.name,
|
||||
type: value.type || 'text',
|
||||
accessor: value.accessor || key,
|
||||
group,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
};
|
||||
return processColumns(resourceColumns);
|
||||
}
|
||||
|
||||
private getPrintableColumns(resourceMeta: IModelMeta) {
|
||||
const processColumns = (
|
||||
columns: { [key: string]: IModelMetaColumn },
|
||||
parent = ''
|
||||
) => {
|
||||
return Object.entries(columns)
|
||||
.filter(([_, value]) => value.printable !== false)
|
||||
.flatMap(([key, value]) => {
|
||||
if (value.type === 'collection' && value.collectionOf === 'object') {
|
||||
return processColumns(value.columns, key);
|
||||
} else {
|
||||
const group = parent;
|
||||
return [
|
||||
{
|
||||
name: value.name,
|
||||
type: value.type || 'text',
|
||||
accessor: value.accessor || key,
|
||||
group,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
};
|
||||
return processColumns(resourceMeta.columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a workbook from the provided data and columns.
|
||||
* @param {any[]} data - The data to be included in the workbook.
|
||||
* @param {any[]} exportableColumns - The columns to be included in the workbook.
|
||||
* @returns The created workbook.
|
||||
*/
|
||||
private createWorkbook(data: any[], exportableColumns: any[]) {
|
||||
const workbook = xlsx.utils.book_new();
|
||||
const worksheetData = data.map((item) =>
|
||||
exportableColumns.map((col) => get(item, getDataAccessor(col)))
|
||||
);
|
||||
worksheetData.unshift(exportableColumns.map((col) => col.name));
|
||||
|
||||
const worksheet = xlsx.utils.aoa_to_sheet(worksheetData);
|
||||
xlsx.utils.book_append_sheet(workbook, worksheet, 'Exported Data');
|
||||
|
||||
return workbook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the workbook in the specified format.
|
||||
* @param {any} workbook - The workbook to be exported.
|
||||
* @param {string} format - The format to export the workbook in.
|
||||
* @returns The exported workbook data.
|
||||
*/
|
||||
private exportWorkbook(workbook: any, format: string) {
|
||||
if (format.toLowerCase() === 'csv') {
|
||||
return xlsx.write(workbook, { type: 'buffer', bookType: 'csv' });
|
||||
} else if (format.toLowerCase() === 'xlsx') {
|
||||
return xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' });
|
||||
}
|
||||
}
|
||||
}
|
||||
21
packages/server/src/modules/Export/Exportable.ts
Normal file
21
packages/server/src/modules/Export/Exportable.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export class Exportable {
|
||||
/**
|
||||
*
|
||||
* @param tenantId
|
||||
* @returns
|
||||
*/
|
||||
public async exportable(
|
||||
query: Record<string, any>,
|
||||
): Promise<Array<Record<string, any>>> {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
public transform(data: Record<string, any>) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
9
packages/server/src/modules/Export/common.ts
Normal file
9
packages/server/src/modules/Export/common.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export enum Errors {
|
||||
RESOURCE_NOT_EXPORTABLE = 'RESOURCE_NOT_EXPORTABLE',
|
||||
}
|
||||
|
||||
export enum ExportFormat {
|
||||
Csv = 'csv',
|
||||
Pdf = 'pdf',
|
||||
Xlsx = 'xlsx',
|
||||
}
|
||||
2
packages/server/src/modules/Export/constants.ts
Normal file
2
packages/server/src/modules/Export/constants.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const EXPORT_SIZE_LIMIT = 9999999;
|
||||
export const EXPORT_DTE_FORMAT = 'YYYY-MM-DD';
|
||||
11
packages/server/src/modules/Export/dtos/ExportQuery.dto.ts
Normal file
11
packages/server/src/modules/Export/dtos/ExportQuery.dto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class ExportQuery {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
resource: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
format: string;
|
||||
}
|
||||
45
packages/server/src/modules/Export/utils.ts
Normal file
45
packages/server/src/modules/Export/utils.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { flatMap, get } from 'lodash';
|
||||
/**
|
||||
* Flattens the data based on a specified attribute.
|
||||
* @param data - The data to be flattened.
|
||||
* @param flattenAttr - The attribute to be flattened.
|
||||
* @returns - The flattened data.
|
||||
*/
|
||||
export const flatDataCollections = (
|
||||
data: Record<string, any>,
|
||||
flattenAttr: string
|
||||
): Record<string, any>[] => {
|
||||
return flatMap(data, (item) =>
|
||||
item[flattenAttr].map((entry) => ({
|
||||
...item,
|
||||
[flattenAttr]: entry,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the data accessor for a given column.
|
||||
* @param col - The column to get the data accessor for.
|
||||
* @returns - The data accessor.
|
||||
*/
|
||||
export const getDataAccessor = (col: any) => {
|
||||
return col.group ? `${col.group}.${col.accessor}` : col.accessor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the data retrieved from the service layer to the pdf document.
|
||||
* @param {any} columns
|
||||
* @param {Record<stringm any>} data
|
||||
* @returns
|
||||
*/
|
||||
export const mapPdfRows = (columns: any, data: Record<string, any>) => {
|
||||
return data.map((item) => {
|
||||
const cells = columns.map((column) => {
|
||||
return {
|
||||
key: column.accessor,
|
||||
value: get(item, getDataAccessor(column)),
|
||||
};
|
||||
});
|
||||
return { cells, classNames: '' };
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user