diff --git a/packages/server/package.json b/packages/server/package.json index 9eb37819a..9d09782e2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,7 +28,6 @@ "@casl/ability": "^5.4.3", "@lemonsqueezy/lemonsqueezy.js": "^2.2.0", "@liaoliaots/nestjs-redis": "^10.0.0", - "@types/multer": "^1.4.11", "@nestjs/bull": "^10.2.1", "@nestjs/bullmq": "^10.2.2", "@nestjs/cache-manager": "^2.2.2", @@ -39,9 +38,11 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^10.0.0", + "@nestjs/schedule": "^4.1.2", "@nestjs/swagger": "^7.4.2", "@nestjs/throttler": "^6.2.1", "@supercharge/promise-pool": "^3.2.0", + "@types/multer": "^1.4.11", "@types/nodemailer": "^6.4.17", "@types/passport-local": "^1.0.38", "@types/ramda": "^0.30.2", @@ -74,10 +75,10 @@ "moment": "^2.30.1", "moment-range": "^4.0.2", "moment-timezone": "^0.5.43", - "mysql": "^2.18.1", - "mysql2": "^3.11.3", "multer": "1.4.5-lts.1", "multer-s3": "^3.0.1", + "mysql": "^2.18.1", + "mysql2": "^3.11.3", "nestjs-cls": "^5.2.0", "nestjs-i18n": "^10.4.9", "nestjs-redis": "^1.3.3", diff --git a/packages/server/src/modules/App/App.module.ts b/packages/server/src/modules/App/App.module.ts index 28791abb3..5ffc2ed98 100644 --- a/packages/server/src/modules/App/App.module.ts +++ b/packages/server/src/modules/App/App.module.ts @@ -12,6 +12,7 @@ import { QueryResolver, } from 'nestjs-i18n'; import { BullModule } from '@nestjs/bullmq'; +import { ScheduleModule } from '@nestjs/schedule'; import { PassportModule } from '@nestjs/passport'; import { ClsModule, ClsService } from 'nestjs-cls'; import { AppController } from './App.controller'; @@ -139,6 +140,7 @@ import { ImportModule } from '../Import/Import.module'; }), inject: [ConfigService], }), + ScheduleModule.forRoot(), TenancyDatabaseModule, TenancyModelsModule, TenancyModule, diff --git a/packages/server/src/modules/Import/Import.controller.ts b/packages/server/src/modules/Import/Import.controller.ts index d1d4a74c7..dcc1e29e4 100644 --- a/packages/server/src/modules/Import/Import.controller.ts +++ b/packages/server/src/modules/Import/Import.controller.ts @@ -30,7 +30,7 @@ export class ImportController { @ApiOperation({ summary: 'Upload import file' }) @ApiResponse({ status: 200, description: 'File uploaded successfully' }) @UseInterceptors( - FileInterceptor('file', { storage: uploadImportFileMulterOptions }), + FileInterceptor('file', uploadImportFileMulterOptions), ) async fileUpload( @Res() res: Response, diff --git a/packages/server/src/modules/Import/Import.module.ts b/packages/server/src/modules/Import/Import.module.ts index 4b37a9974..bc01e4c7c 100644 --- a/packages/server/src/modules/Import/Import.module.ts +++ b/packages/server/src/modules/Import/Import.module.ts @@ -17,10 +17,16 @@ import { TenancyModule } from '../Tenancy/Tenancy.module'; import { AccountsModule } from '../Accounts/Accounts.module'; import { ImportController } from './Import.controller'; import { ImportableRegistry } from './ImportableRegistry'; +import { InjectSystemModel } from '../System/SystemModels/SystemModels.module'; +import { ImportModel } from './models/Import'; +import { ImportDeleteExpiredFilesJobs } from './jobs/ImportDeleteExpiredFilesJob'; + +const models = [InjectSystemModel(ImportModel)]; @Module({ imports: [ResourceModule, TenancyModule, AccountsModule], providers: [ + ...models, ImportAls, ImportSampleService, ImportResourceApplication, @@ -34,9 +40,10 @@ import { ImportableRegistry } from './ImportableRegistry'; ImportFileDataValidator, ImportFileDataTransformer, ImportFileCommon, - ImportableRegistry + ImportableRegistry, + ImportDeleteExpiredFilesJobs ], controllers: [ImportController], - exports: [ImportAls], + exports: [ImportAls, ...models], }) export class ImportModule {} diff --git a/packages/server/src/modules/Import/ImportFileCommon.ts b/packages/server/src/modules/Import/ImportFileCommon.ts index f1c968302..345bfbb85 100644 --- a/packages/server/src/modules/Import/ImportFileCommon.ts +++ b/packages/server/src/modules/Import/ImportFileCommon.ts @@ -1,4 +1,4 @@ -import bluebird from 'bluebird'; +import * as bluebird from 'bluebird'; import * as R from 'ramda'; import { first } from 'lodash'; import { ImportFileDataValidator } from './ImportFileDataValidator'; diff --git a/packages/server/src/modules/Import/ImportFileMapping.ts b/packages/server/src/modules/Import/ImportFileMapping.ts index da97bdcdb..01a822d85 100644 --- a/packages/server/src/modules/Import/ImportFileMapping.ts +++ b/packages/server/src/modules/Import/ImportFileMapping.ts @@ -16,7 +16,7 @@ export class ImportFileMapping { private readonly resource: ResourceService, @Inject(ImportModel.name) - private readonly importModel: () => typeof ImportModel, + private readonly importModel: typeof ImportModel, ) {} /** @@ -28,7 +28,7 @@ export class ImportFileMapping { importId: string, maps: ImportMappingAttr[], ): Promise { - const importFile = await this.importModel() + const importFile = await this.importModel .query() .findOne('filename', importId) .throwIfNotFound(); @@ -46,7 +46,7 @@ export class ImportFileMapping { const mappingStringified = JSON.stringify(maps); - await this.importModel().query().findById(importFile.id).patch({ + await this.importModel.query().findById(importFile.id).patch({ mapping: mappingStringified, }); return { diff --git a/packages/server/src/modules/Import/ImportFileProcess.ts b/packages/server/src/modules/Import/ImportFileProcess.ts index 736247405..b9cdf049d 100644 --- a/packages/server/src/modules/Import/ImportFileProcess.ts +++ b/packages/server/src/modules/Import/ImportFileProcess.ts @@ -27,7 +27,6 @@ export class ImportFileProcess { /** * Preview the imported file results before commiting the transactions. - * @param {number} tenantId * @param {number} importId * @returns {Promise} */ diff --git a/packages/server/src/modules/Import/ImportFileUpload.ts b/packages/server/src/modules/Import/ImportFileUpload.ts index 5d4926f20..2ef5b6afb 100644 --- a/packages/server/src/modules/Import/ImportFileUpload.ts +++ b/packages/server/src/modules/Import/ImportFileUpload.ts @@ -1,3 +1,4 @@ +import { Inject, Injectable } from '@nestjs/common'; import { deleteImportFile, getResourceColumns, @@ -10,7 +11,6 @@ import { ImportFileCommon } from './ImportFileCommon'; import { ImportFileDataValidator } from './ImportFileDataValidator'; import { ImportFileUploadPOJO } from './interfaces'; import { parseSheetData } from './sheet_utils'; -import { Inject, Injectable } from '@nestjs/common'; import { ImportModel } from './models/Import'; import { TenancyContext } from '../Tenancy/TenancyContext.service'; @@ -29,9 +29,8 @@ export class ImportFileUploadService { /** * Imports the specified file for the given resource. * Deletes the file if an error occurs during the import process. - * @param {number} tenantId - * @param {string} resourceName - * @param {string} filename + * @param {string} resourceName - Resource name. + * @param {string} filename - File name. * @param {Record} params * @returns {Promise} */ diff --git a/packages/server/src/modules/Import/ImportMulter.utils.ts b/packages/server/src/modules/Import/ImportMulter.utils.ts index 4c07658a4..162c8c182 100644 --- a/packages/server/src/modules/Import/ImportMulter.utils.ts +++ b/packages/server/src/modules/Import/ImportMulter.utils.ts @@ -6,7 +6,6 @@ export const getImportsStoragePath = () => { return path.join(global.__static_dirname, `/imports`); }; - export function allowSheetExtensions(req, file, cb) { if ( file.mimetype !== 'text/csv' && @@ -35,5 +34,5 @@ const storage = Multer.diskStorage({ export const uploadImportFileMulterOptions = { storage, limits: { fileSize: 5 * 1024 * 1024 }, - fileFilter: allowSheetExtensions, + // fileFilter: allowSheetExtensions, }; \ No newline at end of file diff --git a/packages/server/src/modules/Import/ImportRemoveExpiredFiles.ts b/packages/server/src/modules/Import/ImportRemoveExpiredFiles.ts index 1c479745f..653be77ea 100644 --- a/packages/server/src/modules/Import/ImportRemoveExpiredFiles.ts +++ b/packages/server/src/modules/Import/ImportRemoveExpiredFiles.ts @@ -19,6 +19,7 @@ export class ImportDeleteExpiredFiles { const expiredImports = await this.importModel .query() .where('createdAt', '<', yesterday); + await bluebird.map( expiredImports, async (expiredImport) => { diff --git a/packages/server/src/modules/Import/_utils.ts b/packages/server/src/modules/Import/_utils.ts index 63d58e25e..588252da0 100644 --- a/packages/server/src/modules/Import/_utils.ts +++ b/packages/server/src/modules/Import/_utils.ts @@ -434,7 +434,7 @@ export const getMapToPath = (to: string, group = '') => group ? `${group}.${to}` : to; export const getImportsStoragePath = () => { - return path.join(global.__storage_dir, `/imports`); + return path.join(global.__static_dirname, `/imports`); }; /** diff --git a/packages/server/src/modules/Import/jobs/ImportDeleteExpiredFilesJob.ts b/packages/server/src/modules/Import/jobs/ImportDeleteExpiredFilesJob.ts index 849703546..3a76037fc 100644 --- a/packages/server/src/modules/Import/jobs/ImportDeleteExpiredFilesJob.ts +++ b/packages/server/src/modules/Import/jobs/ImportDeleteExpiredFilesJob.ts @@ -1,28 +1,23 @@ -// import Container, { Service } from 'typedi'; -// import { ImportDeleteExpiredFiles } from '../ImportRemoveExpiredFiles'; +import { Cron } from '@nestjs/schedule'; +import { Injectable } from '@nestjs/common'; +import { ImportDeleteExpiredFiles } from '../ImportRemoveExpiredFiles'; -// @Service() -// export class ImportDeleteExpiredFilesJobs { -// /** -// * Constructor method. -// */ -// constructor(agenda) { -// agenda.define('delete-expired-imported-files', this.handler); -// } +@Injectable() +export class ImportDeleteExpiredFilesJobs { + constructor( + private readonly importDeleteExpiredFiles: ImportDeleteExpiredFiles, + ) {} -// /** -// * Triggers sending invoice mail. -// */ -// private handler = async (job, done: Function) => { -// const importDeleteExpiredFiles = Container.get(ImportDeleteExpiredFiles); - -// try { -// console.log('Delete expired import files has started.'); -// await importDeleteExpiredFiles.deleteExpiredFiles(); -// done(); -// } catch (error) { -// console.log(error); -// done(error); -// } -// }; -// } + /** + * Triggers sending invoice mail. + */ + @Cron('* * * * *') + async importDeleteExpiredJob() { + try { + console.log('Delete expired import files has started.'); + await this.importDeleteExpiredFiles.deleteExpiredFiles(); + } catch (error) { + console.log(error); + } + } +} diff --git a/packages/server/src/modules/Import/models/Import.ts b/packages/server/src/modules/Import/models/Import.ts index 680ad8584..6a61a1597 100644 --- a/packages/server/src/modules/Import/models/Import.ts +++ b/packages/server/src/modules/Import/models/Import.ts @@ -67,7 +67,7 @@ export class ImportModel extends BaseModel { * Relationship mapping. */ static get relationMappings() { - const Tenant = require('system/models/Tenant'); + const { TenantModel } = require('../../System/models/TenantModel'); return { /** @@ -75,7 +75,7 @@ export class ImportModel extends BaseModel { */ tenant: { relation: Model.BelongsToOneRelation, - modelClass: Tenant.default, + modelClass: TenantModel, join: { from: 'imports.tenantId', to: 'tenants.id', diff --git a/packages/server/src/modules/Import/sheet_utils.ts b/packages/server/src/modules/Import/sheet_utils.ts index b21f07320..6fecb5425 100644 --- a/packages/server/src/modules/Import/sheet_utils.ts +++ b/packages/server/src/modules/Import/sheet_utils.ts @@ -1,4 +1,4 @@ -import XLSX from 'xlsx'; +import * as XLSX from 'xlsx'; import { first } from 'lodash'; /** diff --git a/packages/server/src/modules/Items/ItemsImportable.service.ts b/packages/server/src/modules/Items/ItemsImportable.service.ts index b17ee1f76..d5bc5aecc 100644 --- a/packages/server/src/modules/Items/ItemsImportable.service.ts +++ b/packages/server/src/modules/Items/ItemsImportable.service.ts @@ -18,8 +18,7 @@ export class ItemsImportable extends Importable { /** * Mapps the imported data to create a new item service. - * @param {number} tenantId - * @param {ICustomerNewDTO} createDTO + * @param {CreateItemDto} createDTO * @param {Knex.Transaction} trx * @returns {Promise} */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61518ffee..9728c3275 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: '@nestjs/platform-express': specifier: ^10.0.0 version: 10.4.7(@nestjs/common@10.4.7)(@nestjs/core@10.4.7) + '@nestjs/schedule': + specifier: ^4.1.2 + version: 4.1.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7) '@nestjs/swagger': specifier: ^7.4.2 version: 7.4.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) @@ -7084,6 +7087,18 @@ packages: transitivePeerDependencies: - supports-color + /@nestjs/schedule@4.1.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7): + resolution: {integrity: sha512-hCTQ1lNjIA5EHxeu8VvQu2Ed2DBLS1GSC6uKPYlBiQe6LL9a7zfE9iVSK+zuK8E2odsApteEBmfAQchc8Hx0Gg==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.4.7(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.4.7(@nestjs/common@10.4.7)(@nestjs/platform-express@10.4.7)(reflect-metadata@0.2.2)(rxjs@7.8.1) + cron: 3.2.1 + uuid: 11.0.3 + dev: false + /@nestjs/schematics@10.2.3(chokidar@3.6.0)(typescript@5.6.3): resolution: {integrity: sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==} peerDependencies: @@ -11637,6 +11652,10 @@ packages: /@types/lodash@4.17.4: resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==} + /@types/luxon@3.4.2: + resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} + dev: false + /@types/mathjs@6.0.12: resolution: {integrity: sha512-bpKs8CDJ0aOiiJguywryE/U6Wre/uftJ89xhp4aCgF4oRb3Yug2VyZ87958gmSeq4WMsvWPMs2Q5TtFv+dJtaA==} dependencies: @@ -15212,6 +15231,13 @@ packages: luxon: 3.5.0 dev: false + /cron@3.2.1: + resolution: {integrity: sha512-w2n5l49GMmmkBFEsH9FIDhjZ1n1QgTMOCMGuQtOXs5veNiosZmso6bQGuqOJSYAXXrG84WQFVneNk+Yt0Ua9iw==} + dependencies: + '@types/luxon': 3.4.2 + luxon: 3.5.0 + dev: false + /cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -28560,9 +28586,9 @@ packages: webpack: ^5.0.0 dependencies: chalk: 4.1.2 - enhanced-resolve: 5.16.1 + enhanced-resolve: 5.17.1 micromatch: 4.0.7 - semver: 7.6.2 + semver: 7.6.3 source-map: 0.7.4 typescript: 5.6.3 webpack: 5.91.0(esbuild@0.18.20)(webpack-cli@5.1.4) @@ -29242,6 +29268,11 @@ packages: hasBin: true dev: false + /uuid@11.0.3: + resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==} + hasBin: true + dev: false + /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true