From 72bc4de42d4ab5d57ba8fe088cd2b6893fa53fec Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 2 Jul 2025 18:43:32 +0700 Subject: [PATCH 01/28] feat(SPG-1124): create feature crud data scheduling and data scheduling default --- env/env.development | 4 +- env/env.production | 4 +- src/app.module.ts | 8 ++ .../strings/constants/module.constants.ts | 2 + src/core/strings/constants/table.constants.ts | 2 + .../1751455019718-data-scheduling.ts | 23 ++++ .../data-scheduling/data-scheduling.module.ts | 70 ++++++++++ .../models/data-scheduling-default.model.ts | 13 ++ .../data/models/data-scheduling.model.ts | 22 +++ .../services/data-scheduling-data.service.ts | 17 +++ .../services/data-scheduling-read.service.ts | 17 +++ .../domain/entities/data-scheduling.entity.ts | 13 ++ .../data-scheduling-change-status.event.ts | 5 + .../event/data-scheduling-created.event.ts | 5 + .../event/data-scheduling-deleted.event.ts | 5 + .../event/data-scheduling-updated.event.ts | 5 + .../entities/filter-data-scheduling.entity.ts | 6 + .../data-scheduling-data.orchestrator.ts | 125 +++++++++++++++++ .../data-scheduling-read.orchestrator.ts | 33 +++++ .../active-data-scheduling.manager.ts | 45 ++++++ .../batch-active-data-scheduling.manager.ts | 45 ++++++ .../batch-confirm-data-scheduling.manager.ts | 45 ++++++ .../batch-delete-data-scheduling.manager.ts | 45 ++++++ .../batch-inactive-data-scheduling.manager.ts | 45 ++++++ .../confirm-data-scheduling.manager.ts | 45 ++++++ .../create-data-scheduling.manager.ts | 87 ++++++++++++ .../data-scheduling-default.manager.ts | 66 +++++++++ .../delete-data-scheduling.manager.ts | 45 ++++++ .../detail-data-scheduling.manager.ts | 59 ++++++++ .../inactive-data-scheduling.manager.ts | 45 ++++++ .../managers/index-data-scheduling.manager.ts | 75 ++++++++++ .../update-data-scheduling.manager.ts | 76 ++++++++++ .../data-scheduling-data.controller.ts | 130 ++++++++++++++++++ .../data-scheduling-read.controller.ts | 30 ++++ .../infrastructure/dto/data-scheduling.dto.ts | 92 +++++++++++++ .../dto/filter-data-scheduling.dto.ts | 17 +++ .../guards/setup-scheduling.guard.ts | 26 ++++ .../infrastructure/helpers/index.ts | 7 + 38 files changed, 1402 insertions(+), 2 deletions(-) create mode 100644 src/database/migrations/1751455019718-data-scheduling.ts create mode 100644 src/modules/configuration/data-scheduling/data-scheduling.module.ts create mode 100644 src/modules/configuration/data-scheduling/data/models/data-scheduling-default.model.ts create mode 100644 src/modules/configuration/data-scheduling/data/models/data-scheduling.model.ts create mode 100644 src/modules/configuration/data-scheduling/data/services/data-scheduling-data.service.ts create mode 100644 src/modules/configuration/data-scheduling/data/services/data-scheduling-read.service.ts create mode 100644 src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts create mode 100644 src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-change-status.event.ts create mode 100644 src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-created.event.ts create mode 100644 src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-deleted.event.ts create mode 100644 src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-updated.event.ts create mode 100644 src/modules/configuration/data-scheduling/domain/entities/filter-data-scheduling.entity.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-data.orchestrator.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/active-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/batch-active-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/batch-confirm-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/batch-delete-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/batch-inactive-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/confirm-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/delete-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/detail-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/inactive-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/update-data-scheduling.manager.ts create mode 100644 src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts create mode 100644 src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts create mode 100644 src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts create mode 100644 src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts create mode 100644 src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts create mode 100644 src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts diff --git a/env/env.development b/env/env.development index 1e241d4..2941384 100644 --- a/env/env.development +++ b/env/env.development @@ -46,4 +46,6 @@ SUPERSET_ADMIN_USERNAME=admin SUPERSET_ADMIN_PASSWORD=admin WHATSAPP_BUSINESS_ACCOUNT_NUMBER_ID=604883366037548 -WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV \ No newline at end of file +WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV + +SETUP_SCHEDULING_KEY=scheduling_key_example \ No newline at end of file diff --git a/env/env.production b/env/env.production index fce7b16..1d16625 100644 --- a/env/env.production +++ b/env/env.production @@ -43,4 +43,6 @@ SUPERSET_ADMIN_USERNAME=admin SUPERSET_ADMIN_PASSWORD=admin WHATSAPP_BUSINESS_ACCOUNT_NUMBER_ID=604883366037548 -WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV \ No newline at end of file +WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV + +SETUP_SCHEDULING_KEY=scheduling_key_example \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index cef2349..631989f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -107,6 +107,9 @@ import { OtpVerificationModel } from './modules/configuration/otp-verification/d import { OtpVerifierModel } from './modules/configuration/otp-verification/data/models/otp-verifier.model'; import { RescheduleVerificationModel } from './modules/booking-online/order/data/models/reschedule-verification.model'; import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard'; +import { DataSchedulingModel } from './modules/configuration/data-scheduling/data/models/data-scheduling.model'; +import { DataSchedulingModule } from './modules/configuration/data-scheduling/data-scheduling.module'; +import { DataSchedulingDefaultModel } from './modules/configuration/data-scheduling/data/models/data-scheduling-default.model'; @Module({ imports: [ @@ -176,6 +179,10 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard'; OtpVerificationModel, OtpVerifierModel, + + // Data Scheduling + DataSchedulingModel, + DataSchedulingDefaultModel, ], synchronize: false, }), @@ -242,6 +249,7 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard'; BookingOnlineAuthModule, BookingOrderModule, OtpVerificationModule, + DataSchedulingModule, ], controllers: [], providers: [ diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index 8e8896e..f07635c 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -33,4 +33,6 @@ export enum MODULE_NAME { OTP_VERIFICATIONS = 'otp-verification', OTP_VERIFIER = 'otp-verifier', + DATA_SCHEDULING = 'data-scheduling', + DATA_SCHEDULING_DEFAULT = 'data-scheduling-default', } diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 2139ba6..470fa71 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -47,4 +47,6 @@ export enum TABLE_NAME { TIME_GROUPS = 'time_groups', OTP_VERIFICATIONS = 'otp_verifications', OTP_VERIFIER = 'otp_verifier', + DATA_SCHEDULING = 'data_scheduling', + DATA_SCHEDULING_DEFAULT = 'data_scheduling_default', } diff --git a/src/database/migrations/1751455019718-data-scheduling.ts b/src/database/migrations/1751455019718-data-scheduling.ts new file mode 100644 index 0000000..76f2bc9 --- /dev/null +++ b/src/database/migrations/1751455019718-data-scheduling.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DataScheduling1751455019718 implements MigrationInterface { + name = 'DataScheduling1751455019718'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."data_scheduling_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`, + ); + await queryRunner.query( + `CREATE TABLE "data_scheduling" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."data_scheduling_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "indexing_key" character varying NOT NULL, "schedule_date_from" date NOT NULL, "schedule_date_to" date NOT NULL, CONSTRAINT "PK_118219da41190e9b934072b4cc5" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "data_scheduling_default" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "default_value" integer NOT NULL, CONSTRAINT "PK_c0c3bb2b53c67440a9b534d42b9" PRIMARY KEY ("id"))`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "data_scheduling_default"`); + await queryRunner.query(`DROP TABLE "data_scheduling"`); + await queryRunner.query(`DROP TYPE "public"."data_scheduling_status_enum"`); + } +} diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts new file mode 100644 index 0000000..c587598 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -0,0 +1,70 @@ +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 { DataSchedulingDataService } from './data/services/data-scheduling-data.service'; +import { DataSchedulingReadService } from './data/services/data-scheduling-read.service'; +import { DataSchedulingReadController } from './infrastructure/data-scheduling-read.controller'; +import { DataSchedulingReadOrchestrator } from './domain/usecases/data-scheduling-read.orchestrator'; +import { + DataSchedulingDataController, + DataSchedulingDefaultController, + DataSchedulingSetupController, +} from './infrastructure/data-scheduling-data.controller'; +import { DataSchedulingDataOrchestrator } from './domain/usecases/data-scheduling-data.orchestrator'; +import { CreateDataSchedulingManager } from './domain/usecases/managers/create-data-scheduling.manager'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexDataSchedulingManager } from './domain/usecases/managers/index-data-scheduling.manager'; +import { DeleteDataSchedulingManager } from './domain/usecases/managers/delete-data-scheduling.manager'; +import { UpdateDataSchedulingManager } from './domain/usecases/managers/update-data-scheduling.manager'; +import { ActiveDataSchedulingManager } from './domain/usecases/managers/active-data-scheduling.manager'; +import { ConfirmDataSchedulingManager } from './domain/usecases/managers/confirm-data-scheduling.manager'; +import { InactiveDataSchedulingManager } from './domain/usecases/managers/inactive-data-scheduling.manager'; +import { DetailDataSchedulingManager } from './domain/usecases/managers/detail-data-scheduling.manager'; +import { BatchDeleteDataSchedulingManager } from './domain/usecases/managers/batch-delete-data-scheduling.manager'; +import { BatchActiveDataSchedulingManager } from './domain/usecases/managers/batch-active-data-scheduling.manager'; +import { BatchConfirmDataSchedulingManager } from './domain/usecases/managers/batch-confirm-data-scheduling.manager'; +import { BatchInactiveDataSchedulingManager } from './domain/usecases/managers/batch-inactive-data-scheduling.manager'; +import { DataSchedulingModel } from './data/models/data-scheduling.model'; +import { DataSchedulingDefaultModel } from './data/models/data-scheduling-default.model'; +import { DataSchedulingDefaultManager } from './domain/usecases/managers/data-scheduling-default.manager'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature( + [DataSchedulingModel, DataSchedulingDefaultModel], + CONNECTION_NAME.DEFAULT, + ), + CqrsModule, + ], + controllers: [ + DataSchedulingDataController, + DataSchedulingReadController, + DataSchedulingDefaultController, + DataSchedulingSetupController, + ], + providers: [ + IndexDataSchedulingManager, + DetailDataSchedulingManager, + CreateDataSchedulingManager, + DeleteDataSchedulingManager, + UpdateDataSchedulingManager, + ActiveDataSchedulingManager, + ConfirmDataSchedulingManager, + InactiveDataSchedulingManager, + BatchDeleteDataSchedulingManager, + BatchActiveDataSchedulingManager, + BatchConfirmDataSchedulingManager, + BatchInactiveDataSchedulingManager, + + DataSchedulingDataService, + DataSchedulingReadService, + + DataSchedulingDataOrchestrator, + DataSchedulingReadOrchestrator, + + DataSchedulingDefaultManager, + ], +}) +export class DataSchedulingModule {} diff --git a/src/modules/configuration/data-scheduling/data/models/data-scheduling-default.model.ts b/src/modules/configuration/data-scheduling/data/models/data-scheduling-default.model.ts new file mode 100644 index 0000000..2ff8728 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/models/data-scheduling-default.model.ts @@ -0,0 +1,13 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { DataSchedulingDefaultEntity } from '../../domain/entities/data-scheduling.entity'; +import { Column, Entity } from 'typeorm'; +import { BaseModel } from 'src/core/modules/data/model/base.model'; + +@Entity(TABLE_NAME.DATA_SCHEDULING_DEFAULT) +export class DataSchedulingDefaultModel + extends BaseModel + implements DataSchedulingDefaultEntity +{ + @Column('int', { nullable: false }) + default_value: number; +} diff --git a/src/modules/configuration/data-scheduling/data/models/data-scheduling.model.ts b/src/modules/configuration/data-scheduling/data/models/data-scheduling.model.ts new file mode 100644 index 0000000..a806445 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/models/data-scheduling.model.ts @@ -0,0 +1,22 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { DataSchedulingEntity } from '../../domain/entities/data-scheduling.entity'; +import { Column, Entity } from 'typeorm'; +import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; + +@Entity(TABLE_NAME.DATA_SCHEDULING) +export class DataSchedulingModel + extends BaseStatusModel + implements DataSchedulingEntity +{ + @Column('varchar', { name: 'name' }) + name: string; + + @Column('varchar', { name: 'indexing_key' }) + indexing_key: string; + + @Column('date', { name: 'schedule_date_from', nullable: false }) + schedule_date_from: Date; + + @Column('date', { name: 'schedule_date_to', nullable: false }) + schedule_date_to: Date; +} diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-data.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-data.service.ts new file mode 100644 index 0000000..689dd20 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-data.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { DataSchedulingEntity } from '../../domain/entities/data-scheduling.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingModel } from '../models/data-scheduling.model'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; + +@Injectable() +export class DataSchedulingDataService extends BaseDataService { + constructor( + @InjectRepository(DataSchedulingModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-read.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-read.service.ts new file mode 100644 index 0000000..7385da0 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-read.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { DataSchedulingEntity } from '../../domain/entities/data-scheduling.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingModel } from '../models/data-scheduling.model'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; +import { BaseReadService } from 'src/core/modules/data/service/base-read.service'; + +@Injectable() +export class DataSchedulingReadService extends BaseReadService { + constructor( + @InjectRepository(DataSchedulingModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts new file mode 100644 index 0000000..49b0e5b --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts @@ -0,0 +1,13 @@ +import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity'; +import { BaseEntity } from 'src/core/modules/domain/entities/base.entity'; + +export interface DataSchedulingEntity extends BaseStatusEntity { + name: string; + indexing_key: number | string; + schedule_date_from: Date; + schedule_date_to: Date; +} + +export interface DataSchedulingDefaultEntity extends BaseEntity { + default_value: number; +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-change-status.event.ts b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-change-status.event.ts new file mode 100644 index 0000000..86c8e9c --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-change-status.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class DataSchedulingChangeStatusEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-created.event.ts b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-created.event.ts new file mode 100644 index 0000000..0ce8274 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-created.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class DataSchedulingCreatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-deleted.event.ts b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-deleted.event.ts new file mode 100644 index 0000000..592233a --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-deleted.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class DataSchedulingDeletedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-updated.event.ts b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-updated.event.ts new file mode 100644 index 0000000..e888703 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-updated.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class DataSchedulingUpdatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/filter-data-scheduling.entity.ts b/src/modules/configuration/data-scheduling/domain/entities/filter-data-scheduling.entity.ts new file mode 100644 index 0000000..5ea5426 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/entities/filter-data-scheduling.entity.ts @@ -0,0 +1,6 @@ +import { BaseFilterEntity } from 'src/core/modules/domain/entities/base-filter.entity'; + +export interface FilterDataSchedulingEntity extends BaseFilterEntity { + schedule_date_from: Date; + schedule_date_to: Date; +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-data.orchestrator.ts b/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-data.orchestrator.ts new file mode 100644 index 0000000..086bcef --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-data.orchestrator.ts @@ -0,0 +1,125 @@ +import { Injectable } from '@nestjs/common'; +import { CreateDataSchedulingManager } from './managers/create-data-scheduling.manager'; +import { DataSchedulingDataService } from '../../data/services/data-scheduling-data.service'; +import { DataSchedulingEntity } from '../entities/data-scheduling.entity'; +import { DeleteDataSchedulingManager } from './managers/delete-data-scheduling.manager'; +import { UpdateDataSchedulingManager } from './managers/update-data-scheduling.manager'; +import { BaseDataTransactionOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-data-transaction.orchestrator'; +import { ActiveDataSchedulingManager } from './managers/active-data-scheduling.manager'; +import { InactiveDataSchedulingManager } from './managers/inactive-data-scheduling.manager'; +import { ConfirmDataSchedulingManager } from './managers/confirm-data-scheduling.manager'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchConfirmDataSchedulingManager } from './managers/batch-confirm-data-scheduling.manager'; +import { BatchInactiveDataSchedulingManager } from './managers/batch-inactive-data-scheduling.manager'; +import { BatchActiveDataSchedulingManager } from './managers/batch-active-data-scheduling.manager'; +import { BatchDeleteDataSchedulingManager } from './managers/batch-delete-data-scheduling.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class DataSchedulingDataOrchestrator extends BaseDataTransactionOrchestrator { + constructor( + private createManager: CreateDataSchedulingManager, + private updateManager: UpdateDataSchedulingManager, + private deleteManager: DeleteDataSchedulingManager, + private activeManager: ActiveDataSchedulingManager, + private confirmManager: ConfirmDataSchedulingManager, + private inactiveManager: InactiveDataSchedulingManager, + private batchDeleteManager: BatchDeleteDataSchedulingManager, + private batchActiveManager: BatchActiveDataSchedulingManager, + private batchConfirmManager: BatchConfirmDataSchedulingManager, + private batchInactiveManager: BatchInactiveDataSchedulingManager, + private serviceData: DataSchedulingDataService, + ) { + super(); + } + + async create(data): Promise { + this.createManager.setData(data); + this.createManager.setService(this.serviceData, TABLE_NAME.DATA_SCHEDULING); + 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.DATA_SCHEDULING); + await this.updateManager.execute(); + return this.updateManager.getResult(); + } + + async delete(dataId): Promise { + this.deleteManager.setData(dataId); + this.deleteManager.setService(this.serviceData, TABLE_NAME.DATA_SCHEDULING); + await this.deleteManager.execute(); + return this.deleteManager.getResult(); + } + + async batchDelete(dataIds: string[]): Promise { + this.batchDeleteManager.setData(dataIds); + this.batchDeleteManager.setService( + this.serviceData, + TABLE_NAME.DATA_SCHEDULING, + ); + 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.DATA_SCHEDULING); + 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.DATA_SCHEDULING, + ); + 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.DATA_SCHEDULING, + ); + 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.DATA_SCHEDULING, + ); + 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.DATA_SCHEDULING, + ); + 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.DATA_SCHEDULING, + ); + await this.batchInactiveManager.execute(); + return this.batchInactiveManager.getResult(); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts b/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts new file mode 100644 index 0000000..b27759c --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { IndexDataSchedulingManager } from './managers/index-data-scheduling.manager'; +import { DataSchedulingReadService } from '../../data/services/data-scheduling-read.service'; +import { DataSchedulingEntity } from '../entities/data-scheduling.entity'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator'; +import { DetailDataSchedulingManager } from './managers/detail-data-scheduling.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class DataSchedulingReadOrchestrator extends BaseReadOrchestrator { + constructor( + private indexManager: IndexDataSchedulingManager, + private detailManager: DetailDataSchedulingManager, + private serviceData: DataSchedulingReadService, + ) { + super(); + } + + async index(params): Promise> { + this.indexManager.setFilterParam(params); + this.indexManager.setService(this.serviceData, TABLE_NAME.DATA_SCHEDULING); + await this.indexManager.execute(); + return this.indexManager.getResult(); + } + + async detail(dataId: string): Promise { + this.detailManager.setData(dataId); + this.detailManager.setService(this.serviceData, TABLE_NAME.DATA_SCHEDULING); + await this.detailManager.execute(); + return this.detailManager.getResult(); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/active-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/active-data-scheduling.manager.ts new file mode 100644 index 0000000..66fdfee --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/active-data-scheduling.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 { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; + +@Injectable() +export class ActiveDataSchedulingManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success active data ${this.result.name}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-active-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-active-data-scheduling.manager.ts new file mode 100644 index 0000000..d1ad181 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-active-data-scheduling.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchActiveDataSchedulingManager extends BaseBatchUpdateStatusManager { + validateData(data: DataSchedulingEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-confirm-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-confirm-data-scheduling.manager.ts new file mode 100644 index 0000000..51ba35f --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-confirm-data-scheduling.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchConfirmDataSchedulingManager extends BaseBatchUpdateStatusManager { + validateData(data: DataSchedulingEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-delete-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-delete-data-scheduling.manager.ts new file mode 100644 index 0000000..30b3cae --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-delete-data-scheduling.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchDeleteManager } from 'src/core/modules/domain/usecase/managers/base-batch-delete.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingDeletedEvent } from '../../entities/event/data-scheduling-deleted.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchDeleteDataSchedulingManager extends BaseBatchDeleteManager { + async beforeProcess(): Promise { + return; + } + + async validateData(data: DataSchedulingEntity): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingDeletedEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-inactive-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-inactive-data-scheduling.manager.ts new file mode 100644 index 0000000..d74b1fc --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/batch-inactive-data-scheduling.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchInactiveDataSchedulingManager extends BaseBatchUpdateStatusManager { + validateData(data: DataSchedulingEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/confirm-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/confirm-data-scheduling.manager.ts new file mode 100644 index 0000000..20abaa7 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/confirm-data-scheduling.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 { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; + +@Injectable() +export class ConfirmDataSchedulingManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success active data ${this.result.name}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts new file mode 100644 index 0000000..b9ca2fd --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@nestjs/common'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager'; +import { DataSchedulingCreatedEvent } from '../../entities/event/data-scheduling-created.event'; +import { encryptionTotal } from '../../../infrastructure/helpers'; +import * as moment from 'moment'; + +@Injectable() +export class CreateDataSchedulingManager extends BaseCreateManager { + async beforeProcess(): Promise { + const total = this.data.indexing_key; + + if (total > 100) { + throw new Error('Maksimal nilai total adalah 100.'); + } + + const queryBuilder = this.dataService + .getRepository() + .createQueryBuilder(this.tableName); + + const overlapping = await queryBuilder + .where(`${this.tableName}.schedule_date_from <= :schedule_date_to`, { + schedule_date_to: this.data.schedule_date_to, + }) + .andWhere(`${this.tableName}.schedule_date_to >= :schedule_date_from`, { + schedule_date_from: this.data.schedule_date_from, + }) + .getOne(); + + if (this.data) { + Object.assign(this.data, { + indexing_key: encryptionTotal(total), + }); + } + + console.log(this.data); + // Validation date + if (overlapping) { + throw new Error('Tanggal yang dimasukkan beririsan dengan data lain.'); + } else if (this.data.schedule_date_from && this.data.schedule_date_to) { + const start_time = moment(this.data.schedule_date_from); + const end_time = moment(this.data.schedule_date_to); + + if (end_time.isBefore(start_time)) { + throw new Error('Tanggal akhir harus lebih besar dari tanggal mulai.'); + } + return; + } + + return; + } + + async afterProcess(): Promise { + return; + } + + async generateConfig(): Promise { + // TODO: Implement logic here + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return [{ column: 'name' }]; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingCreatedEvent, + data: this.data, + }, + ]; + } + + get entityTarget(): any { + return DataSchedulingModel; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts new file mode 100644 index 0000000..cd31b3e --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -0,0 +1,66 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { UserProvider, UsersSession } from 'src/core/sessions'; +import { BLANK_USER } from 'src/core/strings/constants/base.constants'; +import { EditDataSchedulingDefaultDto } from '../../../infrastructure/dto/data-scheduling.dto'; +import { DataSchedulingDefaultEntity } from '../../entities/data-scheduling.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingDefaultModel } from '../../../data/models/data-scheduling-default.model'; +import { Repository } from 'typeorm'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { SelectQueryBuilder } from 'typeorm'; + +@Injectable() +export class DataSchedulingDefaultManager { + @Inject() + protected userProvider: UserProvider; + + constructor( + @InjectRepository(DataSchedulingDefaultModel) + private repository: Repository, + ) {} + + private getUser(): UsersSession { + try { + return this.userProvider?.user ?? BLANK_USER; + } catch (error) { + return BLANK_USER; + } + } + + get tableName(): string { + return TABLE_NAME.DATA_SCHEDULING_DEFAULT; + } + + private queryBuilder(): SelectQueryBuilder { + return this.repository.createQueryBuilder(this.tableName); + } + + async update( + body: EditDataSchedulingDefaultDto, + ): Promise { + if (body.default_value > 100) { + throw new Error('Value tidak boleh lebih dari 100.'); + } + + const userData = this.getUser(); + const dateNow = new Date().getTime(); + const existData = await this.queryBuilder().getOne(); + + const payload: DataSchedulingDefaultEntity = { + id: existData?.id, + default_value: body.default_value, + creator_id: userData.id as any, + creator_name: userData.name, + editor_id: userData.id as any, + editor_name: userData.name, + created_at: dateNow, + updated_at: dateNow, + }; + + return this.repository.save(payload); + } + + async getData() { + return this.queryBuilder().getOne(); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/delete-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/delete-data-scheduling.manager.ts new file mode 100644 index 0000000..05f9b63 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/delete-data-scheduling.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingDeletedEvent } from '../../entities/event/data-scheduling-deleted.event'; + +@Injectable() +export class DeleteDataSchedulingManager extends BaseDeleteManager { + getResult(): string { + return `Success`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingDeletedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/detail-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/detail-data-scheduling.manager.ts new file mode 100644 index 0000000..c3a74f4 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/detail-data-scheduling.manager.ts @@ -0,0 +1,59 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { BaseDetailManager } from 'src/core/modules/domain/usecase/managers/base-detail.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { RelationParam } from 'src/core/modules/domain/entities/base-filter.entity'; +import { decryptionTotal } from '../../../infrastructure/helpers'; + +@Injectable() +export class DetailDataSchedulingManager extends BaseDetailManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + joinRelations: [], + selectRelations: [], + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.status`, + `${this.tableName}.name`, + `${this.tableName}.indexing_key`, + `${this.tableName}.schedule_date_from`, + `${this.tableName}.schedule_date_to`, + `${this.tableName}.created_at`, + `${this.tableName}.creator_name`, + `${this.tableName}.updated_at`, + `${this.tableName}.editor_name`, + ]; + } + + get setFindProperties(): any { + return { + id: this.dataId, + }; + } + + getResult(): DataSchedulingEntity { + if (!this.result) throw new NotFoundException('Data not found.'); + + const total = decryptionTotal(this.result.indexing_key as string); + return { + ...this.result, + indexing_key: total, + }; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/inactive-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/inactive-data-scheduling.manager.ts new file mode 100644 index 0000000..0b5c9ed --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/inactive-data-scheduling.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 { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; + +@Injectable() +export class InactiveDataSchedulingManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success inactive data ${this.result.name}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts new file mode 100644 index 0000000..afc20a8 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { decryptionTotal } from '../../../infrastructure/helpers'; + +@Injectable() +export class IndexDataSchedulingManager extends BaseIndexManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + joinRelations: [], + selectRelations: [], + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.status`, + `${this.tableName}.name`, + `${this.tableName}.indexing_key`, + `${this.tableName}.schedule_date_from`, + `${this.tableName}.schedule_date_to`, + `${this.tableName}.created_at`, + `${this.tableName}.creator_name`, + `${this.tableName}.updated_at`, + `${this.tableName}.editor_name`, + ]; + } + + get specificFilter(): Param[] { + return [ + { + cols: `${this.tableName}.name`, + data: this.filterParam.names, + }, + ]; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + return queryBuilder; + } + + getResult(): PaginationResponse { + const data = this.result.data; + + return { + ...this.result, + data: data.map((item) => { + const total = decryptionTotal(item.indexing_key as string); + return { ...item, indexing_key: total }; + }), + }; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/update-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/update-data-scheduling.manager.ts new file mode 100644 index 0000000..ff61086 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/update-data-scheduling.manager.ts @@ -0,0 +1,76 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager'; +import { DataSchedulingEntity } from '../../entities/data-scheduling.entity'; +import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; +import { DataSchedulingUpdatedEvent } from '../../entities/event/data-scheduling-updated.event'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { encryptionTotal } from '../../../infrastructure/helpers'; + +@Injectable() +export class UpdateDataSchedulingManager extends BaseUpdateManager { + async validateProcess(): Promise { + const total = this.data.indexing_key; + + if (total > 100) { + throw new Error('Maksimal nilai total adalah 100.'); + } + + const queryBuilder = this.dataService + .getRepository() + .createQueryBuilder(this.tableName); + + const overlapping = await queryBuilder + .where(`${this.tableName}.schedule_date_from <= :schedule_date_to`, { + schedule_date_to: this.data.schedule_date_to, + }) + .andWhere(`${this.tableName}.schedule_date_to >= :schedule_date_from`, { + schedule_date_from: this.data.schedule_date_from, + }) + .andWhere(`${this.tableName}.id != :id`, { id: this.dataId ?? null }) + .getOne(); + + // Validation date + if (overlapping) { + throw new Error('Tanggal yang dimasukkan tidak boleh sama.'); + } else { + //Encryption total data + Object.assign(this.data, { + indexing_key: encryptionTotal(total), + }); + } + + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return [{ column: 'name' }]; + } + + get entityTarget(): any { + return DataSchedulingModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: DataSchedulingUpdatedEvent, + }, + ]; + } +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts new file mode 100644 index 0000000..03b04db --- /dev/null +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts @@ -0,0 +1,130 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Put, + UseGuards, +} from '@nestjs/common'; +import { DataSchedulingDataOrchestrator } from '../domain/usecases/data-scheduling-data.orchestrator'; +import { + SetupDataSchedulingDto, + CreateDataSchedulingDto, + EditDataSchedulingDto, + EditDataSchedulingDefaultDto, +} from './dto/data-scheduling.dto'; +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { + DataSchedulingDefaultEntity, + DataSchedulingEntity, +} from '../domain/entities/data-scheduling.entity'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'; +import { ExcludePrivilege, Public } from 'src/core/guards'; +import { SetupSchedulingGuard } from './guards/setup-scheduling.guard'; +import { DataSchedulingDefaultManager } from '../domain/usecases/managers/data-scheduling-default.manager'; +import { get } from 'http'; + +@ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} - data`) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING}`) +@Public(false) +@ApiBearerAuth('JWT') +export class DataSchedulingDataController { + constructor(private orchestrator: DataSchedulingDataOrchestrator) {} + + @Post() + async create( + @Body() data: CreateDataSchedulingDto, + ): 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: EditDataSchedulingDto, + ): Promise { + return await this.orchestrator.update(dataId, data); + } + + @Delete(':id') + async delete(@Param('id') dataId: string): Promise { + return await this.orchestrator.delete(dataId); + } +} + +@ApiTags( + `${MODULE_NAME.DATA_SCHEDULING_DEFAULT.split('-').join(' ')} setup - Data`, +) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_DEFAULT}`) +@Public(false) +@ApiBearerAuth('JWT') +export class DataSchedulingDefaultController { + constructor(private manager: DataSchedulingDefaultManager) {} + @Post() + async create( + @Body() data: EditDataSchedulingDefaultDto, + ): Promise { + return await this.manager.update(data); + } + + @Get() + async get(): Promise { + console.log('here'); + return await this.manager.getData(); + } +} + +@ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} setup - Data`) +@Controller(``) +@Public(true) +export class DataSchedulingSetupController { + @Post('v1/data-scheduling-setup') + @ExcludePrivilege() + @UseGuards(SetupSchedulingGuard) + async setup( + @Body() data: SetupDataSchedulingDto, + ): Promise { + console.log('payload scheduling setup', { data }); + return; + } +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts new file mode 100644 index 0000000..4d1f17f --- /dev/null +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts @@ -0,0 +1,30 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { FilterDataSchedulingDto } from './dto/filter-data-scheduling.dto'; +import { Pagination } from 'src/core/response'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { DataSchedulingEntity } from '../domain/entities/data-scheduling.entity'; +import { DataSchedulingReadOrchestrator } from '../domain/usecases/data-scheduling-read.orchestrator'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { Public } from 'src/core/guards'; + +@ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} - read`) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING}`) +@Public(false) +@ApiBearerAuth('JWT') +export class DataSchedulingReadController { + constructor(private orchestrator: DataSchedulingReadOrchestrator) {} + + @Get() + @Pagination() + async index( + @Query() params: FilterDataSchedulingDto, + ): Promise> { + return await this.orchestrator.index(params); + } + + @Get(':id') + async detail(@Param('id') id: string): Promise { + return await this.orchestrator.detail(id); + } +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts new file mode 100644 index 0000000..527ec22 --- /dev/null +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts @@ -0,0 +1,92 @@ +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { + DataSchedulingDefaultEntity, + DataSchedulingEntity, +} from '../../domain/entities/data-scheduling.entity'; +import { IsString, ValidateIf } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { BaseCoreDto } from 'src/core/modules/infrastructure/dto/base-core.dto'; +import { Transform } from 'class-transformer'; +import { BaseDto } from 'src/core/modules/infrastructure/dto/base.dto'; + +export class CreateDataSchedulingDto + extends BaseStatusDto + implements DataSchedulingEntity +{ + @ApiProperty({ name: 'name', required: true, example: 'Morning' }) + @IsString() + name: string; + + @ApiProperty({ type: 'integer', required: true, example: 80 }) + @Transform((body) => { + return typeof body.value == 'string' ? Number(body.value) : body.value; + }) + @ValidateIf((body) => body.indexing_key) + indexing_key: number; + + @ApiProperty({ + type: Date, + required: true, + example: '2024-01-01', + }) + schedule_date_from: Date; + + @ApiProperty({ + type: Date, + required: true, + example: '2024-01-02', + }) + schedule_date_to: Date; +} + +export class EditDataSchedulingDto + extends BaseStatusDto + implements DataSchedulingEntity +{ + @ApiProperty({ name: 'name', example: 'Morning' }) + @IsString() + @ValidateIf((body) => body.name) + name: string; + + @ApiProperty({ type: 'integer', required: true, example: 80 }) + @Transform((body) => { + return typeof body.value == 'string' ? Number(body.value) : body.value; + }) + @ValidateIf((body) => body.indexing_key) + indexing_key: number; + + @ApiProperty({ + type: Date, + required: true, + example: '2025-01-01', + }) + schedule_date_from: Date; + + @ApiProperty({ + type: Date, + required: true, + example: '2025-01-02', + }) + schedule_date_to: Date; +} + +export class EditDataSchedulingDefaultDto + extends BaseDto + implements DataSchedulingDefaultEntity +{ + @ApiProperty({ type: 'integer', required: true }) + @Transform((body) => { + return typeof body.value == 'string' ? Number(body.value) : body.value; + }) + @ValidateIf((body) => body.default_value) + default_value: number; +} + +export class SetupDataSchedulingDto { + @ApiProperty({ + type: Date, + required: true, + example: '2024-01-01', + }) + schedule_date: Date; +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts new file mode 100644 index 0000000..99a92e0 --- /dev/null +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts @@ -0,0 +1,17 @@ +import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.dto'; +import { FilterDataSchedulingEntity } from '../../domain/entities/filter-data-scheduling.entity'; +import { ApiProperty } from '@nestjs/swagger'; +import { ValidateIf } from 'class-validator'; + +export class FilterDataSchedulingDto + extends BaseFilterDto + implements FilterDataSchedulingEntity +{ + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.schedule_date_from) + schedule_date_from: Date; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.schedule_date_to) + schedule_date_to: Date; +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts b/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts new file mode 100644 index 0000000..1be4a0e --- /dev/null +++ b/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts @@ -0,0 +1,26 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; + +@Injectable() +export class SetupSchedulingGuard implements CanActivate { + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const setupAuth = request.headers['x-setup-authorization']; + + if (setupAuth) { + try { + const setupKey = process.env.SETUP_SCHEDULING_KEY; + if (setupAuth === setupKey) return true; + else new UnprocessableEntityException('Setup Authorization Not Found.'); + } catch (err) { + throw new UnprocessableEntityException('Invalid authentication.'); + } + } + + throw new UnprocessableEntityException('Invalid authentication'); + } +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts b/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts new file mode 100644 index 0000000..9a0049c --- /dev/null +++ b/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts @@ -0,0 +1,7 @@ +export function encryptionTotal(total: number): string { + return btoa(total.toString()); +} + +export function decryptionTotal(total: string): number { + return Number(atob(total)); +} From c7954f2340af2862afc64a7df12534fac9a0f7c6 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:42:26 +0700 Subject: [PATCH 02/28] feat(SPG-1124): add comment TODO at controller data scheduling setup --- .../infrastructure/data-scheduling-data.controller.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts index 03b04db..a40f404 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts @@ -125,6 +125,13 @@ export class DataSchedulingSetupController { @Body() data: SetupDataSchedulingDto, ): Promise { console.log('payload scheduling setup', { data }); + + // TODO: Implement logic for "Send to Black Hole" configuration. + // 1. Retrieve the relevant scheduling configuration based on the date provided in the request body. + // 2. If a specific scheduling configuration is found, apply its values to the main 'configuration' table. + // These values will then be used for the "Send to Black Hole" logic. + // 3. If no specific scheduling configuration is found for the given date, update the 'configuration' table + // with values from the 'data scheduling default' settings instead. return; } } From fc08e98225dcf6b30373fc82d41fb0cd95c49ca3 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:44:14 +0700 Subject: [PATCH 03/28] feat(SPG-1124): add comment TODO at controller data scheduling setup --- .../infrastructure/data-scheduling-data.controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts index a40f404..075116c 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts @@ -109,7 +109,6 @@ export class DataSchedulingDefaultController { @Get() async get(): Promise { - console.log('here'); return await this.manager.getData(); } } From 23b0d9b10377f591b4f842ea4063b9c77ad9a12a Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 2 Jul 2025 21:40:08 +0700 Subject: [PATCH 04/28] feat(SPG-1124): add comment TODO at controller data scheduling setup --- .../domain/usecases/managers/create-data-scheduling.manager.ts | 1 - .../infrastructure/data-scheduling-data.controller.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts index b9ca2fd..f821cb5 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts @@ -39,7 +39,6 @@ export class CreateDataSchedulingManager extends BaseCreateManager Date: Fri, 4 Jul 2025 14:07:13 +0700 Subject: [PATCH 05/28] feat(SPG-1124): add feature get scheduling active --- .../strings/constants/module.constants.ts | 2 ++ .../data-scheduling/data-scheduling.module.ts | 6 ++-- .../domain/entities/data-scheduling.entity.ts | 4 +++ .../create-data-scheduling.manager.ts | 2 ++ .../data-scheduling-default.manager.ts | 29 ++++++++++++++-- .../data-scheduling-data.controller.ts | 34 +++++++++++++++---- .../dto/filter-data-scheduling.dto.ts | 6 ++++ 7 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index f07635c..912eae8 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -35,4 +35,6 @@ export enum MODULE_NAME { OTP_VERIFIER = 'otp-verifier', DATA_SCHEDULING = 'data-scheduling', DATA_SCHEDULING_DEFAULT = 'data-scheduling-default', + DATA_SCHEDULING_ACTIVE = 'data-scheduling-active', + DATA_SCHEDULING_SETUP = 'data-scheduling-setup', } diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts index c587598..7e0dd21 100644 --- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -7,6 +7,7 @@ import { DataSchedulingReadService } from './data/services/data-scheduling-read. import { DataSchedulingReadController } from './infrastructure/data-scheduling-read.controller'; import { DataSchedulingReadOrchestrator } from './domain/usecases/data-scheduling-read.orchestrator'; import { + DataSchedulingActiveController, DataSchedulingDataController, DataSchedulingDefaultController, DataSchedulingSetupController, @@ -27,7 +28,7 @@ import { BatchConfirmDataSchedulingManager } from './domain/usecases/managers/ba import { BatchInactiveDataSchedulingManager } from './domain/usecases/managers/batch-inactive-data-scheduling.manager'; import { DataSchedulingModel } from './data/models/data-scheduling.model'; import { DataSchedulingDefaultModel } from './data/models/data-scheduling-default.model'; -import { DataSchedulingDefaultManager } from './domain/usecases/managers/data-scheduling-default.manager'; +import { DataSchedulingManager } from './domain/usecases/managers/data-scheduling-default.manager'; @Module({ imports: [ @@ -42,6 +43,7 @@ import { DataSchedulingDefaultManager } from './domain/usecases/managers/data-sc DataSchedulingDataController, DataSchedulingReadController, DataSchedulingDefaultController, + DataSchedulingActiveController, DataSchedulingSetupController, ], providers: [ @@ -64,7 +66,7 @@ import { DataSchedulingDefaultManager } from './domain/usecases/managers/data-sc DataSchedulingDataOrchestrator, DataSchedulingReadOrchestrator, - DataSchedulingDefaultManager, + DataSchedulingManager, ], }) export class DataSchedulingModule {} diff --git a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts index 49b0e5b..9733866 100644 --- a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts +++ b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts @@ -11,3 +11,7 @@ export interface DataSchedulingEntity extends BaseStatusEntity { export interface DataSchedulingDefaultEntity extends BaseEntity { default_value: number; } + +export interface DataSchedulingActiveEntity { + value: number; +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts index f821cb5..1a952dd 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/create-data-scheduling.manager.ts @@ -10,6 +10,7 @@ import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base import { DataSchedulingCreatedEvent } from '../../entities/event/data-scheduling-created.event'; import { encryptionTotal } from '../../../infrastructure/helpers'; import * as moment from 'moment'; +import { STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class CreateDataSchedulingManager extends BaseCreateManager { @@ -36,6 +37,7 @@ export class CreateDataSchedulingManager extends BaseCreateManager, + + @InjectRepository(DataSchedulingModel) + private repoSchedule: Repository, ) {} private getUser(): UsersSession { @@ -63,4 +71,21 @@ export class DataSchedulingDefaultManager { async getData() { return this.queryBuilder().getOne(); } + + async getActiveData(date) { + const qb = this.repoSchedule.createQueryBuilder(TABLE_NAME.DATA_SCHEDULING); + + const findData: DataSchedulingEntity = await qb + .where('status = :status', { status: 'active' }) + .andWhere('schedule_date_from <= :date', { date: date }) + .andWhere('schedule_date_to >= :date', { date: date }) + .getOne(); + + if (!findData) { + const defaultData = await this.queryBuilder().getOne(); + return { value: defaultData?.default_value }; + } + + return { value: decryptionTotal(findData.indexing_key as string) }; + } } diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts index fff9d83..c94187c 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts @@ -7,6 +7,7 @@ import { Patch, Post, Put, + Query, UseGuards, } from '@nestjs/common'; import { DataSchedulingDataOrchestrator } from '../domain/usecases/data-scheduling-data.orchestrator'; @@ -19,6 +20,7 @@ import { import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { + DataSchedulingActiveEntity, DataSchedulingDefaultEntity, DataSchedulingEntity, } from '../domain/entities/data-scheduling.entity'; @@ -26,7 +28,8 @@ import { BatchResult } from 'src/core/response/domain/ok-response.interface'; import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'; import { ExcludePrivilege, Public } from 'src/core/guards'; import { SetupSchedulingGuard } from './guards/setup-scheduling.guard'; -import { DataSchedulingDefaultManager } from '../domain/usecases/managers/data-scheduling-default.manager'; +import { DataSchedulingManager } from '../domain/usecases/managers/data-scheduling-default.manager'; +import { FilterActiveDataSchedulingDto } from './dto/filter-data-scheduling.dto'; @ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} - data`) @Controller(`v1/${MODULE_NAME.DATA_SCHEDULING}`) @@ -92,13 +95,13 @@ export class DataSchedulingDataController { } @ApiTags( - `${MODULE_NAME.DATA_SCHEDULING_DEFAULT.split('-').join(' ')} setup - Data`, + `${MODULE_NAME.DATA_SCHEDULING_DEFAULT.split('-').join(' ')} default - Data`, ) @Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_DEFAULT}`) @Public(false) @ApiBearerAuth('JWT') export class DataSchedulingDefaultController { - constructor(private manager: DataSchedulingDefaultManager) {} + constructor(private manager: DataSchedulingManager) {} @Post() async create( @Body() data: EditDataSchedulingDefaultDto, @@ -112,11 +115,30 @@ export class DataSchedulingDefaultController { } } -@ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} setup - Data`) -@Controller(``) +@ApiTags( + `${MODULE_NAME.DATA_SCHEDULING_ACTIVE.split('-').join(' ')} active - Data`, +) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_ACTIVE}`) +@Public(false) +@ApiBearerAuth('JWT') +export class DataSchedulingActiveController { + constructor(private manager: DataSchedulingManager) {} + + @Get() + async get( + @Query() params: FilterActiveDataSchedulingDto, + ): Promise { + return await this.manager.getActiveData(params?.date); + } +} + +@ApiTags( + `${MODULE_NAME.DATA_SCHEDULING_SETUP.split('-').join(' ')} setup - Data`, +) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_SETUP}`) @Public(true) export class DataSchedulingSetupController { - @Post('v1/data-scheduling-setup') + @Post() @ExcludePrivilege() @UseGuards(SetupSchedulingGuard) async setup( diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts index 99a92e0..454aea5 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts @@ -15,3 +15,9 @@ export class FilterDataSchedulingDto @ValidateIf((body) => body.schedule_date_to) schedule_date_to: Date; } + +export class FilterActiveDataSchedulingDto { + @ApiProperty({ type: 'string', required: true }) + @ValidateIf((body) => body.schedule_date_from) + date: Date; +} From c818129c59306f7808f21c36f3f09df94a725a95 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:06:59 +0700 Subject: [PATCH 06/28] feat: update filter index data scheduling --- .../managers/index-data-scheduling.manager.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts index afc20a8..0c4c86b 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts @@ -58,6 +58,19 @@ export class IndexDataSchedulingManager extends BaseIndexManager, ): SelectQueryBuilder { + if (this.filterParam.schedule_date_from) { + const dateFrom = this.filterParam.schedule_date_from; + queryBuilder.andWhere('schedule_date_from >= :dateFrom', { + dateFrom: dateFrom, + }); + } + + if (this.filterParam.schedule_date_to) { + const dateTo = this.filterParam.schedule_date_to; + queryBuilder.andWhere('schedule_date_from <= :dateTo', { + dateTo: dateTo, + }); + } return queryBuilder; } From 10dafec13d481081484e775d86e15c271979756e Mon Sep 17 00:00:00 2001 From: shancheas Date: Mon, 7 Jul 2025 15:38:13 +0700 Subject: [PATCH 07/28] feat: add percent value configuration --- .../1751353749678-add-key-colum-to-setting.ts | 17 +++++++++++++++++ .../data/models/sales-price-formula.model.ts | 8 ++++++++ .../sales-price-formula-data.service.ts | 2 +- .../dto/sales-price-formula.dto.ts | 6 ++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/database/migrations/1751353749678-add-key-colum-to-setting.ts diff --git a/src/database/migrations/1751353749678-add-key-colum-to-setting.ts b/src/database/migrations/1751353749678-add-key-colum-to-setting.ts new file mode 100644 index 0000000..acf5b72 --- /dev/null +++ b/src/database/migrations/1751353749678-add-key-colum-to-setting.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddKeyColumToSetting1751353749678 implements MigrationInterface { + name = 'AddKeyColumToSetting1751353749678'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "transaction_settings" ADD "key" character varying`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "transaction_settings" DROP COLUMN "key"`, + ); + } +} diff --git a/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts b/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts index eecbf02..0781b25 100644 --- a/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts +++ b/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts @@ -42,4 +42,12 @@ export class TransactionSettingModel { @Column('numeric', { default: 100 }) value: number; + + @Column('varchar', { nullable: true }) + key: string; + + //TODO: add logic to get value from key + percentValue(): number { + return this.value ?? 100; + } } diff --git a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts index 66f0fa9..23e46cf 100644 --- a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts +++ b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts @@ -52,7 +52,7 @@ export class SalesPriceFormulaDataService extends BaseDataService Date: Mon, 7 Jul 2025 17:21:38 +0700 Subject: [PATCH 08/28] feat: update guard time schedule --- package.json | 1 + .../data-scheduling/data-scheduling.module.ts | 12 +++++- .../data-scheduling-default.manager.ts | 22 ++++++++++- .../data-scheduling-data.controller.ts | 38 +++---------------- .../infrastructure/dto/data-scheduling.dto.ts | 8 +--- .../dto/filter-data-scheduling.dto.ts | 6 +-- .../guards/setup-scheduling.guard.ts | 14 +++++++ yarn.lock | 9 ++++- 8 files changed, 63 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index ef1f732..bb8761e 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "mathjs": "^13.0.2", "midtrans-client": "^1.3.1", "moment": "^2.30.1", + "moment-timezone": "^0.6.0", "nano": "^10.1.3", "nodemailer": "^6.9.14", "pdfmake": "^0.2.10", diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts index 7e0dd21..3912b44 100644 --- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -7,7 +7,6 @@ import { DataSchedulingReadService } from './data/services/data-scheduling-read. import { DataSchedulingReadController } from './infrastructure/data-scheduling-read.controller'; import { DataSchedulingReadOrchestrator } from './domain/usecases/data-scheduling-read.orchestrator'; import { - DataSchedulingActiveController, DataSchedulingDataController, DataSchedulingDefaultController, DataSchedulingSetupController, @@ -29,6 +28,11 @@ import { BatchInactiveDataSchedulingManager } from './domain/usecases/managers/b import { DataSchedulingModel } from './data/models/data-scheduling.model'; import { DataSchedulingDefaultModel } from './data/models/data-scheduling-default.model'; import { DataSchedulingManager } from './domain/usecases/managers/data-scheduling-default.manager'; +import { SetupSchedulingGuard } from './infrastructure/guards/setup-scheduling.guard'; + +import { JwtModule } from '@nestjs/jwt'; +import { JWT_EXPIRED } from 'src/core/sessions/constants'; +import { JWT_SECRET } from 'src/core/sessions/constants'; @Module({ imports: [ @@ -37,16 +41,20 @@ import { DataSchedulingManager } from './domain/usecases/managers/data-schedulin [DataSchedulingModel, DataSchedulingDefaultModel], CONNECTION_NAME.DEFAULT, ), + JwtModule.register({ + secret: JWT_SECRET, + signOptions: { expiresIn: JWT_EXPIRED }, + }), CqrsModule, ], controllers: [ DataSchedulingDataController, DataSchedulingReadController, DataSchedulingDefaultController, - DataSchedulingActiveController, DataSchedulingSetupController, ], providers: [ + SetupSchedulingGuard, IndexDataSchedulingManager, DetailDataSchedulingManager, CreateDataSchedulingManager, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts index 7d87a93..fb9da9c 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -13,6 +13,9 @@ import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { SelectQueryBuilder } from 'typeorm'; import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; import { decryptionTotal } from '../../../infrastructure/helpers'; +import * as momentTz from 'moment-timezone'; +import { EventBus } from '@nestjs/cqrs'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; @Injectable() export class DataSchedulingManager { @@ -20,6 +23,8 @@ export class DataSchedulingManager { protected userProvider: UserProvider; constructor( + private eventBus: EventBus, + @InjectRepository(DataSchedulingDefaultModel) private repository: Repository, @@ -72,7 +77,11 @@ export class DataSchedulingManager { return this.queryBuilder().getOne(); } - async getActiveData(date) { + async getActiveData() { + const timeZoneWIB = 'Asia/Jakarta'; + const nowInWIB = momentTz().tz(timeZoneWIB).format('YYYY-MM-DD'); + const date = nowInWIB; + const qb = this.repoSchedule.createQueryBuilder(TABLE_NAME.DATA_SCHEDULING); const findData: DataSchedulingEntity = await qb @@ -86,6 +95,15 @@ export class DataSchedulingManager { return { value: defaultData?.default_value }; } - return { value: decryptionTotal(findData.indexing_key as string) }; + return { value: decryptionTotal(findData.indexing_key as string), date }; + } + + async setupActiveScheduling() { + const activeSchedule = await this.getActiveData(); + await this.eventBus.publish( + new DataSchedulingChangeStatusEvent({ data: activeSchedule } as any), + ); + + return { message: 'Berhasil setup transaction scheduling.' }; } } diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts index c94187c..71dcfd2 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts @@ -7,12 +7,10 @@ import { Patch, Post, Put, - Query, UseGuards, } from '@nestjs/common'; import { DataSchedulingDataOrchestrator } from '../domain/usecases/data-scheduling-data.orchestrator'; import { - SetupDataSchedulingDto, CreateDataSchedulingDto, EditDataSchedulingDto, EditDataSchedulingDefaultDto, @@ -20,7 +18,6 @@ import { import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { - DataSchedulingActiveEntity, DataSchedulingDefaultEntity, DataSchedulingEntity, } from '../domain/entities/data-scheduling.entity'; @@ -29,7 +26,6 @@ import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto' import { ExcludePrivilege, Public } from 'src/core/guards'; import { SetupSchedulingGuard } from './guards/setup-scheduling.guard'; import { DataSchedulingManager } from '../domain/usecases/managers/data-scheduling-default.manager'; -import { FilterActiveDataSchedulingDto } from './dto/filter-data-scheduling.dto'; @ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} - data`) @Controller(`v1/${MODULE_NAME.DATA_SCHEDULING}`) @@ -115,43 +111,19 @@ export class DataSchedulingDefaultController { } } -@ApiTags( - `${MODULE_NAME.DATA_SCHEDULING_ACTIVE.split('-').join(' ')} active - Data`, -) -@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_ACTIVE}`) -@Public(false) -@ApiBearerAuth('JWT') -export class DataSchedulingActiveController { - constructor(private manager: DataSchedulingManager) {} - - @Get() - async get( - @Query() params: FilterActiveDataSchedulingDto, - ): Promise { - return await this.manager.getActiveData(params?.date); - } -} - @ApiTags( `${MODULE_NAME.DATA_SCHEDULING_SETUP.split('-').join(' ')} setup - Data`, ) @Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_SETUP}`) @Public(true) +@ApiBearerAuth('JWT') export class DataSchedulingSetupController { + constructor(private manager: DataSchedulingManager) {} + @Post() @ExcludePrivilege() @UseGuards(SetupSchedulingGuard) - async setup( - @Body() data: SetupDataSchedulingDto, - ): Promise { - console.log('payload scheduling setup', { data }); - - // TODO: Implement logic for "Send to Black Hole" configuration. - // 1. Retrieve the relevant scheduling configuration based on the date provided in the request body. - // 2. If a specific scheduling configuration is found, apply its values to the main 'configuration' table. - // These values will then be used for the "Send to Black Hole" logic. - // 3. If no specific scheduling configuration is found for the given date, update the 'configuration' table - // with values from the 'data scheduling default' settings instead. - return; + async setup(): Promise<{ message: string }> { + return this.manager.setupActiveScheduling(); } } diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts index 527ec22..868f4e8 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts @@ -83,10 +83,6 @@ export class EditDataSchedulingDefaultDto } export class SetupDataSchedulingDto { - @ApiProperty({ - type: Date, - required: true, - example: '2024-01-01', - }) - schedule_date: Date; + // @ApiProperty({ type: 'string', required: true, example: '2025-01-01' }) + // date: Date; } diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts index 454aea5..ab6a50f 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts @@ -17,7 +17,7 @@ export class FilterDataSchedulingDto } export class FilterActiveDataSchedulingDto { - @ApiProperty({ type: 'string', required: true }) - @ValidateIf((body) => body.schedule_date_from) - date: Date; + // @ApiProperty({ type: 'string', required: true }) + // @ValidateIf((body) => body.schedule_date_from) + // date: Date; } diff --git a/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts b/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts index 1be4a0e..4993947 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/guards/setup-scheduling.guard.ts @@ -4,11 +4,15 @@ import { Injectable, UnprocessableEntityException, } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; @Injectable() export class SetupSchedulingGuard implements CanActivate { + constructor(private readonly jwtService: JwtService) {} + async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); + const jwtAuth = request.headers['authorization']; const setupAuth = request.headers['x-setup-authorization']; if (setupAuth) { @@ -19,6 +23,16 @@ export class SetupSchedulingGuard implements CanActivate { } catch (err) { throw new UnprocessableEntityException('Invalid authentication.'); } + } else if (jwtAuth && jwtAuth.startsWith('Bearer ')) { + const token = jwtAuth.split(' ')[1]; + try { + const payload = await this.jwtService.verifyAsync(token); + if (payload) return true; + else new UnprocessableEntityException('Setup Authorization Not Found.'); + return true; + } catch (err) { + throw new UnprocessableEntityException('Invalid JWT token'); + } } throw new UnprocessableEntityException('Invalid authentication'); diff --git a/yarn.lock b/yarn.lock index a4abb7f..f235141 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5607,7 +5607,14 @@ module-details-from-path@^1.0.3: resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== -moment@^2.30.1: +moment-timezone@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.6.0.tgz#c5a6519171f31a64739ea75d33f5c136c08ff608" + integrity sha512-ldA5lRNm3iJCWZcBCab4pnNL3HSZYXVb/3TYr75/1WCTWYuTqYUb5f/S384pncYjJ88lbO8Z4uPDvmoluHJc8Q== + dependencies: + moment "^2.29.4" + +moment@^2.29.4, moment@^2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== From 9e52e35612c82242aa82cc33b428317d46648a1d Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Mon, 7 Jul 2025 17:25:37 +0700 Subject: [PATCH 09/28] feat: update guard time schedule --- .../domain/usecases/managers/data-scheduling-default.manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts index fb9da9c..040bc97 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -104,6 +104,6 @@ export class DataSchedulingManager { new DataSchedulingChangeStatusEvent({ data: activeSchedule } as any), ); - return { message: 'Berhasil setup transaction scheduling.' }; + return { message: 'Success setup transaction schedule.' }; } } From 6fdfe8e33f0ae0056218109dc12d6438c9c7bc76 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Tue, 8 Jul 2025 09:47:12 +0700 Subject: [PATCH 10/28] fix: uncomment event publisher when privilege configuration is changing --- .../update-user-privilege-configuration.helper.ts | 10 ++++++++-- .../update-user-privilege-configuration.manager.ts | 9 +++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/handlers/update-user-privilege-configuration.helper.ts b/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/handlers/update-user-privilege-configuration.helper.ts index 509b891..2efc0a4 100644 --- a/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/handlers/update-user-privilege-configuration.helper.ts +++ b/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/handlers/update-user-privilege-configuration.helper.ts @@ -1,4 +1,8 @@ -import { EventBus, EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { + EventBus, + // EventsHandler, + IEventHandler, +} from '@nestjs/cqrs'; import { UserPrivilegeConfigUpdatedEvent } from '../../../entities/event/user-privilege-configuration-updated.event'; import { UserPrivilegeConfigurationHelper } from '../helpers/generate-user-privilege-configuration.helper'; import { UserPrivilegeDataService } from 'src/modules/user-related/user-privilege/data/service/user-privilege-data.service'; @@ -8,7 +12,9 @@ import { UserPrivilegeUpdatedEvent } from '../../../entities/event/user-privileg import { OPERATION } from 'src/core/strings/constants/base.constants'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; -@EventsHandler(UserPrivilegeConfigUpdatedEvent) +// FIXME => FIX HANDLER FOR ADD NEW MODULE PRIVILEGE CONFIGURATIONS + +// @EventsHandler(UserPrivilegeConfigUpdatedEvent) export class UserPrivilegeConfigUpdateHandler implements IEventHandler { diff --git a/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/managers/update-user-privilege-configuration.manager.ts b/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/managers/update-user-privilege-configuration.manager.ts index 5a61040..aea46b3 100644 --- a/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/managers/update-user-privilege-configuration.manager.ts +++ b/src/modules/user-related/user-privilege/domain/usecases/user-privilege-configuration/managers/update-user-privilege-configuration.manager.ts @@ -6,6 +6,7 @@ import { } from 'src/core/strings/constants/interface.constants'; import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; import { UserPrivilegeConfigurationModel } from 'src/modules/user-related/user-privilege/data/models/user-privilege-configuration.model'; +import { UserPrivilegeConfigUpdatedEvent } from '../../../entities/event/user-privilege-configuration-updated.event'; @Injectable() export class UpdateUserPrivilegeConfigurationManager extends BaseCustomManager { @@ -54,10 +55,10 @@ export class UpdateUserPrivilegeConfigurationManager extends BaseCustomManager Date: Tue, 8 Jul 2025 09:57:36 +0700 Subject: [PATCH 11/28] feat: rename table scheduling and setting --- src/core/strings/constants/table.constants.ts | 9 +++-- ...1745991391299-transaction-setting-model.ts | 17 --------- .../1751455019718-data-scheduling.ts | 23 ------------ .../1751942902581-renam-table-config.ts | 35 +++++++++++++++++++ 4 files changed, 41 insertions(+), 43 deletions(-) delete mode 100644 src/database/migrations/1745991391299-transaction-setting-model.ts delete mode 100644 src/database/migrations/1751455019718-data-scheduling.ts create mode 100644 src/database/migrations/1751942902581-renam-table-config.ts diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 470fa71..a2a5475 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -12,7 +12,7 @@ export enum TABLE_NAME { NEWS = 'news', PAYMENT_METHOD = 'payment_methods', PRICE_FORMULA = 'price_formulas', - TRANSACTION_SETTING = 'transaction_settings', + REFUND = 'refunds', REFUND_ITEM = 'refund_items', SEASON_TYPE = 'season_types', @@ -27,6 +27,8 @@ export enum TABLE_NAME { TRANSACTION_ITEM_TAX = 'transaction_item_taxes', TRANSACTION_ITEM_BREAKDOWN_TAX = 't_breakdown_item_taxes', TRANSACTION_DEMOGRAPHY = 'transaction_demographies', + TRANSACTION_SETTING = 'api_settings', + USER = 'users', USER_LOGIN = 'users_login', LOG_USER_LOGIN = 'log_users_login', @@ -47,6 +49,7 @@ export enum TABLE_NAME { TIME_GROUPS = 'time_groups', OTP_VERIFICATIONS = 'otp_verifications', OTP_VERIFIER = 'otp_verifier', - DATA_SCHEDULING = 'data_scheduling', - DATA_SCHEDULING_DEFAULT = 'data_scheduling_default', + + DATA_SCHEDULING = 'event_scheduling', + DATA_SCHEDULING_DEFAULT = 'event_scheduling_default', } diff --git a/src/database/migrations/1745991391299-transaction-setting-model.ts b/src/database/migrations/1745991391299-transaction-setting-model.ts deleted file mode 100644 index f59344a..0000000 --- a/src/database/migrations/1745991391299-transaction-setting-model.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TransactionSettingModel1745991391299 - implements MigrationInterface -{ - name = 'TransactionSettingModel1745991391299'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "transaction_settings" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "value" numeric NOT NULL DEFAULT '100', CONSTRAINT "PK_db7fb38a439358b499ebdee4761" PRIMARY KEY ("id"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "transaction_settings"`); - } -} diff --git a/src/database/migrations/1751455019718-data-scheduling.ts b/src/database/migrations/1751455019718-data-scheduling.ts deleted file mode 100644 index 76f2bc9..0000000 --- a/src/database/migrations/1751455019718-data-scheduling.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DataScheduling1751455019718 implements MigrationInterface { - name = 'DataScheduling1751455019718'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."data_scheduling_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`, - ); - await queryRunner.query( - `CREATE TABLE "data_scheduling" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."data_scheduling_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "indexing_key" character varying NOT NULL, "schedule_date_from" date NOT NULL, "schedule_date_to" date NOT NULL, CONSTRAINT "PK_118219da41190e9b934072b4cc5" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "data_scheduling_default" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "default_value" integer NOT NULL, CONSTRAINT "PK_c0c3bb2b53c67440a9b534d42b9" PRIMARY KEY ("id"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "data_scheduling_default"`); - await queryRunner.query(`DROP TABLE "data_scheduling"`); - await queryRunner.query(`DROP TYPE "public"."data_scheduling_status_enum"`); - } -} diff --git a/src/database/migrations/1751942902581-renam-table-config.ts b/src/database/migrations/1751942902581-renam-table-config.ts new file mode 100644 index 0000000..3b6ee5a --- /dev/null +++ b/src/database/migrations/1751942902581-renam-table-config.ts @@ -0,0 +1,35 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RenamTableConfig1751942902581 implements MigrationInterface { + name = 'RenamTableConfig1751942902581'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "api_settings" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "value" numeric NOT NULL DEFAULT '100', CONSTRAINT "PK_14bbb118ae1b2bd2385186cffb9" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "public"."event_scheduling_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`, + ); + await queryRunner.query( + `CREATE TABLE "event_scheduling" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."event_scheduling_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "indexing_key" character varying NOT NULL, "schedule_date_from" date NOT NULL, "schedule_date_to" date NOT NULL, CONSTRAINT "PK_a2ccd3f6ab787b0d7e2af09d30c" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "event_scheduling_default" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "default_value" integer NOT NULL, CONSTRAINT "PK_9caf65330e76243e9f9285ae2e1" PRIMARY KEY ("id"))`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "event_scheduling_default"`); + await queryRunner.query(`DROP TABLE "event_scheduling"`); + await queryRunner.query( + `DROP TYPE "public"."event_scheduling_status_enum"`, + ); + await queryRunner.query(`DROP TABLE "api_settings"`); + await queryRunner.query( + `ALTER TABLE "item_bundlings" ADD CONSTRAINT "FK_a50e7abf2caba4d0394f3726b86" FOREIGN KEY ("item_bundling_id") REFERENCES "items"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "queue_tickets" ADD CONSTRAINT "FK_0e9823b8b7ca9523b3be73878e5" FOREIGN KEY ("order_id") REFERENCES "queue_orders"("id") ON DELETE SET NULL ON UPDATE CASCADE`, + ); + } +} From 89e474758699291cc00f28ceee513b7f969eb87a Mon Sep 17 00:00:00 2001 From: shancheas Date: Tue, 8 Jul 2025 10:02:44 +0700 Subject: [PATCH 12/28] feat: add SKIP_TRANSACTION_FEATURE to environment files and remove obsolete migration --- env/env.development | 4 +++- env/env.production | 4 +++- .../1751353749678-add-key-colum-to-setting.ts | 17 ----------------- .../1751942902581-renam-table-config.ts | 3 +++ 4 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 src/database/migrations/1751353749678-add-key-colum-to-setting.ts diff --git a/env/env.development b/env/env.development index 2941384..ac7e894 100644 --- a/env/env.development +++ b/env/env.development @@ -48,4 +48,6 @@ SUPERSET_ADMIN_PASSWORD=admin WHATSAPP_BUSINESS_ACCOUNT_NUMBER_ID=604883366037548 WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV -SETUP_SCHEDULING_KEY=scheduling_key_example \ No newline at end of file +SETUP_SCHEDULING_KEY=scheduling_key_example + +SKIP_TRANSACTION_FEATURE=false \ No newline at end of file diff --git a/env/env.production b/env/env.production index 1d16625..05de872 100644 --- a/env/env.production +++ b/env/env.production @@ -45,4 +45,6 @@ SUPERSET_ADMIN_PASSWORD=admin WHATSAPP_BUSINESS_ACCOUNT_NUMBER_ID=604883366037548 WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV -SETUP_SCHEDULING_KEY=scheduling_key_example \ No newline at end of file +SETUP_SCHEDULING_KEY=scheduling_key_example + +SKIP_TRANSACTION_FEATURE=false \ No newline at end of file diff --git a/src/database/migrations/1751353749678-add-key-colum-to-setting.ts b/src/database/migrations/1751353749678-add-key-colum-to-setting.ts deleted file mode 100644 index acf5b72..0000000 --- a/src/database/migrations/1751353749678-add-key-colum-to-setting.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddKeyColumToSetting1751353749678 implements MigrationInterface { - name = 'AddKeyColumToSetting1751353749678'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "transaction_settings" ADD "key" character varying`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "transaction_settings" DROP COLUMN "key"`, - ); - } -} diff --git a/src/database/migrations/1751942902581-renam-table-config.ts b/src/database/migrations/1751942902581-renam-table-config.ts index 3b6ee5a..7cd4dfa 100644 --- a/src/database/migrations/1751942902581-renam-table-config.ts +++ b/src/database/migrations/1751942902581-renam-table-config.ts @@ -16,6 +16,9 @@ export class RenamTableConfig1751942902581 implements MigrationInterface { await queryRunner.query( `CREATE TABLE "event_scheduling_default" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "default_value" integer NOT NULL, CONSTRAINT "PK_9caf65330e76243e9f9285ae2e1" PRIMARY KEY ("id"))`, ); + await queryRunner.query( + `ALTER TABLE "api_settings" ADD "key" character varying`, + ); } public async down(queryRunner: QueryRunner): Promise { From 6d855eabe0931c10691057c9c27779644efe1a4e Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Tue, 8 Jul 2025 10:52:42 +0700 Subject: [PATCH 13/28] feat: rename table scheduling and setting --- .../configuration/couch/couch.module.ts | 9 +++ .../managers/data-scheduling.handler.ts | 64 +++++++++++++++++++ .../data-scheduling-default.manager.ts | 37 ++--------- 3 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts diff --git a/src/modules/configuration/couch/couch.module.ts b/src/modules/configuration/couch/couch.module.ts index 060254c..2698071 100644 --- a/src/modules/configuration/couch/couch.module.ts +++ b/src/modules/configuration/couch/couch.module.ts @@ -57,6 +57,10 @@ import { TimeGroupUpdatedHandler, } from './domain/managers/time-group.handle'; +import { DataSchedulingUpdatedHandler } from './domain/managers/data-scheduling.handler'; +import { DataSchedulingDefaultModel } from '../data-scheduling/data/models/data-scheduling-default.model'; +import { DataSchedulingModel } from '../data-scheduling/data/models/data-scheduling.model'; + @Module({ imports: [ ConfigModule.forRoot(), @@ -71,6 +75,9 @@ import { TransactionTaxModel, TransactionItemModel, TransactionDemographyModel, + + DataSchedulingDefaultModel, + DataSchedulingModel, ], CONNECTION_NAME.DEFAULT, ), @@ -104,6 +111,8 @@ import { SeasonTypeDeletedHandler, SeasonTypeUpdatedHandler, + DataSchedulingUpdatedHandler, + SeasonPeriodDataService, TransactionDataService, UserDataService, diff --git a/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts b/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts new file mode 100644 index 0000000..ae74eaa --- /dev/null +++ b/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts @@ -0,0 +1,64 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { CouchService } from '../../data/services/couch.service'; +import { DataSchedulingDeletedEvent } from 'src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-deleted.event'; +import { DataSchedulingChangeStatusEvent } from 'src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-change-status.event'; +import { DataSchedulingUpdatedEvent } from 'src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-updated.event'; +import { DataSchedulingCreatedEvent } from 'src/modules/configuration/data-scheduling/domain/entities/event/data-scheduling-created.event'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingDefaultModel } from 'src/modules/configuration/data-scheduling/data/models/data-scheduling-default.model'; +import { DataSchedulingModel } from 'src/modules/configuration/data-scheduling/data/models/data-scheduling.model'; +import { Repository } from 'typeorm'; + +import * as momentTz from 'moment-timezone'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { DataSchedulingEntity } from 'src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity'; +import { decryptionTotal } from 'src/modules/configuration/data-scheduling/infrastructure/helpers'; + +@EventsHandler( + DataSchedulingCreatedEvent, + DataSchedulingUpdatedEvent, + DataSchedulingChangeStatusEvent, + DataSchedulingDeletedEvent, +) +export class DataSchedulingUpdatedHandler implements IEventHandler { + constructor( + private couchService: CouchService, + + @InjectRepository(DataSchedulingDefaultModel) + private repository: Repository, + + @InjectRepository(DataSchedulingModel) + private repoSchedule: Repository, + ) {} + + async handle() { + const activeData = await this.getActiveData(); + console.log( + activeData, + 'handle when data scheduling status change and data updated', + ); + } + + async getActiveData() { + const timeZoneWIB = 'Asia/Jakarta'; + const nowInWIB = momentTz().tz(timeZoneWIB).format('YYYY-MM-DD'); + const date = nowInWIB; + + const qb = this.repoSchedule.createQueryBuilder(TABLE_NAME.DATA_SCHEDULING); + + const findData: DataSchedulingEntity = await qb + .where('status = :status', { status: 'active' }) + .andWhere('schedule_date_from <= :date', { date: date }) + .andWhere('schedule_date_to >= :date', { date: date }) + .getOne(); + + if (!findData) { + const defaultData = await this.repository + .createQueryBuilder(TABLE_NAME.DATA_SCHEDULING_DEFAULT) + .getOne(); + return { value: defaultData?.default_value }; + } + + return { value: decryptionTotal(findData.indexing_key as string), date }; + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts index 040bc97..2766147 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -2,18 +2,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserProvider, UsersSession } from 'src/core/sessions'; import { BLANK_USER } from 'src/core/strings/constants/base.constants'; import { EditDataSchedulingDefaultDto } from '../../../infrastructure/dto/data-scheduling.dto'; -import { - DataSchedulingDefaultEntity, - DataSchedulingEntity, -} from '../../entities/data-scheduling.entity'; +import { DataSchedulingDefaultEntity } from '../../entities/data-scheduling.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { DataSchedulingDefaultModel } from '../../../data/models/data-scheduling-default.model'; import { Repository } from 'typeorm'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { SelectQueryBuilder } from 'typeorm'; import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; -import { decryptionTotal } from '../../../infrastructure/helpers'; -import * as momentTz from 'moment-timezone'; import { EventBus } from '@nestjs/cqrs'; import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; @@ -70,6 +65,7 @@ export class DataSchedulingManager { updated_at: dateNow, }; + await this.publishEventUpdates(); return this.repository.save(payload); } @@ -77,33 +73,14 @@ export class DataSchedulingManager { return this.queryBuilder().getOne(); } - async getActiveData() { - const timeZoneWIB = 'Asia/Jakarta'; - const nowInWIB = momentTz().tz(timeZoneWIB).format('YYYY-MM-DD'); - const date = nowInWIB; - - const qb = this.repoSchedule.createQueryBuilder(TABLE_NAME.DATA_SCHEDULING); - - const findData: DataSchedulingEntity = await qb - .where('status = :status', { status: 'active' }) - .andWhere('schedule_date_from <= :date', { date: date }) - .andWhere('schedule_date_to >= :date', { date: date }) - .getOne(); - - if (!findData) { - const defaultData = await this.queryBuilder().getOne(); - return { value: defaultData?.default_value }; - } - - return { value: decryptionTotal(findData.indexing_key as string), date }; + async publishEventUpdates() { + await this.eventBus.publish( + new DataSchedulingChangeStatusEvent({ data: null } as any), + ); } async setupActiveScheduling() { - const activeSchedule = await this.getActiveData(); - await this.eventBus.publish( - new DataSchedulingChangeStatusEvent({ data: activeSchedule } as any), - ); - + await this.publishEventUpdates(); return { message: 'Success setup transaction schedule.' }; } } From f79db3343ddc798b7d52951708efc08225543cc0 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:12:30 +0700 Subject: [PATCH 14/28] feat: integration save to couch --- src/modules/configuration/couch/constants.ts | 1 + .../managers/data-scheduling.handler.ts | 32 +++++++++++++++++-- .../data-scheduling-default.manager.ts | 8 ++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/modules/configuration/couch/constants.ts b/src/modules/configuration/couch/constants.ts index fa8be79..3b8c14a 100644 --- a/src/modules/configuration/couch/constants.ts +++ b/src/modules/configuration/couch/constants.ts @@ -4,4 +4,5 @@ export const DatabaseListen = [ 'pos_activity', 'pos_cash_activity', 'time_groups', + 'api_configuration', ]; diff --git a/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts b/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts index ae74eaa..c61e3b2 100644 --- a/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts +++ b/src/modules/configuration/couch/domain/managers/data-scheduling.handler.ts @@ -13,6 +13,7 @@ import * as momentTz from 'moment-timezone'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { DataSchedulingEntity } from 'src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity'; import { decryptionTotal } from 'src/modules/configuration/data-scheduling/infrastructure/helpers'; +import { Logger } from '@nestjs/common'; @EventsHandler( DataSchedulingCreatedEvent, @@ -21,6 +22,10 @@ import { decryptionTotal } from 'src/modules/configuration/data-scheduling/infra DataSchedulingDeletedEvent, ) export class DataSchedulingUpdatedHandler implements IEventHandler { + private readonly logger = new Logger(DataSchedulingUpdatedHandler.name); + private readonly permanentID = 'e6166c86-d85d-43f8-86ad-c9e85a88f68f'; + private readonly couchTableName = 'api_configuration'; + constructor( private couchService: CouchService, @@ -33,10 +38,31 @@ export class DataSchedulingUpdatedHandler implements IEventHandler { async handle() { const activeData = await this.getActiveData(); - console.log( - activeData, - 'handle when data scheduling status change and data updated', + const existData = await this.couchService.getDoc( + this.permanentID, + this.couchTableName, ); + if (!existData) { + this.logger.verbose('CREATE SCHEDULING CONFIG'); + await this.couchService.createDoc( + { + _id: this.permanentID, + id: this.permanentID, + ...activeData, + }, + this.couchTableName, + ); + } else if (existData) { + this.logger.verbose('UPDATE SCHEDULING CONFIG'); + await this.couchService.updateDoc( + { + _id: this.permanentID, + id: this.permanentID, + ...activeData, + }, + this.couchTableName, + ); + } } async getActiveData() { diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts index 2766147..5866538 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -8,7 +8,6 @@ import { DataSchedulingDefaultModel } from '../../../data/models/data-scheduling import { Repository } from 'typeorm'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { SelectQueryBuilder } from 'typeorm'; -import { DataSchedulingModel } from '../../../data/models/data-scheduling.model'; import { EventBus } from '@nestjs/cqrs'; import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; @@ -22,9 +21,6 @@ export class DataSchedulingManager { @InjectRepository(DataSchedulingDefaultModel) private repository: Repository, - - @InjectRepository(DataSchedulingModel) - private repoSchedule: Repository, ) {} private getUser(): UsersSession { @@ -64,9 +60,9 @@ export class DataSchedulingManager { created_at: dateNow, updated_at: dateNow, }; - + const saveData = await this.repository.save(payload); await this.publishEventUpdates(); - return this.repository.save(payload); + return saveData; } async getData() { From 0f46f5ff393594c26ce0857ad8f51172bd08402c Mon Sep 17 00:00:00 2001 From: shancheas Date: Tue, 8 Jul 2025 12:01:39 +0700 Subject: [PATCH 15/28] feat: implement encryption helper and transaction setting handler - Added EncryptionHelper for encrypting and decrypting transaction settings. - Introduced TransactionSettingHandler to manage changes in transaction settings. - Updated SalesPriceFormulaModel to use decrypted values. - Enhanced SalesPriceFormulaDataService with methods to get and update transaction settings. - Modified SalesPriceFormulaReadService to return decrypted values in the response. --- .../data/models/sales-price-formula.model.ts | 4 +- .../sales-price-formula-data.service.ts | 11 +++ .../sales-price-formula-read.service.ts | 7 +- .../domain/helpers/encryption.helper.ts | 72 +++++++++++++++++++ .../handlers/transaction-setting.handler.ts | 34 +++++++++ .../sales-price-formula.module.ts | 2 + src/services/whatsapp/whatsapp.service.ts | 3 +- 7 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 src/modules/transaction/sales-price-formula/domain/helpers/encryption.helper.ts create mode 100644 src/modules/transaction/sales-price-formula/domain/usecases/handlers/transaction-setting.handler.ts diff --git a/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts b/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts index 0781b25..587be30 100644 --- a/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts +++ b/src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model.ts @@ -6,6 +6,7 @@ import { import { Column, Entity } from 'typeorm'; import { BaseModel } from 'src/core/modules/data/model/base.model'; import { FormulaType } from '../../constants'; +import { EncryptionHelper } from '../../domain/helpers/encryption.helper'; @Entity(TABLE_NAME.PRICE_FORMULA) export class SalesPriceFormulaModel @@ -48,6 +49,7 @@ export class TransactionSettingModel //TODO: add logic to get value from key percentValue(): number { - return this.value ?? 100; + const value = EncryptionHelper.decrypt(this.key); + return Number(value) ?? 100; } } diff --git a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts index 23e46cf..df1c8fc 100644 --- a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts +++ b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts @@ -158,4 +158,15 @@ export class TransactionSettingDataService extends BaseDataService +{ + constructor( + private transactionSettingService: TransactionSettingDataService, // private orchestrator: SalesPriceFormulaDataOrchestrator, + ) {} + + async handle(event: ChangeDocEvent) { + const data = event.data.data; + const database = event.data.database; + + if (database !== 'api_configuration') return; + const value = data.value; + + const currentTransactionSetting = + await this.transactionSettingService.getTransactionSetting(); + + if (value === currentTransactionSetting.value) return; + const key = EncryptionHelper.encrypt(`${value}`); + const payload = { + id: currentTransactionSetting.id, + value: 0, + key, + }; + + await this.transactionSettingService.updateTransactionSetting(payload); + } +} diff --git a/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts b/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts index 2b3717f..8183161 100644 --- a/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts +++ b/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts @@ -24,6 +24,7 @@ import { ItemModel } from 'src/modules/item-related/item/data/models/item.model' import { UpdateTransactionSettingManager } from './domain/usecases/managers/update-transaction-setting.manager'; import { TransactionModel } from '../transaction/data/models/transaction.model'; import { CouchModule } from 'src/modules/configuration/couch/couch.module'; +import { TransactionSettingHandler } from './domain/usecases/handlers/transaction-setting.handler'; @Global() @Module({ @@ -58,6 +59,7 @@ import { CouchModule } from 'src/modules/configuration/couch/couch.module'; SalesPriceFormulaDataOrchestrator, SalesPriceFormulaReadOrchestrator, + TransactionSettingHandler, ], exports: [SalesPriceFormulaDataService, SalesPriceFormulaReadService], }) diff --git a/src/services/whatsapp/whatsapp.service.ts b/src/services/whatsapp/whatsapp.service.ts index 1f4377f..4360d27 100644 --- a/src/services/whatsapp/whatsapp.service.ts +++ b/src/services/whatsapp/whatsapp.service.ts @@ -97,7 +97,8 @@ export class WhatsappService { { parameter_name: 'queue_time', type: 'text', - text: toTime(data.time), // replace with queue_time variable + // text: toTime(data.time), // replace with queue_time variable + text: '--:--', }, ], }, From f76bbcf19dcd7919a4e2c0b8982d804d06e17847 Mon Sep 17 00:00:00 2001 From: shancheas Date: Tue, 8 Jul 2025 14:12:02 +0700 Subject: [PATCH 16/28] feat: add phone number sanitization helper and integrate into queue manager - Introduced sanitizePhoneNumber function to clean and format phone numbers. - Updated GenerateQueueManager to use sanitizePhoneNumber for customer phone input. --- .../queue/domain/helpers/time.helper.ts | 23 +++++++++++++++++++ .../domain/usecases/generate-queue.manager.ts | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/modules/queue/domain/helpers/time.helper.ts b/src/modules/queue/domain/helpers/time.helper.ts index eaa09d9..1eeecba 100644 --- a/src/modules/queue/domain/helpers/time.helper.ts +++ b/src/modules/queue/domain/helpers/time.helper.ts @@ -14,6 +14,29 @@ export function phoneNumberOnly(phone: string): string { return phone.replace(/[^0-9]/g, ''); } +export function sanitizePhoneNumber(phone: string): string { + // Remove all non-numeric characters first + const cleanPhone = phone.replace(/[^0-9]/g, ''); + + // If the number starts with '0', replace it with '+62' + if (cleanPhone.startsWith('0')) { + return '+62' + cleanPhone.slice(1); + } + + // If the number starts with '62', add '+' prefix + if (cleanPhone.startsWith('62')) { + return '+' + cleanPhone; + } + + // If the number doesn't start with '62' and is not empty, assume it's a local number and add '+62' + if (cleanPhone.length > 0) { + return '+62' + cleanPhone; + } + + // Return original if empty or invalid + return phone; +} + export function timeIsBefore( currentTime: number, time: number, diff --git a/src/modules/queue/domain/usecases/generate-queue.manager.ts b/src/modules/queue/domain/usecases/generate-queue.manager.ts index 7729f5c..5e447e2 100644 --- a/src/modules/queue/domain/usecases/generate-queue.manager.ts +++ b/src/modules/queue/domain/usecases/generate-queue.manager.ts @@ -14,6 +14,7 @@ import { QueueTimeFormula } from './formula/queue-time.formula'; import { RegisterQueueManager } from './register-queue.manager'; import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; import { QueueModel } from '../../data/models/queue.model'; +import { sanitizePhoneNumber } from '../helpers/time.helper'; @Injectable() export class GenerateQueueManager { @@ -34,7 +35,7 @@ export class GenerateQueueManager { const customerOrder = { code: invoice_code, customer: customer_name, - phone: customer_phone, + phone: sanitizePhoneNumber(customer_phone), date: queue_date * 1000, transaction_id: id, }; From 0b5aeff89dba3d292c4357c6facaecd841c8e9c5 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 10 Jul 2025 12:51:16 +0700 Subject: [PATCH 17/28] feat: change encryption helper --- .../data-scheduling/infrastructure/helpers/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts b/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts index 9a0049c..abc8f47 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/helpers/index.ts @@ -1,7 +1,9 @@ +import { EncryptionHelper } from 'src/modules/transaction/sales-price-formula/domain/helpers/encryption.helper'; + export function encryptionTotal(total: number): string { - return btoa(total.toString()); + return EncryptionHelper.encrypt(btoa(total.toString())); } export function decryptionTotal(total: string): number { - return Number(atob(total)); + return Number(atob(EncryptionHelper.decrypt(total))); } From 7967df9f1f8d1d5e6afc46bf9d7cd87ff114a188 Mon Sep 17 00:00:00 2001 From: shancheas Date: Thu, 10 Jul 2025 17:39:20 +0700 Subject: [PATCH 18/28] feat: enhance transaction filtering by adding status criteria --- .../configuration/couch/data/services/couch.service.ts | 1 + .../data/services/sales-price-formula-data.service.ts | 6 +++++- .../domain/usecases/handlers/pos-transaction.handler.ts | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/configuration/couch/data/services/couch.service.ts b/src/modules/configuration/couch/data/services/couch.service.ts index 2efb2db..985c9f4 100644 --- a/src/modules/configuration/couch/data/services/couch.service.ts +++ b/src/modules/configuration/couch/data/services/couch.service.ts @@ -111,6 +111,7 @@ export class CouchService { const selector = { created_at: { $gte: todayTimestamp, + status: 'settled', }, }; diff --git a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts index df1c8fc..a4f9c3f 100644 --- a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts +++ b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts @@ -13,7 +13,10 @@ import { SalesPriceFormulaModel, TransactionSettingModel, } from '../models/sales-price-formula.model'; -import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { + CONNECTION_NAME, + STATUS, +} from 'src/core/strings/constants/base.constants'; import { Repository } from 'typeorm'; import { FormulaType } from '../../constants'; import { TaxModel } from 'src/modules/transaction/tax/data/models/tax.model'; @@ -75,6 +78,7 @@ export class SalesPriceFormulaDataService extends BaseDataService :timestamp', { timestamp: todayTimestamp, }) + .andWhere('transaction.status = :status', { status: STATUS.SETTLED }) .andWhere('transaction.type = :type', { type: TransactionType.COUNTER }) .getRawOne() .then((result) => result.sum || 0), diff --git a/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts b/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts index 41e5128..1ae9744 100644 --- a/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts +++ b/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts @@ -19,6 +19,7 @@ import { TransactionCreateQueueEvent, } from '../../entities/event/transaction-change-status.event'; import { PriceCalculator } from '../calculator/price.calculator'; +import { Logger } from '@nestjs/common'; @EventsHandler(ChangeDocEvent) export class PosTransactionHandler implements IEventHandler { @@ -41,6 +42,7 @@ export class PosTransactionHandler implements IEventHandler { try { const database = event.data.database; const data = { ...event.data.data }; + Logger.log(`receipt data with code ${data?.code}`); // jika bukan database transaksi, return langsung if (database != 'transaction') return; From d8a7488f46aa9bc9f6be00fa4ec48b9a2842150d Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 10 Jul 2025 18:32:42 +0700 Subject: [PATCH 19/28] feat: create feature log scheduling activity --- src/core/strings/constants/table.constants.ts | 1 + .../1752146975330-data-scheduling-log.ts | 33 ++++ .../data-scheduling/data-scheduling.module.ts | 13 +- .../data/models/data-scheduling-log.model.ts | 44 +++++ .../domain/entities/data-scheduling.entity.ts | 30 ++++ .../data-scheduling-change-status.handler.ts | 62 +++++++ .../data-scheduling-created.handler.ts | 58 +++++++ .../data-scheduling-deleted.handler.ts | 64 ++++++++ .../data-scheduling-updated.handler.ts | 153 ++++++++++++++++++ 9 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 src/database/migrations/1752146975330-data-scheduling-log.ts create mode 100644 src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index a2a5475..e8990d3 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -52,4 +52,5 @@ export enum TABLE_NAME { DATA_SCHEDULING = 'event_scheduling', DATA_SCHEDULING_DEFAULT = 'event_scheduling_default', + DATA_SCHEDULING_LOG = 'event_scheduling_log', } diff --git a/src/database/migrations/1752146975330-data-scheduling-log.ts b/src/database/migrations/1752146975330-data-scheduling-log.ts new file mode 100644 index 0000000..619f1f0 --- /dev/null +++ b/src/database/migrations/1752146975330-data-scheduling-log.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class DataSchedulingLog1752146975330 implements MigrationInterface { + name = 'DataSchedulingLog1752146975330'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."event_scheduling_log_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`, + ); + await queryRunner.query( + `CREATE TYPE "public"."event_scheduling_log_type_enum" AS ENUM('Default Percentage', 'Data Scheduling')`, + ); + await queryRunner.query( + `CREATE TYPE "public"."event_scheduling_log_action_enum" AS ENUM('CREATE', 'UPDATE', 'DELETE', 'CHANGE_STATUS')`, + ); + await queryRunner.query( + `CREATE TABLE "event_scheduling_log" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."event_scheduling_log_status_enum" NOT NULL DEFAULT 'draft', "type" "public"."event_scheduling_log_type_enum" NOT NULL, "action" "public"."event_scheduling_log_action_enum" NOT NULL, "log_created_at" bigint NOT NULL, "data_id" character varying, "name" character varying, "indexing_key" character varying, "schedule_date_from" date, "schedule_date_to" date, "default_value" integer, "description" text, CONSTRAINT "PK_984247db566636baacab18f593a" PRIMARY KEY ("id"))`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "event_scheduling_log"`); + await queryRunner.query( + `DROP TYPE "public"."event_scheduling_log_action_enum"`, + ); + await queryRunner.query( + `DROP TYPE "public"."event_scheduling_log_type_enum"`, + ); + await queryRunner.query( + `DROP TYPE "public"."event_scheduling_log_status_enum"`, + ); + } +} diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts index 3912b44..ef409bd 100644 --- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -30,15 +30,21 @@ import { DataSchedulingDefaultModel } from './data/models/data-scheduling-defaul import { DataSchedulingManager } from './domain/usecases/managers/data-scheduling-default.manager'; import { SetupSchedulingGuard } from './infrastructure/guards/setup-scheduling.guard'; +import { DataSchedulingChangeStatusHandler } from './domain/usecases/handlers/data-scheduling-change-status.handler'; +import { DataSchedulingCreatedHandler } from './domain/usecases/handlers/data-scheduling-created.handler'; +import { DataSchedulingDeletedHandler } from './domain/usecases/handlers/data-scheduling-deleted.handler'; +import { DataSchedulingUpdatedHandler } from './domain/usecases/handlers/data-scheduling-updated.handler'; + import { JwtModule } from '@nestjs/jwt'; import { JWT_EXPIRED } from 'src/core/sessions/constants'; import { JWT_SECRET } from 'src/core/sessions/constants'; +import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model'; @Module({ imports: [ ConfigModule.forRoot(), TypeOrmModule.forFeature( - [DataSchedulingModel, DataSchedulingDefaultModel], + [DataSchedulingModel, DataSchedulingDefaultModel, DataSchedulingLogModel], CONNECTION_NAME.DEFAULT, ), JwtModule.register({ @@ -75,6 +81,11 @@ import { JWT_SECRET } from 'src/core/sessions/constants'; DataSchedulingReadOrchestrator, DataSchedulingManager, + + DataSchedulingChangeStatusHandler, + DataSchedulingCreatedHandler, + DataSchedulingDeletedHandler, + DataSchedulingUpdatedHandler, ], }) export class DataSchedulingModule {} diff --git a/src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts b/src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts new file mode 100644 index 0000000..4950656 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts @@ -0,0 +1,44 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { + DataSchedulingLogEntity, + SCHEDULING_LOG_ACTION_ENUM, + SCHEDULING_LOG_TYPE_ENUM, +} from '../../domain/entities/data-scheduling.entity'; +import { Column, Entity } from 'typeorm'; +import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; + +@Entity(TABLE_NAME.DATA_SCHEDULING_LOG) +export class DataSchedulingLogModel + extends BaseStatusModel + implements DataSchedulingLogEntity +{ + @Column({ type: 'enum', enum: SCHEDULING_LOG_TYPE_ENUM, nullable: false }) + type: SCHEDULING_LOG_TYPE_ENUM; + + @Column({ type: 'enum', enum: SCHEDULING_LOG_ACTION_ENUM, nullable: false }) + action: SCHEDULING_LOG_ACTION_ENUM; + + @Column({ type: 'bigint', nullable: false }) + log_created_at: number; + + @Column('varchar', { name: 'data_id', nullable: true }) + data_id: string; + + @Column('varchar', { name: 'name', nullable: true }) + name: string; + + @Column('varchar', { name: 'indexing_key', nullable: true }) + indexing_key: string; + + @Column('date', { name: 'schedule_date_from', nullable: true }) + schedule_date_from: Date; + + @Column('date', { name: 'schedule_date_to', nullable: true }) + schedule_date_to: Date; + + @Column('int', { nullable: true }) + default_value: number; + + @Column('text', { name: 'description', nullable: true }) + description: string; +} diff --git a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts index 9733866..ea2e92b 100644 --- a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts +++ b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts @@ -1,5 +1,6 @@ import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity'; import { BaseEntity } from 'src/core/modules/domain/entities/base.entity'; +import { STATUS } from 'src/core/strings/constants/base.constants'; export interface DataSchedulingEntity extends BaseStatusEntity { name: string; @@ -15,3 +16,32 @@ export interface DataSchedulingDefaultEntity extends BaseEntity { export interface DataSchedulingActiveEntity { value: number; } + +export enum SCHEDULING_LOG_TYPE_ENUM { + DEFAULT_PERCENTAGE = 'Default Percentage', + DATA_SCHEDULING = 'Data Scheduling', +} + +export enum SCHEDULING_LOG_ACTION_ENUM { + CREATE = 'CREATE', + UPDATE = 'UPDATE', + DELETE = 'DELETE', + CHANGE_STATUS = 'CHANGE_STATUS', +} + +export interface DataSchedulingLogEntity extends BaseStatusEntity { + type: SCHEDULING_LOG_TYPE_ENUM; + action: SCHEDULING_LOG_ACTION_ENUM; + log_created_at: number; + + data_id?: string; + + name?: string; + indexing_key?: number | string; + schedule_date_from?: Date; + schedule_date_to?: Date; + + default_value?: number; + + description?: string; +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts new file mode 100644 index 0000000..d8074e4 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -0,0 +1,62 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; +import { capitalizeEachWord } from 'src/modules/reports/shared/helpers'; +import { + DataSchedulingLogEntity, + SCHEDULING_LOG_ACTION_ENUM, + SCHEDULING_LOG_TYPE_ENUM, +} from '../../entities/data-scheduling.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; +import { Repository } from 'typeorm'; +import { Logger } from '@nestjs/common'; + +@EventsHandler(DataSchedulingChangeStatusEvent) +export class DataSchedulingChangeStatusHandler + implements IEventHandler +{ + private readonly logger = new Logger(DataSchedulingChangeStatusHandler.name); + + constructor( + @InjectRepository(DataSchedulingLogModel) + private repository: Repository, + ) {} + + async handle(event: DataSchedulingChangeStatusEvent) { + const oldData = event?.data?.old; + const newData = event?.data?.data; + + const oldStatus = capitalizeEachWord(oldData?.status); + const newStatus = capitalizeEachWord(newData.status); + + const scheduleName = newData?.name || 'an item'; + const editorName = newData.editor_name || 'System'; + const description = `

${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.

`; + + const payload: DataSchedulingLogEntity = { + type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, + action: SCHEDULING_LOG_ACTION_ENUM.CHANGE_STATUS, + log_created_at: new Date().getTime(), + + data_id: newData?.id, + name: newData?.name, + indexing_key: newData?.indexing_key, + schedule_date_from: newData?.schedule_date_from, + schedule_date_to: newData?.schedule_date_to, + + status: newData?.status, + creator_id: newData?.creator_id, + creator_name: newData?.creator_name, + editor_id: newData?.editor_id, + editor_name: newData?.editor_name, + created_at: newData?.created_at, + updated_at: newData?.updated_at, + description: description, + }; + + await this.repository.save(payload as any); + this.logger.verbose( + `[SCHEDULING LOG] Change status data for ID: ${payload.data_id}`, + ); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts new file mode 100644 index 0000000..c35b8e2 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts @@ -0,0 +1,58 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { DataSchedulingCreatedEvent } from '../../entities/event/data-scheduling-created.event'; +import { + DataSchedulingLogEntity, + SCHEDULING_LOG_ACTION_ENUM, + SCHEDULING_LOG_TYPE_ENUM, +} from '../../entities/data-scheduling.entity'; +import { decryptionTotal } from '../../../infrastructure/helpers'; +import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Logger } from '@nestjs/common'; + +@EventsHandler(DataSchedulingCreatedEvent) +export class DataSchedulingCreatedHandler + implements IEventHandler +{ + private readonly logger = new Logger(DataSchedulingCreatedHandler.name); + + constructor( + @InjectRepository(DataSchedulingLogModel) + private repository: Repository, + ) {} + + async handle(event: DataSchedulingCreatedEvent) { + const data = event?.data?.data; + const totalPercentage = decryptionTotal(data?.indexing_key); + + const scheduleName = data?.name || 'a new schedule'; + const description = `

${data.creator_name} created ${scheduleName} schedule from ${data?.schedule_date_from} to ${data.schedule_date_to} with a total percentage of ${totalPercentage}%.

`; + + const payload: DataSchedulingLogEntity = { + type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, + action: SCHEDULING_LOG_ACTION_ENUM.CREATE, + log_created_at: new Date().getTime(), + + data_id: data?.id, + name: data?.name, + indexing_key: data?.indexing_key, + schedule_date_from: data?.schedule_date_from, + schedule_date_to: data?.schedule_date_to, + + status: data?.status, + creator_id: data?.creator_id, + creator_name: data?.creator_name, + editor_id: data?.editor_id, + editor_name: data?.editor_name, + created_at: data?.created_at, + updated_at: data?.updated_at, + description: description, + }; + + await this.repository.save(payload as any); + this.logger.verbose( + `[SCHEDULING LOG] Create data for ID: ${payload.data_id}`, + ); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts new file mode 100644 index 0000000..8ba8e87 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts @@ -0,0 +1,64 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { DataSchedulingDeletedEvent } from '../../entities/event/data-scheduling-deleted.event'; +import { + DataSchedulingLogEntity, + SCHEDULING_LOG_ACTION_ENUM, + SCHEDULING_LOG_TYPE_ENUM, +} from '../../entities/data-scheduling.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; +import { Repository } from 'typeorm'; +import { Logger } from '@nestjs/common'; + +@EventsHandler(DataSchedulingDeletedEvent) +export class DataSchedulingDeletedHandler + implements IEventHandler +{ + private readonly logger = new Logger(DataSchedulingDeletedHandler.name); + + constructor( + @InjectRepository(DataSchedulingLogModel) + private repository: Repository, + ) {} + + async handle(event: DataSchedulingDeletedEvent) { + const deletedData = event?.data?.data; + const user = event?.data?.user; + + const deleterName = + user?.name || + deletedData?.editor_name || + deletedData?.creator_name || + 'System'; + + const scheduleName = deletedData?.name || 'an item'; + + const description = `

${deleterName} deleted schedule: ${scheduleName}.

`; + + const payload: DataSchedulingLogEntity = { + type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, + action: SCHEDULING_LOG_ACTION_ENUM.DELETE, + log_created_at: new Date().getTime(), + + data_id: deletedData?.id, + name: deletedData?.name, + indexing_key: deletedData?.indexing_key, + schedule_date_from: deletedData?.schedule_date_from, + schedule_date_to: deletedData?.schedule_date_to, + status: deletedData?.status, + + creator_id: deletedData?.creator_id, + creator_name: deletedData?.creator_name, + editor_id: deletedData?.editor_id, + editor_name: deletedData?.editor_name, + created_at: deletedData?.created_at, + updated_at: deletedData?.updated_at, + description: description, + }; + + await this.repository.save(payload as any); + this.logger.verbose( + `[SCHEDULING LOG] Delete data for ID: ${payload.data_id}`, + ); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts new file mode 100644 index 0000000..d170e90 --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -0,0 +1,153 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { DataSchedulingUpdatedEvent } from '../../entities/event/data-scheduling-updated.event'; +import { + DataSchedulingLogEntity, + SCHEDULING_LOG_ACTION_ENUM, + SCHEDULING_LOG_TYPE_ENUM, +} from '../../entities/data-scheduling.entity'; +import { + decryptionTotal, + encryptionTotal, +} from '../../../infrastructure/helpers'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; +import { Repository } from 'typeorm'; +import { Logger } from '@nestjs/common'; + +@EventsHandler(DataSchedulingUpdatedEvent) +export class DataSchedulingUpdatedHandler + implements IEventHandler +{ + private readonly logger = new Logger(DataSchedulingUpdatedHandler.name); + + constructor( + @InjectRepository(DataSchedulingLogModel) + private repository: Repository, + ) {} + + // Map for readable labels + private readonly labelMap: { [key: string]: string } = { + name: 'Name', + indexing_key: 'Total Data', + schedule_date_from: 'Start Date', + schedule_date_to: 'End Date', + }; + + // Relevant keys for comparing changes + private readonly keysToCompare: string[] = [ + 'name', + 'indexing_key', + 'schedule_date_from', + 'schedule_date_to', + ]; + + async handle(event: DataSchedulingUpdatedEvent) { + const oldData = event?.data?.old; + // Decrypt oldData.indexing_key here before comparison + if (oldData?.indexing_key !== undefined && oldData?.indexing_key !== null) { + oldData.indexing_key = decryptionTotal(oldData.indexing_key); + } + + const newData = event?.data?.data; + // Decrypt newData.indexing_key here before comparison + if (newData?.indexing_key !== undefined && newData?.indexing_key !== null) { + newData.indexing_key = decryptionTotal(newData.indexing_key); + } + + const changingData = this.getChangingData(oldData, newData); + const description = this.generateDescription( + oldData, + newData, + changingData, + ); + + const payload: DataSchedulingLogEntity = { + type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, + action: SCHEDULING_LOG_ACTION_ENUM.UPDATE, + log_created_at: new Date().getTime(), + + data_id: newData?.id, + name: newData?.name, + indexing_key: encryptionTotal(newData?.indexing_key), + schedule_date_from: newData?.schedule_date_from, + schedule_date_to: newData?.schedule_date_to, + + status: newData?.status, + creator_id: newData?.creator_id, + creator_name: newData?.creator_name, + editor_id: newData?.editor_id, + editor_name: newData?.editor_name, + created_at: newData?.created_at, + updated_at: newData?.updated_at, + description: description, + }; + + await this.repository.save(payload as any); + this.logger.verbose( + `[SCHEDULING LOG] Update data for ID: ${payload.data_id}`, + ); + } + + /** + * Compares old and new data to find changes. + * @param oldData Data before the change. + * @param newData Data after the change. + * @returns An object containing the old and new changed data. + */ + private getChangingData(oldData: any, newData: any): { old: any; new: any } { + const changingData: { old: any; new: any } = { old: {}, new: {} }; + + this.keysToCompare.forEach((key) => { + // Ensure comparisons are made on decrypted values if decryption happens before this + if (oldData?.[key] !== newData?.[key]) { + changingData.old[key] = oldData?.[key]; + changingData.new[key] = newData?.[key]; + } + }); + + return changingData; + } + + /** + * Generates an HTML description string based on data changes. + * Includes the name from oldData for identification. + * @param oldData Old data, used to get the name of the item. + * @param newData New data containing editor information. + * @param changingData An object containing the changed data. + * @returns The HTML string of the change description. + */ + private generateDescription( + oldData: any, + newData: any, + changingData: { old: any; new: any }, + ): string { + const editorName = newData.editor_name || 'System'; + const itemName = oldData?.name || 'an item'; + + let description = `

${editorName} has updated schedule for ${itemName}.`; + + if (Object.keys(changingData.old).length > 0) { + description += ` Change details:

    `; + for (const key in changingData.old) { + if (Object.prototype.hasOwnProperty.call(changingData.old, key)) { + const label = this.labelMap[key] || key; + let oldValue = changingData.old[key] || 'empty'; + let newValue = changingData.new[key] || 'empty'; + + // Add '%' suffix if the key is 'indexing_key' + if (key === 'indexing_key') { + oldValue = `${oldValue}%`; + newValue = `${newValue}%`; + } + + description += `
  • ${label} changed from ${oldValue} to ${newValue}.
  • `; + } + } + description += `

`; + } else { + description += ` No significant data detail changes.

`; + } + + return description; + } +} From 41f691f02ea6300ec8a178bcdd38a93823c988d3 Mon Sep 17 00:00:00 2001 From: shancheas Date: Thu, 10 Jul 2025 18:36:09 +0700 Subject: [PATCH 20/28] feat: improve CouchService change handling and enhance async processing --- .../couch/data/services/couch.service.ts | 22 ++++++++++--------- .../handlers/pos-transaction.handler.ts | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/modules/configuration/couch/data/services/couch.service.ts b/src/modules/configuration/couch/data/services/couch.service.ts index 985c9f4..97c5991 100644 --- a/src/modules/configuration/couch/data/services/couch.service.ts +++ b/src/modules/configuration/couch/data/services/couch.service.ts @@ -23,21 +23,23 @@ export class CouchService { const nano = this.nanoInstance; for (const database of DatabaseListen) { const db = nano.db.use(database); - db.changesReader.start({ includeDocs: true }).on('change', (change) => { - Logger.verbose( - `Receive Data from ${database}: ${change?.id}`, - 'CouchService', - ); - this.changeDoc(change, database); - }); + db.changesReader + .start({ includeDocs: true }) + .on('change', async (change) => { + Logger.verbose( + `Receive Data from ${database}: ${change?.id}`, + 'CouchService', + ); + await this.changeDoc(change, database); + }); // transaction Logger.log(`start listen database ${database}`, 'CouchService'); } } - private changeDoc(data, database) { - this.eventBus.publish( + private async changeDoc(data, database) { + await this.eventBus.publish( new ChangeDocEvent({ id: data.id, database: database, @@ -111,8 +113,8 @@ export class CouchService { const selector = { created_at: { $gte: todayTimestamp, - status: 'settled', }, + status: 'settled', }; const result = await db.find({ diff --git a/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts b/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts index 1ae9744..ec52e38 100644 --- a/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts +++ b/src/modules/transaction/transaction/domain/usecases/handlers/pos-transaction.handler.ts @@ -42,10 +42,10 @@ export class PosTransactionHandler implements IEventHandler { try { const database = event.data.database; const data = { ...event.data.data }; - Logger.log(`receipt data with code ${data?.code}`); // jika bukan database transaksi, return langsung if (database != 'transaction') return; + Logger.log(`receipt data with code ${data?.code}`); const sales_formula = await this.formulaService.getOneByOptions({ where: { From 4120e7fc1acfebeb20cfad1984931981d7ed8296 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 10 Jul 2025 18:55:53 +0700 Subject: [PATCH 21/28] feat: integration save scheduling data log to database --- src/app.module.ts | 2 ++ .../data-scheduling/data-scheduling.module.ts | 3 +++ .../services/data-scheduling-log.service.ts | 17 +++++++++++++++++ .../data-scheduling-change-status.handler.ts | 11 +++-------- .../handlers/data-scheduling-created.handler.ts | 11 +++-------- .../handlers/data-scheduling-deleted.handler.ts | 11 +++-------- .../handlers/data-scheduling-updated.handler.ts | 11 +++-------- 7 files changed, 34 insertions(+), 32 deletions(-) create mode 100644 src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 631989f..0af318e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -110,6 +110,7 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard'; import { DataSchedulingModel } from './modules/configuration/data-scheduling/data/models/data-scheduling.model'; import { DataSchedulingModule } from './modules/configuration/data-scheduling/data-scheduling.module'; import { DataSchedulingDefaultModel } from './modules/configuration/data-scheduling/data/models/data-scheduling-default.model'; +import { DataSchedulingLogModel } from './modules/configuration/data-scheduling/data/models/data-scheduling-log.model'; @Module({ imports: [ @@ -183,6 +184,7 @@ import { DataSchedulingDefaultModel } from './modules/configuration/data-schedul // Data Scheduling DataSchedulingModel, DataSchedulingDefaultModel, + DataSchedulingLogModel, ], synchronize: false, }), diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts index ef409bd..a47c394 100644 --- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -38,6 +38,8 @@ import { DataSchedulingUpdatedHandler } from './domain/usecases/handlers/data-sc import { JwtModule } from '@nestjs/jwt'; import { JWT_EXPIRED } from 'src/core/sessions/constants'; import { JWT_SECRET } from 'src/core/sessions/constants'; + +import { DataSchedulingLogService } from './data/services/data-scheduling-log.service'; import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model'; @Module({ @@ -74,6 +76,7 @@ import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model' BatchConfirmDataSchedulingManager, BatchInactiveDataSchedulingManager, + DataSchedulingLogService, DataSchedulingDataService, DataSchedulingReadService, diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts new file mode 100644 index 0000000..b714c40 --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { DataSchedulingLogModel } from '../models/data-scheduling-log.model'; +import { Repository } from 'typeorm'; + +@Injectable() +export class DataSchedulingLogService { + constructor( + @InjectRepository(DataSchedulingLogModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) {} + + async create(entity: any): Promise { + return await this.repo.save(entity); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts index d8074e4..b7c95dd 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -6,10 +6,8 @@ import { SCHEDULING_LOG_ACTION_ENUM, SCHEDULING_LOG_TYPE_ENUM, } from '../../entities/data-scheduling.entity'; -import { InjectRepository } from '@nestjs/typeorm'; -import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; -import { Repository } from 'typeorm'; import { Logger } from '@nestjs/common'; +import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; @EventsHandler(DataSchedulingChangeStatusEvent) export class DataSchedulingChangeStatusHandler @@ -17,10 +15,7 @@ export class DataSchedulingChangeStatusHandler { private readonly logger = new Logger(DataSchedulingChangeStatusHandler.name); - constructor( - @InjectRepository(DataSchedulingLogModel) - private repository: Repository, - ) {} + constructor(private service: DataSchedulingLogService) {} async handle(event: DataSchedulingChangeStatusEvent) { const oldData = event?.data?.old; @@ -54,7 +49,7 @@ export class DataSchedulingChangeStatusHandler description: description, }; - await this.repository.save(payload as any); + await this.service.create(payload as any); this.logger.verbose( `[SCHEDULING LOG] Change status data for ID: ${payload.data_id}`, ); diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts index c35b8e2..b1c6c1d 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts @@ -6,10 +6,8 @@ import { SCHEDULING_LOG_TYPE_ENUM, } from '../../entities/data-scheduling.entity'; import { decryptionTotal } from '../../../infrastructure/helpers'; -import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; import { Logger } from '@nestjs/common'; +import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; @EventsHandler(DataSchedulingCreatedEvent) export class DataSchedulingCreatedHandler @@ -17,10 +15,7 @@ export class DataSchedulingCreatedHandler { private readonly logger = new Logger(DataSchedulingCreatedHandler.name); - constructor( - @InjectRepository(DataSchedulingLogModel) - private repository: Repository, - ) {} + constructor(private service: DataSchedulingLogService) {} async handle(event: DataSchedulingCreatedEvent) { const data = event?.data?.data; @@ -50,7 +45,7 @@ export class DataSchedulingCreatedHandler description: description, }; - await this.repository.save(payload as any); + await this.service.create(payload as any); this.logger.verbose( `[SCHEDULING LOG] Create data for ID: ${payload.data_id}`, ); diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts index 8ba8e87..d9a13b7 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts @@ -5,10 +5,8 @@ import { SCHEDULING_LOG_ACTION_ENUM, SCHEDULING_LOG_TYPE_ENUM, } from '../../entities/data-scheduling.entity'; -import { InjectRepository } from '@nestjs/typeorm'; -import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; -import { Repository } from 'typeorm'; import { Logger } from '@nestjs/common'; +import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; @EventsHandler(DataSchedulingDeletedEvent) export class DataSchedulingDeletedHandler @@ -16,10 +14,7 @@ export class DataSchedulingDeletedHandler { private readonly logger = new Logger(DataSchedulingDeletedHandler.name); - constructor( - @InjectRepository(DataSchedulingLogModel) - private repository: Repository, - ) {} + constructor(private service: DataSchedulingLogService) {} async handle(event: DataSchedulingDeletedEvent) { const deletedData = event?.data?.data; @@ -56,7 +51,7 @@ export class DataSchedulingDeletedHandler description: description, }; - await this.repository.save(payload as any); + await this.service.create(payload as any); this.logger.verbose( `[SCHEDULING LOG] Delete data for ID: ${payload.data_id}`, ); diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts index d170e90..2d45674 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -9,10 +9,8 @@ import { decryptionTotal, encryptionTotal, } from '../../../infrastructure/helpers'; -import { InjectRepository } from '@nestjs/typeorm'; -import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; -import { Repository } from 'typeorm'; import { Logger } from '@nestjs/common'; +import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; @EventsHandler(DataSchedulingUpdatedEvent) export class DataSchedulingUpdatedHandler @@ -20,10 +18,7 @@ export class DataSchedulingUpdatedHandler { private readonly logger = new Logger(DataSchedulingUpdatedHandler.name); - constructor( - @InjectRepository(DataSchedulingLogModel) - private repository: Repository, - ) {} + constructor(private service: DataSchedulingLogService) {} // Map for readable labels private readonly labelMap: { [key: string]: string } = { @@ -82,7 +77,7 @@ export class DataSchedulingUpdatedHandler description: description, }; - await this.repository.save(payload as any); + await this.service.create(payload as any); this.logger.verbose( `[SCHEDULING LOG] Update data for ID: ${payload.data_id}`, ); From 7dadd3f8f11f8e3a6355465e239cac2ffc0b6446 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 10 Jul 2025 19:30:38 +0700 Subject: [PATCH 22/28] feat: add read index scheduling log --- .../strings/constants/module.constants.ts | 1 + .../data-scheduling/data-scheduling.module.ts | 14 ++- ...ts => data-scheduling-log-data.service.ts} | 2 +- .../data-scheduling-log-read.service.ts | 17 ++++ .../data-scheduling-read.orchestrator.ts | 19 +++- .../data-scheduling-change-status.handler.ts | 4 +- .../data-scheduling-created.handler.ts | 4 +- .../data-scheduling-deleted.handler.ts | 4 +- .../data-scheduling-updated.handler.ts | 4 +- .../index-data-scheduling-log.manager.ts | 95 +++++++++++++++++++ .../data-scheduling-read.controller.ts | 24 ++++- 11 files changed, 173 insertions(+), 15 deletions(-) rename src/modules/configuration/data-scheduling/data/services/{data-scheduling-log.service.ts => data-scheduling-log-data.service.ts} (92%) create mode 100644 src/modules/configuration/data-scheduling/data/services/data-scheduling-log-read.service.ts create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index 912eae8..64fedc4 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -34,6 +34,7 @@ export enum MODULE_NAME { OTP_VERIFIER = 'otp-verifier', DATA_SCHEDULING = 'data-scheduling', + DATA_SCHEDULING_LOG = 'data-scheduling-log', DATA_SCHEDULING_DEFAULT = 'data-scheduling-default', DATA_SCHEDULING_ACTIVE = 'data-scheduling-active', DATA_SCHEDULING_SETUP = 'data-scheduling-setup', diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts index a47c394..4c39a05 100644 --- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -4,7 +4,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; import { DataSchedulingDataService } from './data/services/data-scheduling-data.service'; import { DataSchedulingReadService } from './data/services/data-scheduling-read.service'; -import { DataSchedulingReadController } from './infrastructure/data-scheduling-read.controller'; +import { + DataSchedulingLogReadController, + DataSchedulingReadController, +} from './infrastructure/data-scheduling-read.controller'; import { DataSchedulingReadOrchestrator } from './domain/usecases/data-scheduling-read.orchestrator'; import { DataSchedulingDataController, @@ -39,8 +42,10 @@ import { JwtModule } from '@nestjs/jwt'; import { JWT_EXPIRED } from 'src/core/sessions/constants'; import { JWT_SECRET } from 'src/core/sessions/constants'; -import { DataSchedulingLogService } from './data/services/data-scheduling-log.service'; +import { DataSchedulingLogDataService } from './data/services/data-scheduling-log-data.service'; import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model'; +import { DataSchedulingLogReadService } from './data/services/data-scheduling-log-read.service'; +import { IndexDataSchedulingLogManager } from './domain/usecases/managers/index-data-scheduling-log.manager'; @Module({ imports: [ @@ -60,6 +65,7 @@ import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model' DataSchedulingReadController, DataSchedulingDefaultController, DataSchedulingSetupController, + DataSchedulingLogReadController, ], providers: [ SetupSchedulingGuard, @@ -76,7 +82,8 @@ import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model' BatchConfirmDataSchedulingManager, BatchInactiveDataSchedulingManager, - DataSchedulingLogService, + DataSchedulingLogDataService, + DataSchedulingLogReadService, DataSchedulingDataService, DataSchedulingReadService, @@ -84,6 +91,7 @@ import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model' DataSchedulingReadOrchestrator, DataSchedulingManager, + IndexDataSchedulingLogManager, DataSchedulingChangeStatusHandler, DataSchedulingCreatedHandler, diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts similarity index 92% rename from src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts rename to src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts index b714c40..170e091 100644 --- a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts +++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts @@ -5,7 +5,7 @@ import { DataSchedulingLogModel } from '../models/data-scheduling-log.model'; import { Repository } from 'typeorm'; @Injectable() -export class DataSchedulingLogService { +export class DataSchedulingLogDataService { constructor( @InjectRepository(DataSchedulingLogModel, CONNECTION_NAME.DEFAULT) private repo: Repository, diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-read.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-read.service.ts new file mode 100644 index 0000000..21943ab --- /dev/null +++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-read.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { DataSchedulingLogEntity } from '../../domain/entities/data-scheduling.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { BaseReadService } from 'src/core/modules/data/service/base-read.service'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; +import { DataSchedulingLogModel } from '../models/data-scheduling-log.model'; + +@Injectable() +export class DataSchedulingLogReadService extends BaseReadService { + constructor( + @InjectRepository(DataSchedulingLogModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts b/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts index b27759c..1f4da92 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/data-scheduling-read.orchestrator.ts @@ -1,18 +1,25 @@ import { Injectable } from '@nestjs/common'; import { IndexDataSchedulingManager } from './managers/index-data-scheduling.manager'; import { DataSchedulingReadService } from '../../data/services/data-scheduling-read.service'; -import { DataSchedulingEntity } from '../entities/data-scheduling.entity'; +import { + DataSchedulingEntity, + DataSchedulingLogEntity, +} from '../entities/data-scheduling.entity'; import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator'; import { DetailDataSchedulingManager } from './managers/detail-data-scheduling.manager'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { IndexDataSchedulingLogManager } from './managers/index-data-scheduling-log.manager'; +import { DataSchedulingLogReadService } from '../../data/services/data-scheduling-log-read.service'; @Injectable() export class DataSchedulingReadOrchestrator extends BaseReadOrchestrator { constructor( private indexManager: IndexDataSchedulingManager, + private indexLogManager: IndexDataSchedulingLogManager, private detailManager: DetailDataSchedulingManager, private serviceData: DataSchedulingReadService, + private logServiceData: DataSchedulingLogReadService, ) { super(); } @@ -24,6 +31,16 @@ export class DataSchedulingReadOrchestrator extends BaseReadOrchestrator> { + this.indexLogManager.setFilterParam(params); + this.indexLogManager.setService( + this.logServiceData, + TABLE_NAME.DATA_SCHEDULING_LOG, + ); + await this.indexLogManager.execute(); + return this.indexLogManager.getResult(); + } + async detail(dataId: string): Promise { this.detailManager.setData(dataId); this.detailManager.setService(this.serviceData, TABLE_NAME.DATA_SCHEDULING); diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts index b7c95dd..80c6fe7 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -7,7 +7,7 @@ import { SCHEDULING_LOG_TYPE_ENUM, } from '../../entities/data-scheduling.entity'; import { Logger } from '@nestjs/common'; -import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; +import { DataSchedulingLogDataService } from '../../../data/services/data-scheduling-log-data.service'; @EventsHandler(DataSchedulingChangeStatusEvent) export class DataSchedulingChangeStatusHandler @@ -15,7 +15,7 @@ export class DataSchedulingChangeStatusHandler { private readonly logger = new Logger(DataSchedulingChangeStatusHandler.name); - constructor(private service: DataSchedulingLogService) {} + constructor(private service: DataSchedulingLogDataService) {} async handle(event: DataSchedulingChangeStatusEvent) { const oldData = event?.data?.old; diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts index b1c6c1d..155d8f1 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts @@ -7,7 +7,7 @@ import { } from '../../entities/data-scheduling.entity'; import { decryptionTotal } from '../../../infrastructure/helpers'; import { Logger } from '@nestjs/common'; -import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; +import { DataSchedulingLogDataService } from '../../../data/services/data-scheduling-log-data.service'; @EventsHandler(DataSchedulingCreatedEvent) export class DataSchedulingCreatedHandler @@ -15,7 +15,7 @@ export class DataSchedulingCreatedHandler { private readonly logger = new Logger(DataSchedulingCreatedHandler.name); - constructor(private service: DataSchedulingLogService) {} + constructor(private service: DataSchedulingLogDataService) {} async handle(event: DataSchedulingCreatedEvent) { const data = event?.data?.data; diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts index d9a13b7..4ad1d42 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts @@ -6,7 +6,7 @@ import { SCHEDULING_LOG_TYPE_ENUM, } from '../../entities/data-scheduling.entity'; import { Logger } from '@nestjs/common'; -import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; +import { DataSchedulingLogDataService } from '../../../data/services/data-scheduling-log-data.service'; @EventsHandler(DataSchedulingDeletedEvent) export class DataSchedulingDeletedHandler @@ -14,7 +14,7 @@ export class DataSchedulingDeletedHandler { private readonly logger = new Logger(DataSchedulingDeletedHandler.name); - constructor(private service: DataSchedulingLogService) {} + constructor(private service: DataSchedulingLogDataService) {} async handle(event: DataSchedulingDeletedEvent) { const deletedData = event?.data?.data; diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts index 2d45674..67b7cf8 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -10,7 +10,7 @@ import { encryptionTotal, } from '../../../infrastructure/helpers'; import { Logger } from '@nestjs/common'; -import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service'; +import { DataSchedulingLogDataService } from '../../../data/services/data-scheduling-log-data.service'; @EventsHandler(DataSchedulingUpdatedEvent) export class DataSchedulingUpdatedHandler @@ -18,7 +18,7 @@ export class DataSchedulingUpdatedHandler { private readonly logger = new Logger(DataSchedulingUpdatedHandler.name); - constructor(private service: DataSchedulingLogService) {} + constructor(private service: DataSchedulingLogDataService) {} // Map for readable labels private readonly labelMap: { [key: string]: string } = { diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts new file mode 100644 index 0000000..124727d --- /dev/null +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { DataSchedulingLogEntity } from '../../entities/data-scheduling.entity'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { decryptionTotal } from '../../../infrastructure/helpers'; + +@Injectable() +export class IndexDataSchedulingLogManager extends BaseIndexManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + joinRelations: [], + selectRelations: [], + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.type`, + `${this.tableName}.action`, + `${this.tableName}.log_created_at`, + `${this.tableName}.data_id`, + `${this.tableName}.default_value`, + `${this.tableName}.description`, + + `${this.tableName}.status`, + `${this.tableName}.name`, + `${this.tableName}.indexing_key`, + `${this.tableName}.schedule_date_from`, + `${this.tableName}.schedule_date_to`, + `${this.tableName}.created_at`, + `${this.tableName}.creator_name`, + `${this.tableName}.updated_at`, + `${this.tableName}.editor_name`, + ]; + } + + get specificFilter(): Param[] { + return [ + { + cols: `${this.tableName}.name`, + data: this.filterParam.names, + }, + ]; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + if (this.filterParam.schedule_date_from) { + const dateFrom = this.filterParam.schedule_date_from; + queryBuilder.andWhere('schedule_date_from >= :dateFrom', { + dateFrom: dateFrom, + }); + } + + if (this.filterParam.schedule_date_to) { + const dateTo = this.filterParam.schedule_date_to; + queryBuilder.andWhere('schedule_date_from <= :dateTo', { + dateTo: dateTo, + }); + } + return queryBuilder; + } + + getResult(): PaginationResponse { + const data = this.result.data; + + return { + ...this.result, + data: data.map((item) => { + const total = decryptionTotal(item.indexing_key as string); + return { ...item, indexing_key: total }; + }), + }; + } +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts index 4d1f17f..669c12f 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-read.controller.ts @@ -2,11 +2,14 @@ import { Controller, Get, Param, Query } from '@nestjs/common'; import { FilterDataSchedulingDto } from './dto/filter-data-scheduling.dto'; import { Pagination } from 'src/core/response'; import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; -import { DataSchedulingEntity } from '../domain/entities/data-scheduling.entity'; +import { + DataSchedulingEntity, + DataSchedulingLogEntity, +} from '../domain/entities/data-scheduling.entity'; import { DataSchedulingReadOrchestrator } from '../domain/usecases/data-scheduling-read.orchestrator'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; -import { Public } from 'src/core/guards'; +import { ExcludePrivilege, Public } from 'src/core/guards'; @ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} - read`) @Controller(`v1/${MODULE_NAME.DATA_SCHEDULING}`) @@ -28,3 +31,20 @@ export class DataSchedulingReadController { return await this.orchestrator.detail(id); } } + +@ApiTags(`${MODULE_NAME.DATA_SCHEDULING_LOG.split('-').join(' ')} - read`) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_LOG}`) +@Public(false) +@ApiBearerAuth('JWT') +export class DataSchedulingLogReadController { + constructor(private orchestrator: DataSchedulingReadOrchestrator) {} + + @Get() + @Pagination() + @ExcludePrivilege() + async index( + @Query() params: FilterDataSchedulingDto, + ): Promise> { + return await this.orchestrator.indexLog(params); + } +} From 8527b8ee1d2f40ed9eb30312cb167f8c01b05ef8 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 10 Jul 2025 19:52:41 +0700 Subject: [PATCH 23/28] feat: implementation save log when update default percentage --- .../data-scheduling-change-status.handler.ts | 61 ++++++++++--------- .../data-scheduling-default.manager.ts | 39 +++++++++++- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts index 80c6fe7..59cb7da 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -18,40 +18,43 @@ export class DataSchedulingChangeStatusHandler constructor(private service: DataSchedulingLogDataService) {} async handle(event: DataSchedulingChangeStatusEvent) { - const oldData = event?.data?.old; - const newData = event?.data?.data; + // Prevent execution if the event data is null, which can happen if triggered from the default percentage update service. + if (event.data?.data) { + const oldData = event?.data?.old; + const newData = event?.data?.data; - const oldStatus = capitalizeEachWord(oldData?.status); - const newStatus = capitalizeEachWord(newData.status); + const oldStatus = capitalizeEachWord(oldData?.status); + const newStatus = capitalizeEachWord(newData?.status); - const scheduleName = newData?.name || 'an item'; - const editorName = newData.editor_name || 'System'; - const description = `

${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.

`; + const scheduleName = newData?.name || 'an item'; + const editorName = newData.editor_name || 'System'; + const description = `

${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.

`; - const payload: DataSchedulingLogEntity = { - type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, - action: SCHEDULING_LOG_ACTION_ENUM.CHANGE_STATUS, - log_created_at: new Date().getTime(), + const payload: DataSchedulingLogEntity = { + type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, + action: SCHEDULING_LOG_ACTION_ENUM.CHANGE_STATUS, + log_created_at: new Date().getTime(), - data_id: newData?.id, - name: newData?.name, - indexing_key: newData?.indexing_key, - schedule_date_from: newData?.schedule_date_from, - schedule_date_to: newData?.schedule_date_to, + data_id: newData?.id, + name: newData?.name, + indexing_key: newData?.indexing_key, + schedule_date_from: newData?.schedule_date_from, + schedule_date_to: newData?.schedule_date_to, - status: newData?.status, - creator_id: newData?.creator_id, - creator_name: newData?.creator_name, - editor_id: newData?.editor_id, - editor_name: newData?.editor_name, - created_at: newData?.created_at, - updated_at: newData?.updated_at, - description: description, - }; + status: newData?.status, + creator_id: newData?.creator_id, + creator_name: newData?.creator_name, + editor_id: newData?.editor_id, + editor_name: newData?.editor_name, + created_at: newData?.created_at, + updated_at: newData?.updated_at, + description: description, + }; - await this.service.create(payload as any); - this.logger.verbose( - `[SCHEDULING LOG] Change status data for ID: ${payload.data_id}`, - ); + await this.service.create(payload as any); + this.logger.verbose( + `[SCHEDULING LOG] Change status data for ID: ${payload.data_id}`, + ); + } } } diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts index 5866538..97d68ba 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -2,7 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserProvider, UsersSession } from 'src/core/sessions'; import { BLANK_USER } from 'src/core/strings/constants/base.constants'; import { EditDataSchedulingDefaultDto } from '../../../infrastructure/dto/data-scheduling.dto'; -import { DataSchedulingDefaultEntity } from '../../entities/data-scheduling.entity'; +import { + DataSchedulingDefaultEntity, + DataSchedulingLogEntity, + SCHEDULING_LOG_ACTION_ENUM, + SCHEDULING_LOG_TYPE_ENUM, +} from '../../entities/data-scheduling.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { DataSchedulingDefaultModel } from '../../../data/models/data-scheduling-default.model'; import { Repository } from 'typeorm'; @@ -10,6 +15,7 @@ import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { SelectQueryBuilder } from 'typeorm'; import { EventBus } from '@nestjs/cqrs'; import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event'; +import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model'; @Injectable() export class DataSchedulingManager { @@ -21,6 +27,9 @@ export class DataSchedulingManager { @InjectRepository(DataSchedulingDefaultModel) private repository: Repository, + + @InjectRepository(DataSchedulingLogModel) + private repositoryLog: Repository, ) {} private getUser(): UsersSession { @@ -60,7 +69,35 @@ export class DataSchedulingManager { created_at: dateNow, updated_at: dateNow, }; + const saveData = await this.repository.save(payload); + + if (existData?.default_value !== saveData?.default_value) { + const description = existData?.id + ? `

${saveData.editor_name} mengubah pengaturan Default Percentage dari ${existData.default_value}% menjadi ${saveData.default_value}%.

` + : `

${saveData.creator_name} membuat pengaturan Default Percentage dengan value ${saveData.default_value}%.

`; + + const logPayload: DataSchedulingLogEntity = { + type: SCHEDULING_LOG_TYPE_ENUM.DEFAULT_PERCENTAGE, + action: existData?.id + ? SCHEDULING_LOG_ACTION_ENUM.UPDATE + : SCHEDULING_LOG_ACTION_ENUM.CREATE, + log_created_at: new Date().getTime(), + status: undefined, + + data_id: saveData?.id, + creator_id: saveData?.creator_id, + creator_name: saveData?.creator_name, + editor_id: saveData?.editor_id, + editor_name: saveData?.editor_name, + created_at: saveData?.created_at, + updated_at: saveData?.updated_at, + default_value: saveData?.default_value, + description: description, + }; + + await this.repositoryLog.save(logPayload as any); + } await this.publishEventUpdates(); return saveData; } From dc0dbf6ad3217dd5964720b58f3978a3bf83e850 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani Date: Fri, 11 Jul 2025 10:06:41 +0700 Subject: [PATCH 24/28] feat: update text desc scheduling loh --- .../handlers/data-scheduling-change-status.handler.ts | 2 +- .../usecases/handlers/data-scheduling-created.handler.ts | 2 +- .../usecases/handlers/data-scheduling-deleted.handler.ts | 2 +- .../usecases/handlers/data-scheduling-updated.handler.ts | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts index 59cb7da..0006df5 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -28,7 +28,7 @@ export class DataSchedulingChangeStatusHandler const scheduleName = newData?.name || 'an item'; const editorName = newData.editor_name || 'System'; - const description = `

${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.

`; + const description = `
${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.
`; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts index 155d8f1..df851ee 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts @@ -22,7 +22,7 @@ export class DataSchedulingCreatedHandler const totalPercentage = decryptionTotal(data?.indexing_key); const scheduleName = data?.name || 'a new schedule'; - const description = `

${data.creator_name} created ${scheduleName} schedule from ${data?.schedule_date_from} to ${data.schedule_date_to} with a total percentage of ${totalPercentage}%.

`; + const description = `
${data.creator_name} created ${scheduleName} schedule from ${data?.schedule_date_from} to ${data.schedule_date_to} with a total percentage of ${totalPercentage}%.
`; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts index 4ad1d42..48914ac 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts @@ -28,7 +28,7 @@ export class DataSchedulingDeletedHandler const scheduleName = deletedData?.name || 'an item'; - const description = `

${deleterName} deleted schedule: ${scheduleName}.

`; + const description = `
${deleterName} deleted schedule: ${scheduleName}.
`; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts index 67b7cf8..5ff4aba 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -119,7 +119,7 @@ export class DataSchedulingUpdatedHandler const editorName = newData.editor_name || 'System'; const itemName = oldData?.name || 'an item'; - let description = `

${editorName} has updated schedule for ${itemName}.`; + let description = `

${editorName} has updated schedule for ${itemName}.`; if (Object.keys(changingData.old).length > 0) { description += ` Change details:
    `; @@ -135,10 +135,10 @@ export class DataSchedulingUpdatedHandler newValue = `${newValue}%`; } - description += `
  • ${label} changed from ${oldValue} to ${newValue}.
  • `; + description += `
  • ${label} changed from ${oldValue} to ${newValue}.
  • `; } } - description += `

`; + description += `
`; } else { description += ` No significant data detail changes.

`; } From 0e8256bee75da18774e8d49d4a7083824ddd6e80 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani Date: Fri, 11 Jul 2025 11:19:11 +0700 Subject: [PATCH 25/28] feat: update text desc scheduling loh --- .../handlers/data-scheduling-change-status.handler.ts | 5 ++++- .../handlers/data-scheduling-deleted.handler.ts | 5 +++-- .../handlers/data-scheduling-updated.handler.ts | 11 ++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts index 0006df5..fb32d29 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -8,6 +8,7 @@ import { } from '../../entities/data-scheduling.entity'; import { Logger } from '@nestjs/common'; import { DataSchedulingLogDataService } from '../../../data/services/data-scheduling-log-data.service'; +import { decryptionTotal } from '../../../infrastructure/helpers'; @EventsHandler(DataSchedulingChangeStatusEvent) export class DataSchedulingChangeStatusHandler @@ -28,7 +29,9 @@ export class DataSchedulingChangeStatusHandler const scheduleName = newData?.name || 'an item'; const editorName = newData.editor_name || 'System'; - const description = `
${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.
`; + const totalPercentage = decryptionTotal(newData?.indexing_key); + + const description = `
${editorName} changed the status of ${scheduleName} (${totalPercentage}%) from ${oldStatus} to ${newStatus}.
`; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts index 48914ac..1d7f366 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts @@ -7,6 +7,7 @@ import { } from '../../entities/data-scheduling.entity'; import { Logger } from '@nestjs/common'; import { DataSchedulingLogDataService } from '../../../data/services/data-scheduling-log-data.service'; +import { decryptionTotal } from '../../../infrastructure/helpers'; @EventsHandler(DataSchedulingDeletedEvent) export class DataSchedulingDeletedHandler @@ -27,8 +28,8 @@ export class DataSchedulingDeletedHandler 'System'; const scheduleName = deletedData?.name || 'an item'; - - const description = `
${deleterName} deleted schedule: ${scheduleName}.
`; + const totalPercentage = decryptionTotal(deletedData?.indexing_key); + const description = `
${deleterName} deleted schedule: ${scheduleName} (${totalPercentage}%).
`; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts index 5ff4aba..e0747c1 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -119,7 +119,16 @@ export class DataSchedulingUpdatedHandler const editorName = newData.editor_name || 'System'; const itemName = oldData?.name || 'an item'; - let description = `
${editorName} has updated schedule for ${itemName}.`; + const totalPercentageOld = decryptionTotal(oldData?.indexing_key); + const totalPercentageNew = decryptionTotal(newData?.indexing_key); + const isTotalSame = totalPercentageOld === totalPercentageNew; + const labelName = `${ + isTotalSame + ? `${totalPercentageNew}%` + : `${totalPercentageOld}% to ${totalPercentageNew}%` + }`; + + let description = `
${editorName} has updated schedule for ${itemName} (${labelName}).`; if (Object.keys(changingData.old).length > 0) { description += ` Change details:
    `; From 5d94c7a07c7471020fd527a88e7df4b33ee17b92 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani Date: Fri, 11 Jul 2025 11:42:08 +0700 Subject: [PATCH 26/28] feat: add filter data scheduling date --- .../index-data-scheduling-log.manager.ts | 17 ++++++++++++++- .../managers/index-data-scheduling.manager.ts | 2 +- .../data-scheduling-read.controller.ts | 7 +++++-- .../dto/filter-data-scheduling.dto.ts | 21 +++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts index 124727d..b260df2 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts @@ -74,10 +74,25 @@ export class IndexDataSchedulingLogManager extends BaseIndexManager= :dateFrom', { + dateFrom: dateFrom, + }); + } + + if (this.filterParam.log_created_to) { + const dateTo = this.filterParam.log_created_to; + queryBuilder.andWhere('log_created_to <= :dateTo', { + dateTo: dateTo, + }); + } + return queryBuilder; } diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts index 0c4c86b..3acd015 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling.manager.ts @@ -67,7 +67,7 @@ export class IndexDataSchedulingManager extends BaseIndexManager> { return await this.orchestrator.indexLog(params); } diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts index ab6a50f..628cd11 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/filter-data-scheduling.dto.ts @@ -16,6 +16,27 @@ export class FilterDataSchedulingDto schedule_date_to: Date; } +export class FilterDataSchedulingLogDto + extends BaseFilterDto + implements FilterDataSchedulingEntity +{ + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.schedule_date_from) + schedule_date_from: Date; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.schedule_date_to) + schedule_date_to: Date; + + @ApiProperty({ type: Number, required: false }) + @ValidateIf((body) => body.log_created_from) + log_created_from: number; + + @ApiProperty({ type: Number, required: false }) + @ValidateIf((body) => body.log_created_to) + log_created_to: number; +} + export class FilterActiveDataSchedulingDto { // @ApiProperty({ type: 'string', required: true }) // @ValidateIf((body) => body.schedule_date_from) From 6cd6bc2268a8b2c3ef81dd55586c2a1fe08f16d7 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani Date: Fri, 11 Jul 2025 14:03:17 +0700 Subject: [PATCH 27/28] feat: add filter data scheduling date --- .../data-scheduling/data-scheduling.module.ts | 3 +++ .../data-scheduling-log-data.service.ts | 16 ++++++++++++++++ .../data-scheduling-updated.handler.ts | 4 ++-- .../data-scheduling-data.controller.ts | 19 +++++++++++++++++++ .../infrastructure/dto/data-scheduling.dto.ts | 10 ++++++++++ 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts index 4c39a05..3f786ed 100644 --- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts +++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts @@ -12,6 +12,7 @@ import { DataSchedulingReadOrchestrator } from './domain/usecases/data-schedulin import { DataSchedulingDataController, DataSchedulingDefaultController, + DataSchedulingDataLogController, DataSchedulingSetupController, } from './infrastructure/data-scheduling-data.controller'; import { DataSchedulingDataOrchestrator } from './domain/usecases/data-scheduling-data.orchestrator'; @@ -66,6 +67,7 @@ import { IndexDataSchedulingLogManager } from './domain/usecases/managers/index- DataSchedulingDefaultController, DataSchedulingSetupController, DataSchedulingLogReadController, + DataSchedulingDataLogController, ], providers: [ SetupSchedulingGuard, @@ -86,6 +88,7 @@ import { IndexDataSchedulingLogManager } from './domain/usecases/managers/index- DataSchedulingLogReadService, DataSchedulingDataService, DataSchedulingReadService, + DataSchedulingLogDataService, DataSchedulingDataOrchestrator, DataSchedulingReadOrchestrator, diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts index 170e091..d86b824 100644 --- a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts +++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log-data.service.ts @@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; import { DataSchedulingLogModel } from '../models/data-scheduling-log.model'; import { Repository } from 'typeorm'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; @Injectable() export class DataSchedulingLogDataService { @@ -14,4 +15,19 @@ export class DataSchedulingLogDataService { async create(entity: any): Promise { return await this.repo.save(entity); } + + async deleteRange(from: number, to: number): Promise { + try { + const deleteResult = await this.repo + .createQueryBuilder() + .delete() + .from(TABLE_NAME.DATA_SCHEDULING_LOG) + .where('log_created_at BETWEEN :from AND :to', { from, to }) + .execute(); + + return deleteResult; + } catch (error) { + throw new Error('Failed to delete range due to an internal error.'); + } + } } diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts index e0747c1..e2687bb 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -119,8 +119,8 @@ export class DataSchedulingUpdatedHandler const editorName = newData.editor_name || 'System'; const itemName = oldData?.name || 'an item'; - const totalPercentageOld = decryptionTotal(oldData?.indexing_key); - const totalPercentageNew = decryptionTotal(newData?.indexing_key); + const totalPercentageOld = oldData?.indexing_key; + const totalPercentageNew = newData?.indexing_key; const isTotalSame = totalPercentageOld === totalPercentageNew; const labelName = `${ isTotalSame diff --git a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts index 71dcfd2..bafdfae 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/data-scheduling-data.controller.ts @@ -14,6 +14,7 @@ import { CreateDataSchedulingDto, EditDataSchedulingDto, EditDataSchedulingDefaultDto, + DeleteDataSchedulingLogDto, } from './dto/data-scheduling.dto'; import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; @@ -26,6 +27,7 @@ import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto' import { ExcludePrivilege, Public } from 'src/core/guards'; import { SetupSchedulingGuard } from './guards/setup-scheduling.guard'; import { DataSchedulingManager } from '../domain/usecases/managers/data-scheduling-default.manager'; +import { DataSchedulingLogDataService } from '../data/services/data-scheduling-log-data.service'; @ApiTags(`${MODULE_NAME.DATA_SCHEDULING.split('-').join(' ')} - data`) @Controller(`v1/${MODULE_NAME.DATA_SCHEDULING}`) @@ -127,3 +129,20 @@ export class DataSchedulingSetupController { return this.manager.setupActiveScheduling(); } } + +@ApiTags(`${MODULE_NAME.DATA_SCHEDULING_LOG.split('-').join(' ')} log - Data`) +@Controller(`v1/${MODULE_NAME.DATA_SCHEDULING_LOG}`) +@Public(true) +@ApiBearerAuth('JWT') +export class DataSchedulingDataLogController { + constructor(private service: DataSchedulingLogDataService) {} + + @Post('delete-range') + @ExcludePrivilege() + @UseGuards(SetupSchedulingGuard) + async setup( + @Body() data: DeleteDataSchedulingLogDto, + ): Promise<{ message: string }> { + return this.service.deleteRange(data.log_created_from, data.log_created_to); + } +} diff --git a/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts b/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts index 868f4e8..ab1f7b0 100644 --- a/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts +++ b/src/modules/configuration/data-scheduling/infrastructure/dto/data-scheduling.dto.ts @@ -86,3 +86,13 @@ export class SetupDataSchedulingDto { // @ApiProperty({ type: 'string', required: true, example: '2025-01-01' }) // date: Date; } + +export class DeleteDataSchedulingLogDto { + @ApiProperty({ type: Number, required: true }) + @ValidateIf((body) => body.log_created_from) + log_created_from: number; + + @ApiProperty({ type: Number, required: true }) + @ValidateIf((body) => body.log_created_to) + log_created_to: number; +} From e80bf48d187eea891b481e8548fc535a0089af8c Mon Sep 17 00:00:00 2001 From: Firman Ramdhani Date: Fri, 11 Jul 2025 16:22:41 +0700 Subject: [PATCH 28/28] feat: update text desc scheduling loh --- .../data-scheduling-change-status.handler.ts | 2 +- .../data-scheduling-deleted.handler.ts | 2 +- .../data-scheduling-updated.handler.ts | 4 ++-- .../data-scheduling-default.manager.ts | 4 ++-- .../index-data-scheduling-log.manager.ts | 18 ++++++++++++------ 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts index fb32d29..174ef1b 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts @@ -31,7 +31,7 @@ export class DataSchedulingChangeStatusHandler const editorName = newData.editor_name || 'System'; const totalPercentage = decryptionTotal(newData?.indexing_key); - const description = `
    ${editorName} changed the status of ${scheduleName} (${totalPercentage}%) from ${oldStatus} to ${newStatus}.
    `; + const description = `
    ${editorName} changed the status of ${scheduleName} (${totalPercentage}%) schedule from ${newData?.schedule_date_from} to ${newData.schedule_date_to} from ${oldStatus} to ${newStatus}.
    `; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts index 1d7f366..b173251 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts @@ -29,7 +29,7 @@ export class DataSchedulingDeletedHandler const scheduleName = deletedData?.name || 'an item'; const totalPercentage = decryptionTotal(deletedData?.indexing_key); - const description = `
    ${deleterName} deleted schedule: ${scheduleName} (${totalPercentage}%).
    `; + const description = `
    ${deleterName} deleted schedule: ${scheduleName} (${totalPercentage}%) schedule from ${deletedData?.schedule_date_from} to ${deletedData.schedule_date_to}.
    `; const payload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts index e2687bb..b945d97 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts @@ -128,10 +128,10 @@ export class DataSchedulingUpdatedHandler : `${totalPercentageOld}% to ${totalPercentageNew}%` }`; - let description = `
    ${editorName} has updated schedule for ${itemName} (${labelName}).`; + let description = `
    ${editorName} has updated schedule for ${itemName} (${labelName}) schedule from ${newData?.schedule_date_from} to ${newData.schedule_date_to}.
    `; if (Object.keys(changingData.old).length > 0) { - description += ` Change details:
      `; + description += `Change details:
        `; for (const key in changingData.old) { if (Object.prototype.hasOwnProperty.call(changingData.old, key)) { const label = this.labelMap[key] || key; diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts index 97d68ba..ceac80f 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/data-scheduling-default.manager.ts @@ -74,8 +74,8 @@ export class DataSchedulingManager { if (existData?.default_value !== saveData?.default_value) { const description = existData?.id - ? `

        ${saveData.editor_name} mengubah pengaturan Default Percentage dari ${existData.default_value}% menjadi ${saveData.default_value}%.

        ` - : `

        ${saveData.creator_name} membuat pengaturan Default Percentage dengan value ${saveData.default_value}%.

        `; + ? `
        ${saveData.editor_name} changed the Default Percentage setting from ${existData.default_value}% to ${saveData.default_value}%.
        ` + : `
        ${saveData.creator_name} created the Default Percentage setting with a value of ${saveData.default_value}%.
        `; const logPayload: DataSchedulingLogEntity = { type: SCHEDULING_LOG_TYPE_ENUM.DEFAULT_PERCENTAGE, diff --git a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts index b260df2..33a8eac 100644 --- a/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts +++ b/src/modules/configuration/data-scheduling/domain/usecases/managers/index-data-scheduling-log.manager.ts @@ -79,16 +79,22 @@ export class IndexDataSchedulingLogManager extends BaseIndexManager= :dateFrom', { + const dateTo = this.filterParam.log_created_to; + + queryBuilder.andWhere('log_created_at BETWEEN :dateFrom AND :dateTo', { + dateFrom: dateFrom, + dateTo: dateTo, + }); + } else if (this.filterParam.log_created_from) { + const dateFrom = this.filterParam.log_created_from; + queryBuilder.andWhere('log_created_at >= :dateFrom', { dateFrom: dateFrom, }); - } - - if (this.filterParam.log_created_to) { + } else if (this.filterParam.log_created_to) { const dateTo = this.filterParam.log_created_to; - queryBuilder.andWhere('log_created_to <= :dateTo', { + queryBuilder.andWhere('log_created_at <= :dateTo', { dateTo: dateTo, }); }