diff --git a/src/modules/transaction/reconciliation/constants.ts b/src/modules/transaction/reconciliation/constants.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts new file mode 100644 index 0000000..b70dd80 --- /dev/null +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts @@ -0,0 +1,61 @@ +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 { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { + HttpStatus, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; +import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; + +@Injectable() +export class BatchCancelReconciliationManager extends BaseBatchUpdateStatusManager { + validateData(data: TransactionEntity): Promise { + Object.assign(data, { + reconciliation_mdr: this.data.reconciliation_mdr ?? null, + reconciliation_confirm_by: this.user.name, + reconciliation_confirm_date: new Date().getTime(), + status: this.dataStatus, + reconciliation_status: this.dataStatus, + payment_date: this.data.payment_date, + }); + + if (data.is_recap_transaction) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! cant cancel recap data`, + error: 'Unprocessable Entity', + }); + } + + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return TransactionModel; + } + + get eventTopics(): EventTopics[] { + return []; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts new file mode 100644 index 0000000..e074b10 --- /dev/null +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts @@ -0,0 +1,53 @@ +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 { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; +import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; + +@Injectable() +export class BatchConfirmReconciliationManager extends BaseBatchUpdateStatusManager { + validateData(data: TransactionEntity): Promise { + const net_profit = data.reconciliation_mdr + ? Number(this.data.payment_total) - Number(this.data.reconciliation_mdr) + : null; + + Object.assign(data, { + reconciliation_mdr: this.data.reconciliation_mdr ?? null, + reconciliation_confirm_by: this.user.name, + reconciliation_confirm_date: new Date().getTime(), + status: this.oldData.status, + reconciliation_status: this.dataStatus, + payment_total_net_profit: net_profit, + payment_date: this.data.payment_date, + }); + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return TransactionModel; + } + + get eventTopics(): EventTopics[] { + return []; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts new file mode 100644 index 0000000..8874667 --- /dev/null +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts @@ -0,0 +1,59 @@ +import { + HttpStatus, + Injectable, + UnprocessableEntityException, +} 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 { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; + +@Injectable() +export class CancelReconciliationManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success active data ${this.result.id}`; + } + + async validateProcess(): Promise { + if (this.data.is_recap_transaction) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! cant cancel recap data`, + error: 'Unprocessable Entity', + }); + } + return; + } + + async beforeProcess(): Promise { + Object.assign(this.data, { + reconciliation_mdr: this.data.reconciliation_mdr ?? null, + reconciliation_confirm_by: this.user.name, + reconciliation_confirm_date: new Date().getTime(), + status: this.dataStatus, + reconciliation_status: this.dataStatus, + payment_date: this.data.payment_date, + }); + + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return TransactionModel; + } + + get eventTopics(): EventTopics[] { + return []; + } +} diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/confirm-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/confirm-reconciliation.manager.ts new file mode 100644 index 0000000..bca70d8 --- /dev/null +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/confirm-reconciliation.manager.ts @@ -0,0 +1,46 @@ +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 { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; + +@Injectable() +export class ConfirmReconciliationManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success active data ${this.result.id}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + Object.assign(this.data, { + reconciliation_confirm_by: this.user.name, + reconciliation_confirm_date: new Date().getTime(), + status: this.oldData.status, + reconciliation_status: this.dataStatus, + }); + + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return TransactionModel; + } + + get eventTopics(): EventTopics[] { + return []; + } +} diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/update-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/update-reconciliation.manager.ts new file mode 100644 index 0000000..3c30fc1 --- /dev/null +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/update-reconciliation.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 { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; + +@Injectable() +export class UpdateReconciliationManager extends BaseUpdateManager { + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + const net_profit = this.data.reconciliation_mdr + ? Number(this.oldData.payment_total) - + Number(this.data.reconciliation_mdr) + : null; + + Object.assign(this.data, { + reconciliation_mdr: this.data.reconciliation_mdr ?? null, + payment_total_net_profit: net_profit, + payment_date: this.data.payment_date ?? this.oldData.payment_total, + }); + + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return []; + } + + get entityTarget(): any { + return TransactionModel; + } + + get eventTopics(): EventTopics[] { + return []; + } +} diff --git a/src/modules/transaction/reconciliation/domain/usecases/reconciliation-data.orchestrator.ts b/src/modules/transaction/reconciliation/domain/usecases/reconciliation-data.orchestrator.ts new file mode 100644 index 0000000..34bf1a6 --- /dev/null +++ b/src/modules/transaction/reconciliation/domain/usecases/reconciliation-data.orchestrator.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@nestjs/common'; +import { UpdateReconciliationManager } from './managers/update-reconciliation.manager'; +import { ConfirmReconciliationManager } from './managers/confirm-reconciliation.manager'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchConfirmReconciliationManager } from './managers/batch-confirm-reconciliation.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { CancelReconciliationManager } from './managers/cancel-reconciliation.manager'; +import { BatchCancelReconciliationManager } from './managers/batch-cancel-reconciliation.manager'; +import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; +import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; + +@Injectable() +export class ReconciliationDataOrchestrator { + constructor( + private updateManager: UpdateReconciliationManager, + private confirmManager: ConfirmReconciliationManager, + private cancelManager: CancelReconciliationManager, + private batchConfirmManager: BatchConfirmReconciliationManager, + private batchCancelManager: BatchCancelReconciliationManager, + private serviceData: TransactionDataService, + ) {} + + async update(dataId, data): Promise { + this.updateManager.setData(dataId, data); + this.updateManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); + await this.updateManager.execute(); + return this.updateManager.getResult(); + } + + async confirm(dataId): Promise { + this.confirmManager.setData(dataId, STATUS.CONFIRMED); + this.confirmManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); + await this.confirmManager.execute(); + return this.confirmManager.getResult(); + } + + async batchConfirm(dataIds: string[]): Promise { + this.batchConfirmManager.setData(dataIds, STATUS.CONFIRMED); + this.batchConfirmManager.setService( + this.serviceData, + TABLE_NAME.TRANSACTION, + ); + await this.batchConfirmManager.execute(); + return this.batchConfirmManager.getResult(); + } + + async cancel(dataId): Promise { + this.cancelManager.setData(dataId, STATUS.REJECTED); + this.cancelManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); + await this.cancelManager.execute(); + return this.cancelManager.getResult(); + } + + async batchCancel(dataIds: string[]): Promise { + this.batchCancelManager.setData(dataIds, STATUS.REJECTED); + this.batchCancelManager.setService( + this.serviceData, + TABLE_NAME.TRANSACTION, + ); + await this.batchCancelManager.execute(); + return this.batchCancelManager.getResult(); + } +} diff --git a/src/modules/transaction/reconciliation/index.ts b/src/modules/transaction/reconciliation/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/transaction/reconciliation/infrastructure/dto/reconciliation.dto.ts b/src/modules/transaction/reconciliation/infrastructure/dto/reconciliation.dto.ts new file mode 100644 index 0000000..a8226e8 --- /dev/null +++ b/src/modules/transaction/reconciliation/infrastructure/dto/reconciliation.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UpdateReconciliationDto { + @ApiProperty({ + type: Date, + required: true, + example: '2024/06/06', + }) + payment_date: Date; + + @ApiProperty({ + type: Number, + required: false, + example: 350000, + }) + reconciliation_mdr: number; +} diff --git a/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts b/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts new file mode 100644 index 0000000..00c87c0 --- /dev/null +++ b/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts @@ -0,0 +1,53 @@ +import { + Body, + Controller, + Delete, + Param, + Patch, + Post, + Put, +} from '@nestjs/common'; +import { ReconciliationDataOrchestrator } from '../domain/usecases/reconciliation-data.orchestrator'; +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 { TransactionEntity } from '../../transaction/domain/entities/transaction.entity'; +import { UpdateReconciliationDto } from './dto/reconciliation.dto'; + +@ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`) +@Controller(`v1/${MODULE_NAME.RECONCILIATION}`) +@Public(false) +@ApiBearerAuth('JWT') +export class ReconciliationDataController { + constructor(private orchestrator: ReconciliationDataOrchestrator) {} + + @Put('/batch-confirm') + async batchConfirm(@Body() body: BatchIdsDto): Promise { + return await this.orchestrator.batchConfirm(body.ids); + } + + @Patch(':id/confirm') + async confirm(@Param('id') dataId: string): Promise { + return await this.orchestrator.confirm(dataId); + } + + @Patch(':id/cancel') + async cancel(@Param('id') dataId: string): Promise { + return await this.orchestrator.cancel(dataId); + } + + @Put('/batch-cancel') + async batchCancel(@Body() body: BatchIdsDto): Promise { + return await this.orchestrator.batchCancel(body.ids); + } + + @Put(':id') + async update( + @Param('id') dataId: string, + @Body() data: UpdateReconciliationDto, + ): Promise { + return await this.orchestrator.update(dataId, data); + } +} diff --git a/src/modules/transaction/reconciliation/reconciliation.module.ts b/src/modules/transaction/reconciliation/reconciliation.module.ts new file mode 100644 index 0000000..7b8d1e0 --- /dev/null +++ b/src/modules/transaction/reconciliation/reconciliation.module.ts @@ -0,0 +1,44 @@ +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 { ReconciliationReadController } from './infrastructure/reconciliation-read.controller'; +import { ReconciliationReadOrchestrator } from './domain/usecases/reconciliation-read.orchestrator'; +import { ReconciliationDataController } from './infrastructure/reconciliation-data.controller'; +import { ReconciliationDataOrchestrator } from './domain/usecases/reconciliation-data.orchestrator'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexReconciliationManager } from './domain/usecases/managers/index-reconciliation.manager'; +import { UpdateReconciliationManager } from './domain/usecases/managers/update-reconciliation.manager'; +import { ConfirmReconciliationManager } from './domain/usecases/managers/confirm-reconciliation.manager'; +import { DetailReconciliationManager } from './domain/usecases/managers/detail-reconciliation.manager'; +import { TransactionModel } from '../transaction/data/models/transaction.model'; +import { TransactionDataService } from '../transaction/data/services/transaction-data.service'; +import { TransactionReadService } from '../transaction/data/services/transaction-read.service'; +import { CancelReconciliationManager } from './domain/usecases/managers/cancel-reconciliation.manager'; +import { BatchCancelReconciliationManager } from './domain/usecases/managers/batch-cancel-reconciliation.manager'; +import { BatchConfirmReconciliationManager } from './domain/usecases/managers/batch-confirm-reconciliation.manager'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature([TransactionModel], CONNECTION_NAME.DEFAULT), + CqrsModule, + ], + controllers: [ReconciliationDataController, ReconciliationReadController], + providers: [ + IndexReconciliationManager, + DetailReconciliationManager, + UpdateReconciliationManager, + ConfirmReconciliationManager, + BatchConfirmReconciliationManager, + CancelReconciliationManager, + BatchCancelReconciliationManager, + + TransactionDataService, + TransactionReadService, + + ReconciliationDataOrchestrator, + ReconciliationReadOrchestrator, + ], +}) +export class ReconciliationModule {}