mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
refactor(nestjs): import module
This commit is contained in:
126
packages/server/src/modules/Import/Import.controller.ts
Normal file
126
packages/server/src/modules/Import/Import.controller.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
Get,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
Query,
|
||||||
|
Res,
|
||||||
|
Next,
|
||||||
|
UseInterceptors,
|
||||||
|
UploadedFile,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||||
|
import { ImportResourceApplication } from './ImportResourceApplication';
|
||||||
|
import { uploadImportFileMulterOptions } from './ImportMulter.utils';
|
||||||
|
import { parseJsonSafe } from '@/utils/parse-json';
|
||||||
|
|
||||||
|
@Controller('import')
|
||||||
|
@ApiTags('import')
|
||||||
|
export class ImportController {
|
||||||
|
constructor(private readonly importResourceApp: ImportResourceApplication) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports xlsx/csv to the given resource type.
|
||||||
|
*/
|
||||||
|
@Post('/file')
|
||||||
|
@ApiOperation({ summary: 'Upload import file' })
|
||||||
|
@ApiResponse({ status: 200, description: 'File uploaded successfully' })
|
||||||
|
@UseInterceptors(
|
||||||
|
FileInterceptor('file', { storage: uploadImportFileMulterOptions }),
|
||||||
|
)
|
||||||
|
async fileUpload(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Next() next: NextFunction,
|
||||||
|
@UploadedFile() file: Express.Multer.File,
|
||||||
|
@Body('resource') resource: string,
|
||||||
|
@Body('params') rawParams?: string,
|
||||||
|
) {
|
||||||
|
const params = defaultTo(parseJsonSafe(rawParams), {});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await this.importResourceApp.import(
|
||||||
|
resource,
|
||||||
|
file.filename,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
return res.status(200).send(data);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the columns of the imported file.
|
||||||
|
*/
|
||||||
|
@Post('/:import_id/mapping')
|
||||||
|
@ApiOperation({ summary: 'Map import columns' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Mapping successful' })
|
||||||
|
async mapping(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Param('import_id') importId: string,
|
||||||
|
@Body('mapping')
|
||||||
|
mapping: Array<{ group?: string; from: string; to: string }>,
|
||||||
|
) {
|
||||||
|
const result = await this.importResourceApp.mapping(importId, mapping);
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preview the imported file before actual importing.
|
||||||
|
*/
|
||||||
|
@Get('/:import_id/preview')
|
||||||
|
@ApiOperation({ summary: 'Preview import data' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Preview data' })
|
||||||
|
async preview(@Res() res: Response, @Param('import_id') importId: string) {
|
||||||
|
const preview = await this.importResourceApp.preview(importId);
|
||||||
|
|
||||||
|
return res.status(200).send(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Importing the imported file to the application storage.
|
||||||
|
*/
|
||||||
|
@Post('/:import_id/import')
|
||||||
|
@ApiOperation({ summary: 'Process import' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Import processed successfully' })
|
||||||
|
async import(@Res() res: Response, @Param('import_id') importId: string) {
|
||||||
|
const result = await this.importResourceApp.process(importId);
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the csv/xlsx sample sheet of the given resource name.
|
||||||
|
*/
|
||||||
|
@Get('/sample')
|
||||||
|
@ApiOperation({ summary: 'Get import sample' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Sample data' })
|
||||||
|
async downloadImportSample(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('resource') resource: string,
|
||||||
|
@Query('format') format?: 'csv' | 'xlsx',
|
||||||
|
) {
|
||||||
|
const result = await this.importResourceApp.sample(resource, format);
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the import file meta.
|
||||||
|
*/
|
||||||
|
@Get('/:import_id')
|
||||||
|
@ApiOperation({ summary: 'Get import metadata' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Import metadata' })
|
||||||
|
async getImportFileMeta(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Param('import_id') importId: string,
|
||||||
|
) {
|
||||||
|
const result = await this.importResourceApp.importMeta(importId);
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,17 +12,17 @@ import { ImportFileMapping } from './ImportFileMapping';
|
|||||||
import { ImportFileDataValidator } from './ImportFileDataValidator';
|
import { ImportFileDataValidator } from './ImportFileDataValidator';
|
||||||
import { ImportFileDataTransformer } from './ImportFileDataTransformer';
|
import { ImportFileDataTransformer } from './ImportFileDataTransformer';
|
||||||
import { ImportFileCommon } from './ImportFileCommon';
|
import { ImportFileCommon } from './ImportFileCommon';
|
||||||
import { ImportableResources } from './ImportableResources';
|
|
||||||
import { ResourceModule } from '../Resource/Resource.module';
|
import { ResourceModule } from '../Resource/Resource.module';
|
||||||
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
||||||
import { AccountsModule } from '../Accounts/Accounts.module';
|
import { AccountsModule } from '../Accounts/Accounts.module';
|
||||||
|
import { ImportController } from './Import.controller';
|
||||||
|
import { ImportableRegistry } from './ImportableRegistry';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ResourceModule, TenancyModule, AccountsModule],
|
imports: [ResourceModule, TenancyModule, AccountsModule],
|
||||||
providers: [
|
providers: [
|
||||||
ImportAls,
|
ImportAls,
|
||||||
ImportSampleService,
|
ImportSampleService,
|
||||||
ImportableResources,
|
|
||||||
ImportResourceApplication,
|
ImportResourceApplication,
|
||||||
ImportDeleteExpiredFiles,
|
ImportDeleteExpiredFiles,
|
||||||
ImportFileUploadService,
|
ImportFileUploadService,
|
||||||
@@ -34,7 +34,9 @@ import { AccountsModule } from '../Accounts/Accounts.module';
|
|||||||
ImportFileDataValidator,
|
ImportFileDataValidator,
|
||||||
ImportFileDataTransformer,
|
ImportFileDataTransformer,
|
||||||
ImportFileCommon,
|
ImportFileCommon,
|
||||||
|
ImportableRegistry
|
||||||
],
|
],
|
||||||
|
controllers: [ImportController],
|
||||||
exports: [ImportAls],
|
exports: [ImportAls],
|
||||||
})
|
})
|
||||||
export class ImportModule {}
|
export class ImportModule {}
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ import {
|
|||||||
ImportableContext,
|
ImportableContext,
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { getUniqueImportableValue, trimObject } from './_utils';
|
import { getUniqueImportableValue, trimObject } from './_utils';
|
||||||
import { ImportableResources } from './ImportableResources';
|
|
||||||
import { ResourceService } from '../Resource/ResourceService';
|
import { ResourceService } from '../Resource/ResourceService';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ServiceError } from '../Items/ServiceError';
|
import { ServiceError } from '../Items/ServiceError';
|
||||||
import { ImportModelShape } from './models/Import';
|
import { ImportModelShape } from './models/Import';
|
||||||
|
import { ImportableRegistry } from './ImportableRegistry';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImportFileCommon {
|
export class ImportFileCommon {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly importFileValidator: ImportFileDataValidator,
|
private readonly importFileValidator: ImportFileDataValidator,
|
||||||
private readonly importable: ImportableResources,
|
|
||||||
private readonly resource: ResourceService,
|
private readonly resource: ResourceService,
|
||||||
|
private readonly importableRegistry: ImportableRegistry,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,9 +39,9 @@ export class ImportFileCommon {
|
|||||||
const resourceFields = this.resource.getResourceFields2(
|
const resourceFields = this.resource.getResourceFields2(
|
||||||
importFile.resource,
|
importFile.resource,
|
||||||
);
|
);
|
||||||
const ImportableRegistry = this.importable.registry;
|
const importable = await this.importableRegistry.getImportable(
|
||||||
const importable = ImportableRegistry.getImportable(importFile.resource);
|
importFile.resource,
|
||||||
|
);
|
||||||
const concurrency = importable.concurrency || 10;
|
const concurrency = importable.concurrency || 10;
|
||||||
|
|
||||||
const success: ImportOperSuccess[] = [];
|
const success: ImportOperSuccess[] = [];
|
||||||
@@ -67,10 +67,7 @@ export class ImportFileCommon {
|
|||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
// Run the importable function and listen to the errors.
|
// Run the importable function and listen to the errors.
|
||||||
const data = await importable.importable(
|
const data = await importable.importable(transformedDTO, trx);
|
||||||
transformedDTO,
|
|
||||||
trx,
|
|
||||||
);
|
|
||||||
success.push({ index, data });
|
success.push({ index, data });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ServiceError) {
|
if (err instanceof ServiceError) {
|
||||||
@@ -112,9 +109,8 @@ export class ImportFileCommon {
|
|||||||
resourceName: string,
|
resourceName: string,
|
||||||
params: Record<string, any>,
|
params: Record<string, any>,
|
||||||
) {
|
) {
|
||||||
const ImportableRegistry = this.importable.registry;
|
const importable =
|
||||||
const importable = ImportableRegistry.getImportable(resourceName);
|
await this.importableRegistry.getImportable(resourceName);
|
||||||
|
|
||||||
const yupSchema = importable.paramsValidationSchema();
|
const yupSchema = importable.paramsValidationSchema();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -137,8 +133,8 @@ export class ImportFileCommon {
|
|||||||
resourceName: string,
|
resourceName: string,
|
||||||
params: Record<string, any>,
|
params: Record<string, any>,
|
||||||
) {
|
) {
|
||||||
const ImportableRegistry = this.importable.registry;
|
const importable =
|
||||||
const importable = ImportableRegistry.getImportable(resourceName);
|
await this.importableRegistry.getImportable(resourceName);
|
||||||
|
|
||||||
await importable.validateParams(params);
|
await importable.validateParams(params);
|
||||||
}
|
}
|
||||||
@@ -149,9 +145,12 @@ export class ImportFileCommon {
|
|||||||
* @param {Record<string, any>} params
|
* @param {Record<string, any>} params
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public transformParams(resourceName: string, params: Record<string, any>) {
|
public async transformParams(
|
||||||
const ImportableRegistry = this.importable.registry;
|
resourceName: string,
|
||||||
const importable = ImportableRegistry.getImportable(resourceName);
|
params: Record<string, any>,
|
||||||
|
) {
|
||||||
|
const importable =
|
||||||
|
await this.importableRegistry.getImportable(resourceName);
|
||||||
|
|
||||||
return importable.transformParams(params);
|
return importable.transformParams(params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import bluebird from 'bluebird';
|
import * as bluebird from 'bluebird';
|
||||||
import { isUndefined, pickBy, set } from 'lodash';
|
import { isUndefined, pickBy, set } from 'lodash';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ImportMappingAttr, ResourceMetaFieldsMap } from './interfaces';
|
import { ImportMappingAttr, ResourceMetaFieldsMap } from './interfaces';
|
||||||
import {
|
import {
|
||||||
valueParser,
|
valueParser,
|
||||||
@@ -12,7 +13,6 @@ import {
|
|||||||
} from './_utils';
|
} from './_utils';
|
||||||
import { ResourceService } from '../Resource/ResourceService';
|
import { ResourceService } from '../Resource/ResourceService';
|
||||||
import { CurrencyParsingDTOs } from './_constants';
|
import { CurrencyParsingDTOs } from './_constants';
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImportFileDataTransformer {
|
export class ImportFileDataTransformer {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ImportInsertError, ResourceMetaFieldsMap } from './interfaces';
|
import { ImportInsertError, ResourceMetaFieldsMap } from './interfaces';
|
||||||
import { ERRORS, convertFieldsToYupValidation } from './_utils';
|
import { ERRORS, convertFieldsToYupValidation } from './_utils';
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { IModelMeta } from '@/interfaces/Model';
|
import { IModelMeta } from '@/interfaces/Model';
|
||||||
import { ServiceError } from '../Items/ServiceError';
|
import { ServiceError } from '../Items/ServiceError';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { ImportModel } from './models/Import';
|
import { ImportModel } from './models/Import';
|
||||||
import { ImportFileMetaTransformer } from './ImportFileMetaTransformer';
|
import { ImportFileMetaTransformer } from './ImportFileMetaTransformer';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
|
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
|
||||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
|
|||||||
39
packages/server/src/modules/Import/ImportMulter.utils.ts
Normal file
39
packages/server/src/modules/Import/ImportMulter.utils.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import * as Multer from 'multer';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { ServiceError } from '../Items/ServiceError';
|
||||||
|
|
||||||
|
export const getImportsStoragePath = () => {
|
||||||
|
return path.join(global.__static_dirname, `/imports`);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function allowSheetExtensions(req, file, cb) {
|
||||||
|
if (
|
||||||
|
file.mimetype !== 'text/csv' &&
|
||||||
|
file.mimetype !== 'application/vnd.ms-excel' &&
|
||||||
|
file.mimetype !==
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
) {
|
||||||
|
cb(new ServiceError('IMPORTED_FILE_EXTENSION_INVALID'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const storage = Multer.diskStorage({
|
||||||
|
destination: function (req, file, cb) {
|
||||||
|
const path = getImportsStoragePath();
|
||||||
|
cb(null, path);
|
||||||
|
},
|
||||||
|
filename: function (req, file, cb) {
|
||||||
|
// Add the creation timestamp to clean up temp files later.
|
||||||
|
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
|
||||||
|
cb(null, uniqueSuffix);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const uploadImportFileMulterOptions = {
|
||||||
|
storage,
|
||||||
|
limits: { fileSize: 5 * 1024 * 1024 },
|
||||||
|
fileFilter: allowSheetExtensions,
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import bluebird from 'bluebird';
|
import * as bluebird from 'bluebird';
|
||||||
import { deleteImportFile } from './_utils';
|
import { deleteImportFile } from './_utils';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { ImportModel } from './models/Import';
|
import { ImportModel } from './models/Import';
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import { ImportableResources } from './ImportableResources';
|
|
||||||
import { sanitizeResourceName } from './_utils';
|
import { sanitizeResourceName } from './_utils';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { getImportableService } from './decorators/Import.decorator';
|
||||||
|
import { ImportableRegistry } from './ImportableRegistry';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImportSampleService {
|
export class ImportSampleService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly importable: ImportableResources,
|
private readonly importableRegistry: ImportableRegistry,
|
||||||
) {}
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Retrieves the sample sheet of the given resource.
|
* Retrieves the sample sheet of the given resource.
|
||||||
* @param {string} resource
|
* @param {string} resource
|
||||||
* @param {string} format
|
* @param {string} format
|
||||||
* @returns {Buffer | string}
|
* @returns {Buffer | string}
|
||||||
*/
|
*/
|
||||||
public sample(
|
public async sample(
|
||||||
resource: string,
|
resource: string,
|
||||||
format: 'csv' | 'xlsx'
|
format: 'csv' | 'xlsx'
|
||||||
): Buffer | string {
|
): Promise<Buffer | string> {
|
||||||
const _resource = sanitizeResourceName(resource);
|
const _resource = sanitizeResourceName(resource);
|
||||||
|
const importable = await this.importableRegistry.getImportable(_resource);
|
||||||
const ImportableRegistry = this.importable.registry;
|
|
||||||
const importable = ImportableRegistry.getImportable(_resource);
|
|
||||||
|
|
||||||
const data = importable.sampleData();
|
const data = importable.sampleData();
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ export class ImportSampleService {
|
|||||||
const worksheet = XLSX.utils.json_to_sheet(data);
|
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||||||
|
|
||||||
|
|
||||||
// Determine the output format
|
// Determine the output format
|
||||||
if (format === 'csv') {
|
if (format === 'csv') {
|
||||||
const csvOutput = XLSX.utils.sheet_to_csv(worksheet);
|
const csvOutput = XLSX.utils.sheet_to_csv(worksheet);
|
||||||
|
|||||||
@@ -1,43 +1,26 @@
|
|||||||
import { camelCase, upperFirst } from 'lodash';
|
import { camelCase, upperFirst } from 'lodash';
|
||||||
import { Importable } from './Importable';
|
import { Importable } from './Importable';
|
||||||
|
import { getImportableService } from './decorators/Import.decorator';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ContextIdFactory, ModuleRef } from '@nestjs/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class ImportableRegistry {
|
export class ImportableRegistry {
|
||||||
private static instance: ImportableRegistry;
|
constructor(private readonly moduleRef: ModuleRef) {}
|
||||||
private importables: Record<string, Importable>;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.importables = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets singleton instance of registry.
|
|
||||||
* @returns {ImportableRegistry}
|
|
||||||
*/
|
|
||||||
public static getInstance(): ImportableRegistry {
|
|
||||||
if (!ImportableRegistry.instance) {
|
|
||||||
ImportableRegistry.instance = new ImportableRegistry();
|
|
||||||
}
|
|
||||||
return ImportableRegistry.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the given importable service.
|
|
||||||
* @param {string} resource
|
|
||||||
* @param {Importable} importable
|
|
||||||
*/
|
|
||||||
public registerImportable(resource: string, importable: Importable): void {
|
|
||||||
const _resource = this.sanitizeResourceName(resource);
|
|
||||||
this.importables[_resource] = importable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the importable service instance of the given resource name.
|
* Retrieves the importable service instance of the given resource name.
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {Importable}
|
* @returns {Importable}
|
||||||
*/
|
*/
|
||||||
public getImportable(name: string): Importable {
|
public async getImportable(name: string) {
|
||||||
const _name = this.sanitizeResourceName(name);
|
const _name = this.sanitizeResourceName(name);
|
||||||
return this.importables[_name];
|
const importable = getImportableService(_name);
|
||||||
|
const contextId = ContextIdFactory.create();
|
||||||
|
|
||||||
|
const importableInstance = await this.moduleRef.resolve(importable, contextId, {
|
||||||
|
strict: false,
|
||||||
|
});
|
||||||
|
return importableInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sanitizeResourceName(resource: string) {
|
private sanitizeResourceName(resource: string) {
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
// import { AccountsImportable } from '../Accounts/AccountsImportable';
|
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { ImportableRegistry } from './ImportableRegistry';
|
|
||||||
// import { UncategorizedTransactionsImportable } from '../BankingCategorize/commands/UncategorizedTransactionsImportable';
|
|
||||||
// import { CustomersImportable } from '../Contacts/Customers/CustomersImportable';
|
|
||||||
// import { VendorsImportable } from '../Contacts/Vendors/VendorsImportable';
|
|
||||||
// import { ItemsImportable } from '../Items/ItemsImportable';
|
|
||||||
// import { ItemCategoriesImportable } from '../ItemCategories/ItemCategoriesImportable';
|
|
||||||
// import { ManualJournalImportable } from '../ManualJournals/commands/ManualJournalsImport';
|
|
||||||
// import { BillsImportable } from '../Purchases/Bills/BillsImportable';
|
|
||||||
// import { ExpensesImportable } from '../Expenses/ExpensesImportable';
|
|
||||||
// import { SaleInvoicesImportable } from '../Sales/Invoices/SaleInvoicesImportable';
|
|
||||||
// import { SaleEstimatesImportable } from '../Sales/Estimates/SaleEstimatesImportable';
|
|
||||||
// import { BillPaymentsImportable } from '../Purchases/BillPayments/BillPaymentsImportable';
|
|
||||||
// import { VendorCreditsImportable } from '../Purchases/VendorCredits/VendorCreditsImportable';
|
|
||||||
// import { PaymentsReceivedImportable } from '../Sales/PaymentReceived/PaymentsReceivedImportable';
|
|
||||||
// import { CreditNotesImportable } from '../CreditNotes/commands/CreditNotesImportable';
|
|
||||||
// import { SaleReceiptsImportable } from '../Sales/Receipts/SaleReceiptsImportable';
|
|
||||||
// import { TaxRatesImportable } from '../TaxRates/TaxRatesImportable';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ImportableResources {
|
|
||||||
private static registry: ImportableRegistry;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.boot();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Importable instances.
|
|
||||||
*/
|
|
||||||
private importables = [
|
|
||||||
// { resource: 'Account', importable: AccountsImportable },
|
|
||||||
// {
|
|
||||||
// resource: 'UncategorizedCashflowTransaction',
|
|
||||||
// importable: UncategorizedTransactionsImportable,
|
|
||||||
// },
|
|
||||||
// { resource: 'Customer', importable: CustomersImportable },
|
|
||||||
// { resource: 'Vendor', importable: VendorsImportable },
|
|
||||||
// { resource: 'Item', importable: ItemsImportable },
|
|
||||||
// { resource: 'ItemCategory', importable: ItemCategoriesImportable },
|
|
||||||
// { resource: 'ManualJournal', importable: ManualJournalImportable },
|
|
||||||
// { resource: 'Bill', importable: BillsImportable },
|
|
||||||
// { resource: 'Expense', importable: ExpensesImportable },
|
|
||||||
// { resource: 'SaleInvoice', importable: SaleInvoicesImportable },
|
|
||||||
// { resource: 'SaleEstimate', importable: SaleEstimatesImportable },
|
|
||||||
// { resource: 'BillPayment', importable: BillPaymentsImportable },
|
|
||||||
// { resource: 'PaymentReceive', importable: PaymentsReceivedImportable },
|
|
||||||
// { resource: 'VendorCredit', importable: VendorCreditsImportable },
|
|
||||||
// { resource: 'CreditNote', importable: CreditNotesImportable },
|
|
||||||
// { resource: 'SaleReceipt', importable: SaleReceiptsImportable },
|
|
||||||
// { resource: 'TaxRate', importable: TaxRatesImportable },
|
|
||||||
];
|
|
||||||
|
|
||||||
public get registry() {
|
|
||||||
return ImportableResources.registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boots all the registered importables.
|
|
||||||
*/
|
|
||||||
public boot() {
|
|
||||||
if (!ImportableResources.registry) {
|
|
||||||
const instance = ImportableRegistry.getInstance();
|
|
||||||
|
|
||||||
this.importables.forEach((importable) => {
|
|
||||||
// const importableInstance = Container.get(importable.importable);
|
|
||||||
// instance.registerImportable(importable.resource, importableInstance);
|
|
||||||
});
|
|
||||||
ImportableResources.registry = instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Global } from "@nestjs/common";
|
||||||
|
|
||||||
|
const importableModels = new Map<string, boolean>();
|
||||||
|
const importableService = new Map<string, any>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorator that marks a model as exportable and registers its metadata.
|
||||||
|
* @param metadata Model metadata configuration for export
|
||||||
|
*/
|
||||||
|
export function ImportableModel() {
|
||||||
|
return function (target: any) {
|
||||||
|
const modelName = target.name;
|
||||||
|
importableModels.set(modelName, true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImportableService({ name }: { name: string }) {
|
||||||
|
return function (target: any) {
|
||||||
|
importableService.set(name, target);
|
||||||
|
|
||||||
|
// Apply the @Global() decorator to make the service globally available
|
||||||
|
Global()(target);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the registered exportable model metadata
|
||||||
|
* @param modelName Name of the model class
|
||||||
|
*/
|
||||||
|
export function getImportableModelMeta(modelName: string): boolean | undefined {
|
||||||
|
return importableModels.get(modelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getImportableService(modelName: string) {
|
||||||
|
return importableService.get(modelName);
|
||||||
|
}
|
||||||
@@ -4,8 +4,11 @@ import { ItemCategoriesSampleData } from './constants';
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { CreateItemCategoryDto } from './dtos/ItemCategory.dto';
|
import { CreateItemCategoryDto } from './dtos/ItemCategory.dto';
|
||||||
import { ItemCategoryApplication } from './ItemCategory.application';
|
import { ItemCategoryApplication } from './ItemCategory.application';
|
||||||
|
import { ImportableService } from '../Import/decorators/Import.decorator';
|
||||||
|
import { ItemCategory } from './models/ItemCategory.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ImportableService({ name: ItemCategory.name })
|
||||||
export class ItemCategoriesImportable extends Importable {
|
export class ItemCategoriesImportable extends Importable {
|
||||||
constructor(private readonly itemCategoriesApp: ItemCategoryApplication) {
|
constructor(private readonly itemCategoriesApp: ItemCategoryApplication) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.dec
|
|||||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
||||||
import { ItemCategoryMeta } from './ItemCategory.meta';
|
import { ItemCategoryMeta } from './ItemCategory.meta';
|
||||||
|
import { ImportableModel } from '@/modules/Import/decorators/Import.decorator';
|
||||||
|
|
||||||
@ExportableModel()
|
@ExportableModel()
|
||||||
|
@ImportableModel()
|
||||||
@InjectModelMeta(ItemCategoryMeta)
|
@InjectModelMeta(ItemCategoryMeta)
|
||||||
export class ItemCategory extends TenantBaseModel {
|
export class ItemCategory extends TenantBaseModel {
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { GetItemsService } from './GetItems.service';
|
|||||||
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
||||||
import { InventoryAdjustmentsModule } from '../InventoryAdjutments/InventoryAdjustments.module';
|
import { InventoryAdjustmentsModule } from '../InventoryAdjutments/InventoryAdjustments.module';
|
||||||
import { ItemsExportable } from './ItemsExportable.service';
|
import { ItemsExportable } from './ItemsExportable.service';
|
||||||
|
import { ItemsImportable } from './ItemsImportable.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -40,7 +41,8 @@ import { ItemsExportable } from './ItemsExportable.service';
|
|||||||
TransformerInjectable,
|
TransformerInjectable,
|
||||||
ItemsEntriesService,
|
ItemsEntriesService,
|
||||||
ItemsExportable,
|
ItemsExportable,
|
||||||
|
ItemsImportable
|
||||||
],
|
],
|
||||||
exports: [ItemsEntriesService, ItemsExportable],
|
exports: [ItemsEntriesService, ItemsExportable, ItemsImportable],
|
||||||
})
|
})
|
||||||
export class ItemsModule {}
|
export class ItemsModule {}
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import { Importable } from '../Import/Importable';
|
|||||||
import { CreateItemService } from './CreateItem.service';
|
import { CreateItemService } from './CreateItem.service';
|
||||||
import { CreateItemDto } from './dtos/Item.dto';
|
import { CreateItemDto } from './dtos/Item.dto';
|
||||||
import { ItemsSampleData } from './Items.constants';
|
import { ItemsSampleData } from './Items.constants';
|
||||||
|
import { ImportableService } from '../Import/decorators/Import.decorator';
|
||||||
|
import { Item } from './models/Item';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ImportableService({ name: Item.name })
|
||||||
export class ItemsImportable extends Importable {
|
export class ItemsImportable extends Importable {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly createItemService: CreateItemService,
|
private readonly createItemService: CreateItemService,
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import { Model } from 'objection';
|
|||||||
import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.decorator';
|
import { ExportableModel } from '@/modules/Export/decorators/ExportableModel.decorator';
|
||||||
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
import { InjectModelMeta } from '@/modules/Tenancy/TenancyModels/decorators/InjectModelMeta.decorator';
|
||||||
import { ItemMeta } from './Item.meta';
|
import { ItemMeta } from './Item.meta';
|
||||||
|
import { ImportableModel } from '@/modules/Import/decorators/Import.decorator';
|
||||||
|
|
||||||
@ExportableModel()
|
@ExportableModel()
|
||||||
|
@ImportableModel()
|
||||||
@InjectModelMeta(ItemMeta)
|
@InjectModelMeta(ItemMeta)
|
||||||
export class Item extends TenantBaseModel {
|
export class Item extends TenantBaseModel {
|
||||||
public readonly quantityOnHand: number;
|
public readonly quantityOnHand: number;
|
||||||
|
|||||||
7
packages/server/src/utils/parse-json.ts
Normal file
7
packages/server/src/utils/parse-json.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export const parseJsonSafe = (value: string) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user