feat: wip configure resources to be exportable

This commit is contained in:
Ahmed Bouhuolia
2024-05-01 12:45:24 +02:00
parent 7e89966f20
commit fab71d2b65
25 changed files with 295 additions and 39 deletions

View File

@@ -14,6 +14,7 @@ import { BillPaymentExportable } from '../Purchases/BillPayments/BillPaymentExpo
import { ManualJournalsExportable } from '../ManualJournals/ManualJournalExportable';
import { CreditNotesExportable } from '../CreditNotes/CreditNotesExportable';
import { VendorCreditsExportable } from '../Purchases/VendorCredits/VendorCreditsExportable';
import { ItemCategoriesExportable } from '../ItemCategories/ItemCategoriesExportable';
@Service()
export class ExportableResources {
@@ -32,6 +33,7 @@ export class ExportableResources {
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 },
@@ -43,7 +45,7 @@ export class ExportableResources {
{ resource: 'BillPayment', exportable: BillPaymentExportable },
{ resource: 'ManualJournal', exportable: ManualJournalsExportable },
{ resource: 'CreditNote', exportable: CreditNotesExportable },
{ resource: 'VendorCredit', exportable: VendorCreditsExportable }
{ resource: 'VendorCredit', exportable: VendorCreditsExportable },
];
/**

View File

@@ -5,6 +5,7 @@ import ResourceService from '../Resource/ResourceService';
import { ExportableResources } from './ExportResources';
import { ServiceError } from '@/exceptions';
import { Errors } from './common';
import { IModelMeta } from '@/interfaces';
@Service()
export class ExportResourceService {
@@ -22,40 +23,94 @@ export class ExportResourceService {
*/
async export(tenantId: number, resourceName: string, format: string = 'csv') {
const resource = sanitizeResourceName(resourceName);
const resourceMeta = this.resourceService.getResourceMeta(
tenantId,
resource
);
const exportable =
this.exportableResources.registry.getExportable(resource);
const resourceMeta = this.getResourceMeta(tenantId, resource);
if (!resourceMeta.exportable) {
this.validateResourceMeta(resourceMeta);
const data = await this.getExportableData(tenantId, resource);
const exportableColumns = this.getExportableColumns(resourceMeta);
const workbook = this.createWorkbook(data, exportableColumns);
return this.exportWorkbook(workbook, format);
}
/**
* 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(tenantId: number, resource: string) {
return this.resourceService.getResourceMeta(tenantId, 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);
}
const data = await exportable.exportable(tenantId, {});
}
const exportableColumns = Object.entries(resourceMeta.columns)
.filter(([_, value]) => value.exportable)
/**
* 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(tenantId: number, resource: string) {
const exportable =
this.exportableResources.registry.getExportable(resource);
return exportable.exportable(tenantId, {});
}
/**
* 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(resourceMeta: IModelMeta) {
return Object.entries(resourceMeta.columns)
.filter(([_, value]) => value.exportable !== false)
.map(([key, value]) => ({
name: value.name,
type: value.type,
accessor: value.accessor || key,
}));
}
/**
* 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) => item[col.accessor])
);
worksheetData.unshift(exportableColumns.map((col) => col.name)); // Add header row
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') {
// Convert to CSV using the xlsx package
return xlsx.write(workbook, { type: 'buffer', bookType: 'csv' });
} else if (format.toLowerCase() === 'xlsx') {
// Write to XLSX format
return xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' });
}
}