mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 04:10:32 +00:00
feat: import resource
This commit is contained in:
@@ -1,20 +1,36 @@
|
||||
import fs from 'fs/promises';
|
||||
import XLSX from 'xlsx';
|
||||
import bluebird from 'bluebird';
|
||||
import * as R from 'ramda';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { first } from 'lodash';
|
||||
import { ImportFileDataValidator } from './ImportFileDataValidator';
|
||||
import { Knex } from 'knex';
|
||||
import { ImportInsertError } from './interfaces';
|
||||
import {
|
||||
ImportInsertError,
|
||||
ImportOperError,
|
||||
ImportOperSuccess,
|
||||
} from './interfaces';
|
||||
import { AccountsImportable } from './AccountsImportable';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { trimObject } from './_utils';
|
||||
import { ImportableResources } from './ImportableResources';
|
||||
import ResourceService from '../Resource/ResourceService';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class ImportFileCommon {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private importFileValidator: ImportFileDataValidator;
|
||||
|
||||
@Inject()
|
||||
private importable: AccountsImportable;
|
||||
private importable: ImportableResources;
|
||||
|
||||
@Inject()
|
||||
private resource: ResourceService;
|
||||
|
||||
/**
|
||||
* Maps the columns of the imported data based on the provided mapping attributes.
|
||||
@@ -22,7 +38,7 @@ export class ImportFileCommon {
|
||||
* @param {ImportMappingAttr[]} map - The mapping attributes.
|
||||
* @returns {Record<string, any>[]} - The mapped data objects.
|
||||
*/
|
||||
public parseXlsxSheet(buffer) {
|
||||
public parseXlsxSheet(buffer: Buffer): Record<string, unknown>[] {
|
||||
const workbook = XLSX.read(buffer, { type: 'buffer' });
|
||||
|
||||
const firstSheetName = workbook.SheetNames[0];
|
||||
@@ -43,45 +59,81 @@ export class ImportFileCommon {
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId -
|
||||
* @param {Record<string, any>} importableFields
|
||||
* @param {Record<string, any>} parsedData
|
||||
* @param {string} resourceName - Resource name.
|
||||
* @param {Record<string, any>} parsedData -
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns
|
||||
*/
|
||||
public import(
|
||||
public async import(
|
||||
tenantId: number,
|
||||
importableFields,
|
||||
parsedData: Record<string, any>,
|
||||
resourceName: string,
|
||||
parsedData: Record<string, any>[],
|
||||
trx?: Knex.Transaction
|
||||
): Promise<(void | ImportInsertError[])[]> {
|
||||
return bluebird.map(
|
||||
parsedData,
|
||||
async (objectDTO, index: number): Promise<true | ImportInsertError[]> => {
|
||||
try {
|
||||
// Validate the DTO object before passing it to the service layer.
|
||||
await this.importFileValidator.validateData(
|
||||
importableFields,
|
||||
objectDTO
|
||||
);
|
||||
try {
|
||||
// Run the importable function and listen to the errors.
|
||||
await this.importable.importable(tenantId, objectDTO, trx);
|
||||
} catch (error) {
|
||||
if (error instanceof ServiceError) {
|
||||
return [
|
||||
{
|
||||
errorCode: 'ValidationError',
|
||||
errorMessage: error.message || error.errorType,
|
||||
rowNumber: index + 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
} catch (errors) {
|
||||
return errors.map((er) => ({ ...er, rowNumber: index + 1 }));
|
||||
}
|
||||
},
|
||||
{ concurrency: 2 }
|
||||
): Promise<[ImportOperSuccess[], ImportOperError[]]> {
|
||||
const importableFields = this.resource.getResourceImportableFields(
|
||||
tenantId,
|
||||
resourceName
|
||||
);
|
||||
const ImportableRegistry = this.importable.registry;
|
||||
const importable = ImportableRegistry.getImportable(resourceName);
|
||||
|
||||
const success: ImportOperSuccess[] = [];
|
||||
const failed: ImportOperError[] = [];
|
||||
|
||||
const importAsync = async (objectDTO, index: number): Promise<void> => {
|
||||
try {
|
||||
// Validate the DTO object before passing it to the service layer.
|
||||
await this.importFileValidator.validateData(
|
||||
importableFields,
|
||||
objectDTO
|
||||
);
|
||||
try {
|
||||
// Run the importable function and listen to the errors.
|
||||
const data = await importable.importable(tenantId, objectDTO, trx);
|
||||
success.push({ index, data });
|
||||
} catch (err) {
|
||||
if (err instanceof ServiceError) {
|
||||
const error = [
|
||||
{
|
||||
errorCode: 'ValidationError',
|
||||
errorMessage: err.message || err.errorType,
|
||||
rowNumber: index + 1,
|
||||
},
|
||||
];
|
||||
failed.push({ index, error });
|
||||
}
|
||||
}
|
||||
} catch (errors) {
|
||||
const error = errors.map((er) => ({ ...er, rowNumber: index + 1 }));
|
||||
failed.push({ index, error });
|
||||
}
|
||||
};
|
||||
await bluebird.map(parsedData, importAsync, { concurrency: 2 });
|
||||
|
||||
return [success, failed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the sheet columns from the given sheet data.
|
||||
* @param {unknown[]} json
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public parseSheetColumns(json: unknown[]): string[] {
|
||||
return R.compose(Object.keys, trimObject, first)(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the imported file from the storage and database.
|
||||
* @param {number} tenantId
|
||||
* @param {} importFile
|
||||
*/
|
||||
private async deleteImportFile(tenantId: number, importFile: any) {
|
||||
const { Import } = this.tenancy.models(tenantId);
|
||||
|
||||
// Deletes the import row.
|
||||
await Import.query().findById(importFile.id).delete();
|
||||
|
||||
// Deletes the imported file.
|
||||
await fs.unlink(`public/imports/${importFile.filename}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user