diff --git a/src/app.module.ts b/src/app.module.ts index 05bbc98..bc9c409 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -18,6 +18,7 @@ import { UserModule } from './modules/user-related/user/user.module'; import { LogModel } from './modules/configuration/log/data/models/log.model'; import { ErrorLogModel } from './modules/configuration/log/data/models/error-log.model'; import { LogModule } from './modules/configuration/log/log.module'; +import { TenantModule } from './modules/user-related/tenant/tenant.module'; @Module({ imports: [ @@ -43,6 +44,7 @@ import { LogModule } from './modules/configuration/log/log.module'; LogModule, UserModule, + TenantModule, UserPrivilegeModule, ], controllers: [], diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index 4b8377a..8c2bc27 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -1,4 +1,5 @@ export enum MODULE_NAME { + TENANT = 'tenants', USER_PRIVILEGE = 'user-privileges', USER_PRIVILEGE_CONFIGURATION = 'user-privilege-configurations', } diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 560e1ca..93420e3 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -2,6 +2,7 @@ export enum TABLE_NAME { LOG = 'logs', ERROR_LOG = 'log_errors', USER = 'users', + TENANT = 'tenants', USER_PRIVILEGE = 'user_privileges', USER_PRIVILEGE_CONFIGURATION = 'user_privilege_configurations', } diff --git a/src/database/migrations/1717991911676-update-tenant.ts b/src/database/migrations/1717991911676-update-tenant.ts new file mode 100644 index 0000000..13068fb --- /dev/null +++ b/src/database/migrations/1717991911676-update-tenant.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateTenant1717991911676 implements MigrationInterface { + name = 'UpdateTenant1717991911676'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" ADD "share_margin" numeric`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "share_margin"`); + } +} diff --git a/src/modules/user-related/tenant/constants.ts b/src/modules/user-related/tenant/constants.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/user-related/tenant/domain/entities/event/tenant-change-status.event.ts b/src/modules/user-related/tenant/domain/entities/event/tenant-change-status.event.ts new file mode 100644 index 0000000..17d589c --- /dev/null +++ b/src/modules/user-related/tenant/domain/entities/event/tenant-change-status.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TenantChangeStatusEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/user-related/tenant/domain/entities/event/tenant-created.event.ts b/src/modules/user-related/tenant/domain/entities/event/tenant-created.event.ts new file mode 100644 index 0000000..f7d91ba --- /dev/null +++ b/src/modules/user-related/tenant/domain/entities/event/tenant-created.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TenantCreatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/user-related/tenant/domain/entities/event/tenant-deleted.event.ts b/src/modules/user-related/tenant/domain/entities/event/tenant-deleted.event.ts new file mode 100644 index 0000000..39961e6 --- /dev/null +++ b/src/modules/user-related/tenant/domain/entities/event/tenant-deleted.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TenantDeletedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/user-related/tenant/domain/entities/event/tenant-updated.event.ts b/src/modules/user-related/tenant/domain/entities/event/tenant-updated.event.ts new file mode 100644 index 0000000..deebd6d --- /dev/null +++ b/src/modules/user-related/tenant/domain/entities/event/tenant-updated.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TenantUpdatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/active-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/active-tenant.manager.ts new file mode 100644 index 0000000..175168d --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/active-tenant.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantChangeStatusEvent } from '../../entities/event/tenant-change-status.event'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; + +@Injectable() +export class ActiveTenantManager extends BaseUpdateStatusManager { + get validateRelations(): validateRelations[] { + return []; + } + + getResult(): string { + return `Success active data ${this.result.name}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/batch-active-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/batch-active-tenant.manager.ts new file mode 100644 index 0000000..3ebcc9f --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/batch-active-tenant.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantChangeStatusEvent } from '../../entities/event/tenant-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; + +@Injectable() +export class BatchActiveTenantManager extends BaseBatchUpdateStatusManager { + get validateRelations(): validateRelations[] { + return []; + } + + validateData(data: UserEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/batch-confirm-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/batch-confirm-tenant.manager.ts new file mode 100644 index 0000000..617d447 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/batch-confirm-tenant.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantChangeStatusEvent } from '../../entities/event/tenant-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; + +@Injectable() +export class BatchConfirmTenantManager extends BaseBatchUpdateStatusManager { + get validateRelations(): validateRelations[] { + return []; + } + + validateData(data: UserEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/batch-delete-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/batch-delete-tenant.manager.ts new file mode 100644 index 0000000..b3a3c03 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/batch-delete-tenant.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchDeleteManager } from 'src/core/modules/domain/usecase/managers/base-batch-delete.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantDeletedEvent } from '../../entities/event/tenant-deleted.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; + +@Injectable() +export class BatchDeleteTenantManager extends BaseBatchDeleteManager { + get validateRelations(): validateRelations[] { + return []; + } + + async beforeProcess(): Promise { + return; + } + + async validateData(data: UserEntity): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantDeletedEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/batch-inactive-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/batch-inactive-tenant.manager.ts new file mode 100644 index 0000000..8e87cc2 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/batch-inactive-tenant.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantChangeStatusEvent } from '../../entities/event/tenant-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; + +@Injectable() +export class BatchInactiveTenantManager extends BaseBatchUpdateStatusManager { + get validateRelations(): validateRelations[] { + return []; + } + + validateData(data: UserEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/confirm-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/confirm-tenant.manager.ts new file mode 100644 index 0000000..c155336 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/confirm-tenant.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantChangeStatusEvent } from '../../entities/event/tenant-change-status.event'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; + +@Injectable() +export class ConfirmTenantManager extends BaseUpdateStatusManager { + get validateRelations(): validateRelations[] { + return []; + } + + getResult(): string { + return `Success active data ${this.result.name}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/create-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/create-tenant.manager.ts new file mode 100644 index 0000000..65b4736 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/create-tenant.manager.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager'; +import { TenantCreatedEvent } from '../../entities/event/tenant-created.event'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserRole } from 'src/modules/user-related/user/constants'; +import { hashPassword } from 'src/core/helpers/password/bcrypt.helpers'; +import { SALT_OR_ROUNDS } from 'src/core/strings/constants/base.constants'; + +@Injectable() +export class CreateTenantManager extends BaseCreateManager { + get validateRelations(): validateRelations[] { + return []; + } + + async beforeProcess(): Promise { + Object.assign(this.data, { + role: UserRole.TENANT, + password: await hashPassword(this.data.password, SALT_OR_ROUNDS), + }); + return; + } + + async afterProcess(): Promise { + return; + } + + async generateConfig(): Promise {} + + get uniqueColumns(): columnUniques[] { + return [ + // validate username (all data) + { column: 'username' }, + + // validate name (hanya data dengan role tenant) + { + column: 'name', + query: { + role: UserRole.TENANT, + }, + }, + ]; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantCreatedEvent, + data: this.data, + }, + ]; + } + + get entityTarget(): any { + return UserModel; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/delete-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/delete-tenant.manager.ts new file mode 100644 index 0000000..089f1d2 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/delete-tenant.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantDeletedEvent } from '../../entities/event/tenant-deleted.event'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +@Injectable() +export class DeleteTenantManager extends BaseDeleteManager { + get validateRelations(): validateRelations[] { + return []; + } + + getResult(): string { + return `Success`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantDeletedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/inactive-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/inactive-tenant.manager.ts new file mode 100644 index 0000000..e1f0139 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/inactive-tenant.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantChangeStatusEvent } from '../../entities/event/tenant-change-status.event'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +@Injectable() +export class InactiveTenantManager extends BaseUpdateStatusManager { + get validateRelations(): validateRelations[] { + return []; + } + + getResult(): string { + return `Success inactive data ${this.result.name}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/update-password-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/update-password-tenant.manager.ts new file mode 100644 index 0000000..723293f --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/update-password-tenant.manager.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantUpdatedEvent } from '../../entities/event/tenant-updated.event'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { hashPassword } from 'src/core/helpers/password/bcrypt.helpers'; +import { SALT_OR_ROUNDS } from 'src/core/strings/constants/base.constants'; + +@Injectable() +export class UpdatePasswordTenantManager extends BaseUpdateManager { + get validateRelations(): validateRelations[] { + return []; + } + + async validateProcess(): Promise { + Object.assign(this.data, { + password: await hashPassword(this.data.password, SALT_OR_ROUNDS), + }); + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get uniqueColumns(): columnUniques[] { + return []; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantUpdatedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/managers/update-tenant.manager.ts b/src/modules/user-related/tenant/domain/usecases/managers/update-tenant.manager.ts new file mode 100644 index 0000000..ea02385 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/managers/update-tenant.manager.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TenantUpdatedEvent } from '../../entities/event/tenant-updated.event'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserRole } from 'src/modules/user-related/user/constants'; + +@Injectable() +export class UpdateTenantManager extends BaseUpdateManager { + get validateRelations(): validateRelations[] { + return []; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get uniqueColumns(): columnUniques[] { + return [ + // validate username (all data) + { column: 'username' }, + + // validate name (hanya data dengan role tenant) + { + column: 'name', + query: { + role: UserRole.TENANT, + }, + }, + ]; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TenantUpdatedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/user-related/tenant/domain/usecases/tenant-data.orchestrator.ts b/src/modules/user-related/tenant/domain/usecases/tenant-data.orchestrator.ts new file mode 100644 index 0000000..4dce643 --- /dev/null +++ b/src/modules/user-related/tenant/domain/usecases/tenant-data.orchestrator.ts @@ -0,0 +1,116 @@ +import { Injectable } from '@nestjs/common'; +import { CreateTenantManager } from './managers/create-tenant.manager'; +import { DeleteTenantManager } from './managers/delete-tenant.manager'; +import { UpdateTenantManager } from './managers/update-tenant.manager'; +import { BaseDataTransactionOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-data-transaction.orchestrator'; +import { ActiveTenantManager } from './managers/active-tenant.manager'; +import { InactiveTenantManager } from './managers/inactive-tenant.manager'; +import { ConfirmTenantManager } from './managers/confirm-tenant.manager'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchConfirmTenantManager } from './managers/batch-confirm-tenant.manager'; +import { BatchInactiveTenantManager } from './managers/batch-inactive-tenant.manager'; +import { BatchActiveTenantManager } from './managers/batch-active-tenant.manager'; +import { BatchDeleteTenantManager } from './managers/batch-delete-tenant.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { UserDataService } from 'src/modules/user-related/user/data/services.ts/user-data.service'; +import { UpdatePasswordTenantManager } from './managers/update-password-tenant.manager'; + +@Injectable() +export class TenantDataOrchestrator extends BaseDataTransactionOrchestrator { + constructor( + private createManager: CreateTenantManager, + private updateManager: UpdateTenantManager, + private deleteManager: DeleteTenantManager, + private activeManager: ActiveTenantManager, + private confirmManager: ConfirmTenantManager, + private inactiveManager: InactiveTenantManager, + private batchDeleteManager: BatchDeleteTenantManager, + private batchActiveManager: BatchActiveTenantManager, + private batchConfirmManager: BatchConfirmTenantManager, + private batchInactiveManager: BatchInactiveTenantManager, + private updatePasswordManager: UpdatePasswordTenantManager, + private serviceData: UserDataService, + ) { + super(); + } + + async create(data): Promise { + this.createManager.setData(data); + this.createManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.createManager.execute(); + await this.createManager.generateConfig(); + return this.createManager.getResult(); + } + + async update(dataId, data): Promise { + this.updateManager.setData(dataId, data); + this.updateManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.updateManager.execute(); + return this.updateManager.getResult(); + } + + async updatePassword(dataId, data): Promise { + this.updatePasswordManager.setData(dataId, data); + this.updatePasswordManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.updatePasswordManager.execute(); + return this.updatePasswordManager.getResult(); + } + + async delete(dataId): Promise { + this.deleteManager.setData(dataId); + this.deleteManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.deleteManager.execute(); + return this.deleteManager.getResult(); + } + + async batchDelete(dataIds: string[]): Promise { + this.batchDeleteManager.setData(dataIds); + this.batchDeleteManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.batchDeleteManager.execute(); + return this.batchDeleteManager.getResult(); + } + + async active(dataId): Promise { + this.activeManager.setData(dataId, STATUS.ACTIVE); + this.activeManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.activeManager.execute(); + return this.activeManager.getResult(); + } + + async batchActive(dataIds: string[]): Promise { + this.batchActiveManager.setData(dataIds, STATUS.ACTIVE); + this.batchActiveManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.batchActiveManager.execute(); + return this.batchActiveManager.getResult(); + } + + async confirm(dataId): Promise { + this.confirmManager.setData(dataId, STATUS.ACTIVE); + this.confirmManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.confirmManager.execute(); + return this.confirmManager.getResult(); + } + + async batchConfirm(dataIds: string[]): Promise { + this.batchConfirmManager.setData(dataIds, STATUS.ACTIVE); + this.batchConfirmManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.batchConfirmManager.execute(); + return this.batchConfirmManager.getResult(); + } + + async inactive(dataId): Promise { + this.inactiveManager.setData(dataId, STATUS.INACTIVE); + this.inactiveManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.inactiveManager.execute(); + return this.inactiveManager.getResult(); + } + + async batchInactive(dataIds: string[]): Promise { + this.batchInactiveManager.setData(dataIds, STATUS.INACTIVE); + this.batchInactiveManager.setService(this.serviceData, TABLE_NAME.TENANT); + await this.batchInactiveManager.execute(); + return this.batchInactiveManager.getResult(); + } +} diff --git a/src/modules/user-related/tenant/index.ts b/src/modules/user-related/tenant/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/user-related/tenant/infrastructure/dto/tenant.dto.ts b/src/modules/user-related/tenant/infrastructure/dto/tenant.dto.ts new file mode 100644 index 0000000..9474887 --- /dev/null +++ b/src/modules/user-related/tenant/infrastructure/dto/tenant.dto.ts @@ -0,0 +1,41 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; +import { + IsEmail, + IsNumber, + IsString, + Length, + ValidateIf, +} from 'class-validator'; +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { UserRole } from 'src/modules/user-related/user/constants'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +export class TenantDto extends BaseStatusDto implements UserEntity { + @ApiProperty({ name: 'name', required: true, example: 'Tenant 1' }) + @IsString() + name: string; + + @ApiProperty({ name: 'username', required: true, example: 'tenant1' }) + @IsString() + username: string; + + @ApiProperty({ name: 'share_margin', required: true, example: 85 }) + @IsNumber() + share_margin: number; + + @ApiProperty({ name: 'password', required: true, example: 'Tenant123!' }) + @IsString() + password: string; + + @ApiProperty({ name: 'email', required: false, example: 'tenant@mail.com' }) + @IsEmail() + @ValidateIf((body) => body.email) + email: string; + + @Exclude() + role: UserRole; + + @Exclude() + refresh_token: string; +} diff --git a/src/modules/user-related/tenant/infrastructure/dto/update-password-tenant.dto.ts b/src/modules/user-related/tenant/infrastructure/dto/update-password-tenant.dto.ts new file mode 100644 index 0000000..a4fee8b --- /dev/null +++ b/src/modules/user-related/tenant/infrastructure/dto/update-password-tenant.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; +import { IsString } from 'class-validator'; +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { UserRole } from 'src/modules/user-related/user/constants'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +export class UpdatePasswordTenantDto + extends BaseStatusDto + implements UserEntity +{ + @ApiProperty({ name: 'password', required: true, example: 'Tenant123!' }) + @IsString() + password: string; + + @Exclude() + name: string; + + @Exclude() + username: string; + + @Exclude() + share_margin: number; + + @Exclude() + email: string; + + @Exclude() + role: UserRole; + + @Exclude() + refresh_token: string; +} diff --git a/src/modules/user-related/tenant/infrastructure/dto/update-tenant.dto.ts b/src/modules/user-related/tenant/infrastructure/dto/update-tenant.dto.ts new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/src/modules/user-related/tenant/infrastructure/dto/update-tenant.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; +import { + IsEmail, + IsNumber, + IsString, + Length, + ValidateIf, +} from 'class-validator'; +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { UserRole } from 'src/modules/user-related/user/constants'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +export class UpdateTenantDto extends BaseStatusDto implements UserEntity { + @ApiProperty({ name: 'name', required: true, example: 'Tenant 1' }) + @IsString() + name: string; + + @ApiProperty({ name: 'username', required: true, example: 'tenant1' }) + @IsString() + username: string; + + @ApiProperty({ name: 'share_margin', required: true, example: 85 }) + @IsNumber() + share_margin: number; + + @ApiProperty({ name: 'email', required: false, example: 'tenant@mail.com' }) + @IsEmail() + @ValidateIf((body) => body.email) + email: string; + + @Exclude() + password: string; + + @Exclude() + role: UserRole; + + @Exclude() + refresh_token: string; +} diff --git a/src/modules/user-related/tenant/infrastructure/tenant-data.controller.ts b/src/modules/user-related/tenant/infrastructure/tenant-data.controller.ts new file mode 100644 index 0000000..3992be0 --- /dev/null +++ b/src/modules/user-related/tenant/infrastructure/tenant-data.controller.ts @@ -0,0 +1,88 @@ +import { + Body, + Controller, + Delete, + Param, + Patch, + Post, + Put, +} from '@nestjs/common'; +import { TenantDataOrchestrator } from '../domain/usecases/tenant-data.orchestrator'; +import { TenantDto } from './dto/tenant.dto'; +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'; +import { Public } from 'src/core/guards'; +import { UserEntity } from '../../user/domain/entities/user.entity'; +import { UpdateTenantDto } from './dto/update-tenant.dto'; +import { UpdatePasswordTenantDto } from './dto/update-password-tenant.dto'; + +@ApiTags(`${MODULE_NAME.TENANT.split('-').join(' ')} - data`) +@Controller(MODULE_NAME.TENANT) +@Public(false) +@ApiBearerAuth('JWT') +export class TenantDataController { + constructor(private orchestrator: TenantDataOrchestrator) {} + + @Post() + async create(@Body() data: TenantDto): Promise { + return await this.orchestrator.create(data); + } + + @Put('/batch-delete') + async batchDeleted(@Body() body: BatchIdsDto): Promise { + return await this.orchestrator.batchDelete(body.ids); + } + + @Patch(':id/active') + async active(@Param('id') dataId: string): Promise { + return await this.orchestrator.active(dataId); + } + + @Put('/batch-active') + async batchActive(@Body() body: BatchIdsDto): Promise { + return await this.orchestrator.batchActive(body.ids); + } + + @Patch(':id/confirm') + async confirm(@Param('id') dataId: string): Promise { + return await this.orchestrator.confirm(dataId); + } + + @Put('/batch-confirm') + async batchConfirm(@Body() body: BatchIdsDto): Promise { + return await this.orchestrator.batchConfirm(body.ids); + } + + @Patch(':id/inactive') + async inactive(@Param('id') dataId: string): Promise { + return await this.orchestrator.inactive(dataId); + } + + @Put('/batch-inactive') + async batchInactive(@Body() body: BatchIdsDto): Promise { + return await this.orchestrator.batchInactive(body.ids); + } + + @Put(':id') + async update( + @Param('id') dataId: string, + @Body() data: UpdateTenantDto, + ): Promise { + return await this.orchestrator.update(dataId, data); + } + + @Put(':id/change-password') + async updatePassword( + @Param('id') dataId: string, + @Body() data: UpdatePasswordTenantDto, + ): Promise { + return await this.orchestrator.updatePassword(dataId, data); + } + + @Delete(':id') + async delete(@Param('id') dataId: string): Promise { + return await this.orchestrator.delete(dataId); + } +} diff --git a/src/modules/user-related/tenant/tenant.module.ts b/src/modules/user-related/tenant/tenant.module.ts new file mode 100644 index 0000000..3bb7ee2 --- /dev/null +++ b/src/modules/user-related/tenant/tenant.module.ts @@ -0,0 +1,56 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { TenantReadController } from './infrastructure/tenant-read.controller'; +import { TenantReadOrchestrator } from './domain/usecases/tenant-read.orchestrator'; +import { TenantDataController } from './infrastructure/tenant-data.controller'; +import { TenantDataOrchestrator } from './domain/usecases/tenant-data.orchestrator'; +import { CreateTenantManager } from './domain/usecases/managers/create-tenant.manager'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexTenantManager } from './domain/usecases/managers/index-tenant.manager'; +import { DeleteTenantManager } from './domain/usecases/managers/delete-tenant.manager'; +import { UpdateTenantManager } from './domain/usecases/managers/update-tenant.manager'; +import { ActiveTenantManager } from './domain/usecases/managers/active-tenant.manager'; +import { ConfirmTenantManager } from './domain/usecases/managers/confirm-tenant.manager'; +import { InactiveTenantManager } from './domain/usecases/managers/inactive-tenant.manager'; +import { DetailTenantManager } from './domain/usecases/managers/detail-tenant.manager'; +import { BatchDeleteTenantManager } from './domain/usecases/managers/batch-delete-tenant.manager'; +import { BatchActiveTenantManager } from './domain/usecases/managers/batch-active-tenant.manager'; +import { BatchConfirmTenantManager } from './domain/usecases/managers/batch-confirm-tenant.manager'; +import { BatchInactiveTenantManager } from './domain/usecases/managers/batch-inactive-tenant.manager'; +import { UserModel } from '../user/data/models/user.model'; +import { UserDataService } from '../user/data/services.ts/user-data.service'; +import { UserReadService } from '../user/data/services.ts/user-read.service'; +import { UpdatePasswordTenantManager } from './domain/usecases/managers/update-password-tenant.manager'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), + CqrsModule, + ], + controllers: [TenantDataController, TenantReadController], + providers: [ + IndexTenantManager, + DetailTenantManager, + CreateTenantManager, + DeleteTenantManager, + UpdateTenantManager, + UpdatePasswordTenantManager, + ActiveTenantManager, + ConfirmTenantManager, + InactiveTenantManager, + BatchDeleteTenantManager, + BatchActiveTenantManager, + BatchConfirmTenantManager, + BatchInactiveTenantManager, + + UserDataService, + UserReadService, + + TenantDataOrchestrator, + TenantReadOrchestrator, + ], +}) +export class TenantModule {} diff --git a/src/modules/user-related/user/data/models/user.model.ts b/src/modules/user-related/user/data/models/user.model.ts index ae0afc1..7d31a39 100644 --- a/src/modules/user-related/user/data/models/user.model.ts +++ b/src/modules/user-related/user/data/models/user.model.ts @@ -22,9 +22,6 @@ export class UserModel @Column('varchar', { name: 'password', nullable: true }) password: string; - @Column('varchar', { name: 'email', nullable: true }) - email: string; - @Column('enum', { name: 'role', enum: UserRole, default: UserRole.STAFF }) role: UserRole; @@ -36,4 +33,11 @@ export class UserModel }) @JoinColumn({ name: 'user_privilege_id' }) user_privilege: UserPrivilegeModel; + + // tenant data + @Column('decimal', { name: 'share_margin', nullable: true }) + share_margin: number; + + @Column('varchar', { name: 'email', nullable: true }) + email: string; } diff --git a/src/modules/user-related/user/domain/entities/user.entity.ts b/src/modules/user-related/user/domain/entities/user.entity.ts index a2b4c25..11f3363 100644 --- a/src/modules/user-related/user/domain/entities/user.entity.ts +++ b/src/modules/user-related/user/domain/entities/user.entity.ts @@ -5,7 +5,10 @@ export interface UserEntity extends BaseStatusEntity { name: string; username: string; password: string; - email: string; role: UserRole; refresh_token: string; + + // tenant data + share_margin: number; + email: string; }