mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
feat: export resource data to csv, xlsx
This commit is contained in:
29
packages/server/src/services/Accounts/AccountsExportable.ts
Normal file
29
packages/server/src/services/Accounts/AccountsExportable.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { AccountsApplication } from './AccountsApplication';
|
||||
import { Exportable } from '../Export/Exportable';
|
||||
import { IAccountsFilter, IAccountsStructureType } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export class AccountsExportable extends Exportable {
|
||||
@Inject()
|
||||
private accountsApplication: AccountsApplication;
|
||||
|
||||
/**
|
||||
* Retrieves the accounts data to exportable sheet.
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
*/
|
||||
public exportable(tenantId: number, query: IAccountsFilter) {
|
||||
const parsedQuery = {
|
||||
sortOrder: 'desc',
|
||||
columnSortBy: 'created_at',
|
||||
inactiveMode: false,
|
||||
...query,
|
||||
structure: IAccountsStructureType.Flat,
|
||||
} as IAccountsFilter;
|
||||
|
||||
return this.accountsApplication
|
||||
.getAccounts(tenantId, parsedQuery)
|
||||
.then((output) => output.accounts);
|
||||
}
|
||||
}
|
||||
17
packages/server/src/services/Export/ExportApplication.ts
Normal file
17
packages/server/src/services/Export/ExportApplication.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ExportResourceService } from './ExportService';
|
||||
|
||||
@Service()
|
||||
export class ExportApplication {
|
||||
@Inject()
|
||||
private exportResource: ExportResourceService;
|
||||
|
||||
/**
|
||||
* Exports the given resource to csv, xlsx or pdf format.
|
||||
* @param {string} reosurce
|
||||
* @param {string} format
|
||||
*/
|
||||
public export(tenantId: number, resource: string, format: string) {
|
||||
return this.exportResource.export(tenantId, resource, format);
|
||||
}
|
||||
}
|
||||
49
packages/server/src/services/Export/ExportRegistery.ts
Normal file
49
packages/server/src/services/Export/ExportRegistery.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
44
packages/server/src/services/Export/ExportResources.ts
Normal file
44
packages/server/src/services/Export/ExportResources.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import Container, { Service } from 'typedi';
|
||||
import { AccountsExportable } from '../Accounts/AccountsExportable';
|
||||
import { ExportableRegistry } from './ExportRegistery';
|
||||
import { ItemsImportable } from '../Items/ItemsImportable';
|
||||
import { ItemsExportable } from '../Items/ItemsExportable';
|
||||
|
||||
@Service()
|
||||
export class ExportableResources {
|
||||
private static registry: ExportableRegistry;
|
||||
|
||||
constructor() {
|
||||
this.boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Importable instances.
|
||||
*/
|
||||
private importables = [
|
||||
{ resource: 'Account', exportable: AccountsExportable },
|
||||
{ resource: 'Item', exportable: ItemsExportable },
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
packages/server/src/services/Export/ExportService.ts
Normal file
59
packages/server/src/services/Export/ExportService.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import xlsx from 'xlsx';
|
||||
import { sanitizeResourceName } from '../Import/_utils';
|
||||
import ResourceService from '../Resource/ResourceService';
|
||||
import { ExportableResources } from './ExportResources';
|
||||
|
||||
@Service()
|
||||
export class ExportResourceService {
|
||||
@Inject()
|
||||
private resourceService: ResourceService;
|
||||
|
||||
@Inject()
|
||||
private exportableResources: ExportableResources;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {string} resourceName
|
||||
* @param {string} format
|
||||
*/
|
||||
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 data = await exportable.exportable(tenantId, {});
|
||||
|
||||
const exportableColumns = [
|
||||
{
|
||||
label: 'Account Normal',
|
||||
accessor: 'accountNormalFormatted',
|
||||
},
|
||||
{
|
||||
label: 'Account Type',
|
||||
accessor: 'accountTypeFormatted',
|
||||
},
|
||||
];
|
||||
|
||||
const workbook = xlsx.utils.book_new();
|
||||
const worksheetData = data.map((item) =>
|
||||
exportableColumns.map((col) => item[col.accessor])
|
||||
);
|
||||
worksheetData.unshift(exportableColumns.map((col) => col.label)); // Add header row
|
||||
const worksheet = xlsx.utils.aoa_to_sheet(worksheetData);
|
||||
xlsx.utils.book_append_sheet(workbook, worksheet, 'Exported Data');
|
||||
|
||||
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' });
|
||||
}
|
||||
}
|
||||
}
|
||||
22
packages/server/src/services/Export/Exportable.ts
Normal file
22
packages/server/src/services/Export/Exportable.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export class Exportable {
|
||||
/**
|
||||
*
|
||||
* @param tenantId
|
||||
* @returns
|
||||
*/
|
||||
public async exportable(
|
||||
tenantId: number,
|
||||
query: Record<string, any>
|
||||
): Promise<Array<Record<string, any>>> {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
public transform(data: Record<string, any>) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
23
packages/server/src/services/Items/ItemsExportable.ts
Normal file
23
packages/server/src/services/Items/ItemsExportable.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Exportable } from '../Export/Exportable';
|
||||
import { IItemsFilter } from '@/interfaces';
|
||||
import { ItemsApplication } from './ItemsApplication';
|
||||
|
||||
@Service()
|
||||
export class ItemsExportable extends Exportable {
|
||||
@Inject()
|
||||
private itemsApplication: ItemsApplication;
|
||||
|
||||
/**
|
||||
* Retrieves the accounts data to exportable sheet.
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
*/
|
||||
public exportable(tenantId: number, query: IItemsFilter) {
|
||||
const parsedQuery = {} as IItemsFilter;
|
||||
|
||||
return this.itemsApplication
|
||||
.getItems(tenantId, parsedQuery)
|
||||
.then((output) => output.items);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user