From 3b362d2822986ce3f914fe2aae7cab2ecfce04ad Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 May 2025 14:52:26 +0700 Subject: [PATCH 1/7] feat: crete API CRUD feature time group --- src/app.module.ts | 4 + .../strings/constants/module.constants.ts | 2 + src/core/strings/constants/table.constants.ts | 2 + .../1748409891706-create-time-group-table.ts | 27 ++++ .../item/data/models/item.model.ts | 12 ++ .../data/models/time-group.model.ts | 29 +++++ .../data/services/time-group-data.service.ts | 17 +++ .../data/services/time-group-read.service.ts | 17 +++ .../event/time-group-change-status.event.ts | 5 + .../event/time-group-created.event.ts | 5 + .../event/time-group-deleted.event.ts | 5 + .../event/time-group-updated.event.ts | 5 + .../entities/filter-time-group.entity.ts | 11 ++ .../domain/entities/time-group.entity.ts | 8 ++ .../managers/active-time-group.manager.ts | 45 +++++++ .../batch-active-time-group.manager.ts | 45 +++++++ .../batch-confirm-time-group.manager.ts | 45 +++++++ .../batch-delete-time-group.manager.ts | 51 ++++++++ .../batch-inactive-time-group.manager.ts | 51 ++++++++ .../managers/confirm-time-group.manager.ts | 45 +++++++ .../managers/create-time-group.manager.ts | 75 +++++++++++ .../managers/delete-time-group.manager.ts | 51 ++++++++ .../managers/detail-time-group.manager.ts | 48 +++++++ .../managers/inactive-time-group.manager.ts | 51 ++++++++ .../managers/index-time-group.manager.ts | 64 ++++++++++ .../managers/update-time-group.manager.ts | 75 +++++++++++ .../usecases/time-group-data.orchestrator.ts | 119 ++++++++++++++++++ .../usecases/time-group-read.orchestrator.ts | 33 +++++ .../dto/filter-time-group.dto.ts | 33 +++++ .../infrastructure/dto/time-group.dto.ts | 47 +++++++ .../time-group-data.controller.ts | 78 ++++++++++++ .../time-group-read.controller.ts | 30 +++++ .../time-group/time-group.module.ts | 54 ++++++++ 33 files changed, 1189 insertions(+) create mode 100644 src/database/migrations/1748409891706-create-time-group-table.ts create mode 100644 src/modules/item-related/time-group/data/models/time-group.model.ts create mode 100644 src/modules/item-related/time-group/data/services/time-group-data.service.ts create mode 100644 src/modules/item-related/time-group/data/services/time-group-read.service.ts create mode 100644 src/modules/item-related/time-group/domain/entities/event/time-group-change-status.event.ts create mode 100644 src/modules/item-related/time-group/domain/entities/event/time-group-created.event.ts create mode 100644 src/modules/item-related/time-group/domain/entities/event/time-group-deleted.event.ts create mode 100644 src/modules/item-related/time-group/domain/entities/event/time-group-updated.event.ts create mode 100644 src/modules/item-related/time-group/domain/entities/filter-time-group.entity.ts create mode 100644 src/modules/item-related/time-group/domain/entities/time-group.entity.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/active-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/batch-active-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/batch-confirm-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/batch-delete-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/batch-inactive-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/confirm-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/delete-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/detail-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/inactive-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/index-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/time-group-data.orchestrator.ts create mode 100644 src/modules/item-related/time-group/domain/usecases/time-group-read.orchestrator.ts create mode 100644 src/modules/item-related/time-group/infrastructure/dto/filter-time-group.dto.ts create mode 100644 src/modules/item-related/time-group/infrastructure/dto/time-group.dto.ts create mode 100644 src/modules/item-related/time-group/infrastructure/time-group-data.controller.ts create mode 100644 src/modules/item-related/time-group/infrastructure/time-group-read.controller.ts create mode 100644 src/modules/item-related/time-group/time-group.module.ts diff --git a/src/app.module.ts b/src/app.module.ts index 5266aa2..6bb3641 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -99,6 +99,8 @@ import { QueueBucketModel } from './modules/queue/data/models/queue-bucket.model import { VerificationModel } from './modules/booking-online/authentication/data/models/verification.model'; import { BookingOnlineAuthModule } from './modules/booking-online/authentication/auth.module'; import { BookingOrderModule } from './modules/booking-online/order/order.module'; +import { TimeGroupModule } from './modules/item-related/time-group/time-group.module'; +import { TimeGroupModel } from './modules/item-related/time-group/data/models/time-group.model'; @Module({ imports: [ ApmModule.register(), @@ -123,6 +125,7 @@ import { BookingOrderModule } from './modules/booking-online/order/order.module' ItemCategoryModel, ItemRateModel, ItemQueueModel, + TimeGroupModel, LogModel, LogUserLoginModel, NewsModel, @@ -188,6 +191,7 @@ import { BookingOrderModule } from './modules/booking-online/order/order.module' ItemModule, ItemRateModule, ItemQueueModule, + TimeGroupModule, // transaction PaymentMethodModule, diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index b05838e..032dace 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -28,4 +28,6 @@ export enum MODULE_NAME { REPORT_SUMMARY = 'report-summary', QUEUE = 'queue', + + TIME_GROUPS = 'time-groups', } diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 4725e10..dcdc1a3 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -43,4 +43,6 @@ export enum TABLE_NAME { QUEUE_TICKET = 'queue_tickets', QUEUE_ITEM = 'queue_items', QUEUE_BUCKET = 'queue_bucket', + + TIME_GROUPS = 'time_groups', } diff --git a/src/database/migrations/1748409891706-create-time-group-table.ts b/src/database/migrations/1748409891706-create-time-group-table.ts new file mode 100644 index 0000000..5c83f84 --- /dev/null +++ b/src/database/migrations/1748409891706-create-time-group-table.ts @@ -0,0 +1,27 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class CreateTimeGroupTable1748409891706 implements MigrationInterface { + name = 'CreateTimeGroupTable1748409891706'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."time_groups_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`, + ); + await queryRunner.query( + `CREATE TABLE "time_groups" ("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"."time_groups_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "start_time" TIME NOT NULL, "end_time" TIME NOT NULL, "max_usage_time" TIME NOT NULL, CONSTRAINT "PK_083d02988db7bedfe3b7c869b50" PRIMARY KEY ("id"))`, + ); + await queryRunner.query(`ALTER TABLE "items" ADD "time_group_id" uuid`); + await queryRunner.query( + `ALTER TABLE "items" ADD CONSTRAINT "FK_f44f222e1808448dca1b6cc4557" FOREIGN KEY ("time_group_id") REFERENCES "time_groups"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "items" DROP CONSTRAINT "FK_f44f222e1808448dca1b6cc4557"`, + ); + await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "time_group_id"`); + await queryRunner.query(`DROP TABLE "time_groups"`); + await queryRunner.query(`DROP TYPE "public"."time_groups_status_enum"`); + } +} diff --git a/src/modules/item-related/item/data/models/item.model.ts b/src/modules/item-related/item/data/models/item.model.ts index 62e9c83..b565c4d 100644 --- a/src/modules/item-related/item/data/models/item.model.ts +++ b/src/modules/item-related/item/data/models/item.model.ts @@ -17,6 +17,7 @@ import { UserModel } from 'src/modules/user-related/user/data/models/user.model' import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; import { GateModel } from 'src/modules/web-information/gate/data/models/gate.model'; import { ItemQueueModel } from 'src/modules/item-related/item-queue/data/models/item-queue.model'; +import { TimeGroupModel } from 'src/modules/item-related/time-group/data/models/time-group.model'; @Entity(TABLE_NAME.ITEM) export class ItemModel @@ -86,6 +87,17 @@ export class ItemModel @JoinColumn({ name: 'item_category_id' }) item_category: ItemCategoryModel; + // start relation to time group + @Column('varchar', { name: 'time_group_id', nullable: true }) + time_group_id: number; + @ManyToOne(() => TimeGroupModel, (model) => model.items, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'time_group_id' }) + time_group: TimeGroupModel; + // end relation to time group + @ManyToOne(() => ItemQueueModel, (model) => model.items, { onUpdate: 'CASCADE', onDelete: 'SET NULL', diff --git a/src/modules/item-related/time-group/data/models/time-group.model.ts b/src/modules/item-related/time-group/data/models/time-group.model.ts new file mode 100644 index 0000000..e4332e0 --- /dev/null +++ b/src/modules/item-related/time-group/data/models/time-group.model.ts @@ -0,0 +1,29 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { TimeGroupEntity } from '../../domain/entities/time-group.entity'; +import { Column, Entity, OneToMany } from 'typeorm'; +import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; +import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; + +@Entity(TABLE_NAME.TIME_GROUPS) +export class TimeGroupModel + extends BaseStatusModel + implements TimeGroupEntity +{ + @Column('varchar', { name: 'name' }) + name: string; + + @Column({ type: 'time' }) + start_time: string; + + @Column({ type: 'time' }) + end_time: string; + + @Column({ type: 'time' }) + max_usage_time: string; + + @OneToMany(() => ItemModel, (model) => model.time_group, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + items: ItemModel[]; +} diff --git a/src/modules/item-related/time-group/data/services/time-group-data.service.ts b/src/modules/item-related/time-group/data/services/time-group-data.service.ts new file mode 100644 index 0000000..823d81a --- /dev/null +++ b/src/modules/item-related/time-group/data/services/time-group-data.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { TimeGroupEntity } from '../../domain/entities/time-group.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { TimeGroupModel } from '../models/time-group.model'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; + +@Injectable() +export class TimeGroupDataService extends BaseDataService { + constructor( + @InjectRepository(TimeGroupModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/item-related/time-group/data/services/time-group-read.service.ts b/src/modules/item-related/time-group/data/services/time-group-read.service.ts new file mode 100644 index 0000000..a528bd3 --- /dev/null +++ b/src/modules/item-related/time-group/data/services/time-group-read.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { TimeGroupEntity } from '../../domain/entities/time-group.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { TimeGroupModel } from '../models/time-group.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 TimeGroupReadService extends BaseReadService { + constructor( + @InjectRepository(TimeGroupModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/item-related/time-group/domain/entities/event/time-group-change-status.event.ts b/src/modules/item-related/time-group/domain/entities/event/time-group-change-status.event.ts new file mode 100644 index 0000000..4aa911f --- /dev/null +++ b/src/modules/item-related/time-group/domain/entities/event/time-group-change-status.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TimeGroupChangeStatusEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/time-group/domain/entities/event/time-group-created.event.ts b/src/modules/item-related/time-group/domain/entities/event/time-group-created.event.ts new file mode 100644 index 0000000..27c35a0 --- /dev/null +++ b/src/modules/item-related/time-group/domain/entities/event/time-group-created.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TimeGroupCreatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/time-group/domain/entities/event/time-group-deleted.event.ts b/src/modules/item-related/time-group/domain/entities/event/time-group-deleted.event.ts new file mode 100644 index 0000000..a1f8030 --- /dev/null +++ b/src/modules/item-related/time-group/domain/entities/event/time-group-deleted.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TimeGroupDeletedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/time-group/domain/entities/event/time-group-updated.event.ts b/src/modules/item-related/time-group/domain/entities/event/time-group-updated.event.ts new file mode 100644 index 0000000..21d9c80 --- /dev/null +++ b/src/modules/item-related/time-group/domain/entities/event/time-group-updated.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class TimeGroupUpdatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/time-group/domain/entities/filter-time-group.entity.ts b/src/modules/item-related/time-group/domain/entities/filter-time-group.entity.ts new file mode 100644 index 0000000..c286d7c --- /dev/null +++ b/src/modules/item-related/time-group/domain/entities/filter-time-group.entity.ts @@ -0,0 +1,11 @@ +import { BaseFilterEntity } from 'src/core/modules/domain/entities/base-filter.entity'; + +export interface FilterITimeGroupEntity extends BaseFilterEntity { + names: string[]; + start_time_from: string; + start_time_to: string; + end_time_from: string; + end_time_to: string; + max_usage_time_from: string; + max_usage_time_to: string; +} diff --git a/src/modules/item-related/time-group/domain/entities/time-group.entity.ts b/src/modules/item-related/time-group/domain/entities/time-group.entity.ts new file mode 100644 index 0000000..7281552 --- /dev/null +++ b/src/modules/item-related/time-group/domain/entities/time-group.entity.ts @@ -0,0 +1,8 @@ +import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity'; + +export interface TimeGroupEntity extends BaseStatusEntity { + name: string; + start_time: string; + end_time: string; + max_usage_time: string; +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/active-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/active-time-group.manager.ts new file mode 100644 index 0000000..6f3e3e7 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/active-time-group.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 { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupChangeStatusEvent } from '../../entities/event/time-group-change-status.event'; + +@Injectable() +export class ActiveTimeGroupManager 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 TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/batch-active-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/batch-active-time-group.manager.ts new file mode 100644 index 0000000..464c346 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/batch-active-time-group.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupChangeStatusEvent } from '../../entities/event/time-group-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchActiveTimeGroupManager extends BaseBatchUpdateStatusManager { + validateData(data: TimeGroupEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/batch-confirm-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/batch-confirm-time-group.manager.ts new file mode 100644 index 0000000..2d2482c --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/batch-confirm-time-group.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupChangeStatusEvent } from '../../entities/event/time-group-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchConfirmTimeGroupManager extends BaseBatchUpdateStatusManager { + validateData(data: TimeGroupEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/batch-delete-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/batch-delete-time-group.manager.ts new file mode 100644 index 0000000..95bb85b --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/batch-delete-time-group.manager.ts @@ -0,0 +1,51 @@ +import { BaseBatchDeleteManager } from 'src/core/modules/domain/usecase/managers/base-batch-delete.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupDeletedEvent } from '../../entities/event/time-group-deleted.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchDeleteTimeGroupManager extends BaseBatchDeleteManager { + async beforeProcess(): Promise { + return; + } + + async validateData(data: TimeGroupEntity): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return [ + { + relation: 'items', + message: + 'Gagal! tidak dapat menghapus time group karena sudah berelasi dengan item', + }, + ]; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupDeletedEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/batch-inactive-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/batch-inactive-time-group.manager.ts new file mode 100644 index 0000000..ecb437f --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/batch-inactive-time-group.manager.ts @@ -0,0 +1,51 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupChangeStatusEvent } from '../../entities/event/time-group-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchInactiveTimeGroupManager extends BaseBatchUpdateStatusManager { + validateData(data: TimeGroupEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return [ + { + relation: 'items', + message: + 'Gagal! tidak dapat mengubah status time group karena sudah berelasi dengan item', + }, + ]; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/confirm-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/confirm-time-group.manager.ts new file mode 100644 index 0000000..76e207f --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/confirm-time-group.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 { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupChangeStatusEvent } from '../../entities/event/time-group-change-status.event'; + +@Injectable() +export class ConfirmTimeGroupManager 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 TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts new file mode 100644 index 0000000..efa3885 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@nestjs/common'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager'; +import { TimeGroupCreatedEvent } from '../../entities/event/time-group-created.event'; +import * as moment from 'moment'; + +@Injectable() +export class CreateTimeGroupManager extends BaseCreateManager { + async beforeProcess(): Promise { + const queryBuilder = this.dataService + .getRepository() + .createQueryBuilder(this.tableName); + + const overlapping = await queryBuilder + .where(`${this.tableName}.start_time <= :end_time`, { + end_time: this.data.end_time, + }) + .andWhere(`${this.tableName}.end_time >= :start_time`, { + start_time: this.data.start_time, + }) + .getOne(); + + if (overlapping) { + throw new Error( + 'Rentang waktu yang dimasukkan beririsan dengan data lain.', + ); + } else if (this.data.max_usage_time) { + const format = 'HH:mm'; + const end_time = moment(this.data.end_time, format); + const max_usage_time = moment(this.data.max_usage_time, format); + + if (max_usage_time.isBefore(end_time)) { + throw new Error( + 'Waktu maksimum penggunaan harus lebih kecil dari waktu selesai.', + ); + } + } + 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: TimeGroupCreatedEvent, + data: this.data, + }, + ]; + } + + get entityTarget(): any { + return TimeGroupModel; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/delete-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/delete-time-group.manager.ts new file mode 100644 index 0000000..5195904 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/delete-time-group.manager.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupDeletedEvent } from '../../entities/event/time-group-deleted.event'; + +@Injectable() +export class DeleteTimeGroupManager extends BaseDeleteManager { + getResult(): string { + return `Success`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return [ + { + relation: 'items', + message: + 'Gagal! tidak dapat menghapus time group karena sudah berelasi dengan item', + }, + ]; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupDeletedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/detail-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/detail-time-group.manager.ts new file mode 100644 index 0000000..c900594 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/detail-time-group.manager.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDetailManager } from 'src/core/modules/domain/usecase/managers/base-detail.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { RelationParam } from 'src/core/modules/domain/entities/base-filter.entity'; + +@Injectable() +export class DetailTimeGroupManager 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}.start_time`, + `${this.tableName}.end_time`, + `${this.tableName}.max_usage_time`, + `${this.tableName}.created_at`, + `${this.tableName}.creator_name`, + `${this.tableName}.updated_at`, + `${this.tableName}.editor_name`, + ]; + } + + get setFindProperties(): any { + return { + id: this.dataId, + }; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/inactive-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/inactive-time-group.manager.ts new file mode 100644 index 0000000..d6dca30 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/inactive-time-group.manager.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupChangeStatusEvent } from '../../entities/event/time-group-change-status.event'; + +@Injectable() +export class InactiveTimeGroupManager 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 [ + { + relation: 'items', + message: + 'Gagal! tidak dapat mengubah status time group karena sudah berelasi dengan item', + }, + ]; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/index-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/index-time-group.manager.ts new file mode 100644 index 0000000..3ffdd56 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/index-time-group.manager.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; + +// TODO: +// Implementasikan filter by start_time, end_timen, dan max_usage_time + +@Injectable() +export class IndexTimeGroupManager 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}.start_time`, + `${this.tableName}.end_time`, + `${this.tableName}.max_usage_time`, + `${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; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts new file mode 100644 index 0000000..399130a --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager'; +import { TimeGroupEntity } from '../../entities/time-group.entity'; +import { TimeGroupModel } from '../../../data/models/time-group.model'; +import { TimeGroupUpdatedEvent } from '../../entities/event/time-group-updated.event'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import * as moment from 'moment'; + +@Injectable() +export class UpdateTimeGroupManager extends BaseUpdateManager { + async validateProcess(): Promise { + const queryBuilder = this.dataService + .getRepository() + .createQueryBuilder(this.tableName); + + const overlapping = await queryBuilder + .where(`${this.tableName}.start_time <= :end_time`, { + end_time: this.data.end_time, + }) + .andWhere(`${this.tableName}.end_time >= :start_time`, { + start_time: this.data.start_time, + }) + .andWhere(`${this.tableName}.id != :id`, { id: this.dataId ?? null }) + .getOne(); + + if (overlapping) { + throw new Error( + 'Rentang waktu yang dimasukkan beririsan dengan data lain.', + ); + } else if (this.data.max_usage_time) { + const format = 'HH:mm'; + const end_time = moment(this.data.end_time, format); + const max_usage_time = moment(this.data.max_usage_time, format); + + if (max_usage_time.isBefore(end_time)) { + throw new Error( + 'Waktu maksimum penggunaan harus lebih kecil dari waktu selesai.', + ); + } + } + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return [{ column: 'name' }]; + } + + get entityTarget(): any { + return TimeGroupModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: TimeGroupUpdatedEvent, + }, + ]; + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/time-group-data.orchestrator.ts b/src/modules/item-related/time-group/domain/usecases/time-group-data.orchestrator.ts new file mode 100644 index 0000000..d474676 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/time-group-data.orchestrator.ts @@ -0,0 +1,119 @@ +import { Injectable } from '@nestjs/common'; +import { CreateTimeGroupManager } from './managers/create-time-group.manager'; +import { TimeGroupDataService } from '../../data/services/time-group-data.service'; +import { TimeGroupEntity } from '../entities/time-group.entity'; +import { DeleteTimeGroupManager } from './managers/delete-time-group.manager'; +import { UpdateTimeGroupManager } from './managers/update-time-group.manager'; +import { BaseDataTransactionOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-data-transaction.orchestrator'; +import { ActiveTimeGroupManager } from './managers/active-time-group.manager'; +import { InactiveTimeGroupManager } from './managers/inactive-time-group.manager'; +import { ConfirmTimeGroupManager } from './managers/confirm-time-group.manager'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchConfirmTimeGroupManager } from './managers/batch-confirm-time-group.manager'; +import { BatchInactiveTimeGroupManager } from './managers/batch-inactive-time-group.manager'; +import { BatchActiveTimeGroupManager } from './managers/batch-active-time-group.manager'; +import { BatchDeleteTimeGroupManager } from './managers/batch-delete-time-group.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class TimeGroupDataOrchestrator extends BaseDataTransactionOrchestrator { + constructor( + private createManager: CreateTimeGroupManager, + private updateManager: UpdateTimeGroupManager, + private deleteManager: DeleteTimeGroupManager, + private activeManager: ActiveTimeGroupManager, + private confirmManager: ConfirmTimeGroupManager, + private inactiveManager: InactiveTimeGroupManager, + private batchDeleteManager: BatchDeleteTimeGroupManager, + private batchActiveManager: BatchActiveTimeGroupManager, + private batchConfirmManager: BatchConfirmTimeGroupManager, + private batchInactiveManager: BatchInactiveTimeGroupManager, + private serviceData: TimeGroupDataService, + ) { + super(); + } + + async create(data): Promise { + this.createManager.setData(data); + this.createManager.setService(this.serviceData, TABLE_NAME.TIME_GROUPS); + 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.TIME_GROUPS); + await this.updateManager.execute(); + return this.updateManager.getResult(); + } + + async delete(dataId): Promise { + this.deleteManager.setData(dataId); + this.deleteManager.setService(this.serviceData, TABLE_NAME.TIME_GROUPS); + await this.deleteManager.execute(); + return this.deleteManager.getResult(); + } + + async batchDelete(dataIds: string[]): Promise { + this.batchDeleteManager.setData(dataIds); + this.batchDeleteManager.setService( + this.serviceData, + TABLE_NAME.TIME_GROUPS, + ); + 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.TIME_GROUPS); + 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.TIME_GROUPS, + ); + 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.TIME_GROUPS); + 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.TIME_GROUPS, + ); + 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.TIME_GROUPS); + 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.TIME_GROUPS, + ); + await this.batchInactiveManager.execute(); + return this.batchInactiveManager.getResult(); + } +} diff --git a/src/modules/item-related/time-group/domain/usecases/time-group-read.orchestrator.ts b/src/modules/item-related/time-group/domain/usecases/time-group-read.orchestrator.ts new file mode 100644 index 0000000..aa6b6e3 --- /dev/null +++ b/src/modules/item-related/time-group/domain/usecases/time-group-read.orchestrator.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { IndexTimeGroupManager } from './managers/index-time-group.manager'; +import { TimeGroupReadService } from '../../data/services/time-group-read.service'; +import { TimeGroupEntity } from '../entities/time-group.entity'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator'; +import { DetailTimeGroupManager } from './managers/detail-time-group.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class TimeGroupReadOrchestrator extends BaseReadOrchestrator { + constructor( + private indexManager: IndexTimeGroupManager, + private detailManager: DetailTimeGroupManager, + private serviceData: TimeGroupReadService, + ) { + super(); + } + + async index(params): Promise> { + this.indexManager.setFilterParam(params); + this.indexManager.setService(this.serviceData, TABLE_NAME.TIME_GROUPS); + await this.indexManager.execute(); + return this.indexManager.getResult(); + } + + async detail(dataId: string): Promise { + this.detailManager.setData(dataId); + this.detailManager.setService(this.serviceData, TABLE_NAME.TIME_GROUPS); + await this.detailManager.execute(); + return this.detailManager.getResult(); + } +} diff --git a/src/modules/item-related/time-group/infrastructure/dto/filter-time-group.dto.ts b/src/modules/item-related/time-group/infrastructure/dto/filter-time-group.dto.ts new file mode 100644 index 0000000..b4b6cd9 --- /dev/null +++ b/src/modules/item-related/time-group/infrastructure/dto/filter-time-group.dto.ts @@ -0,0 +1,33 @@ +import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.dto'; +import { FilterITimeGroupEntity } from '../../domain/entities/filter-time-group.entity'; +import { ApiProperty } from '@nestjs/swagger'; +import { ValidateIf } from 'class-validator'; + +export class FilterTimeGroupDto + extends BaseFilterDto + implements FilterITimeGroupEntity +{ + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.start_time_from) + start_time_from: string; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.start_time_to) + start_time_to: string; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.end_time_from) + end_time_from: string; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.end_time_to) + end_time_to: string; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.max_usage_time_from) + max_usage_time_from: string; + + @ApiProperty({ type: 'string', required: false }) + @ValidateIf((body) => body.max_usage_time_to) + max_usage_time_to: string; +} diff --git a/src/modules/item-related/time-group/infrastructure/dto/time-group.dto.ts b/src/modules/item-related/time-group/infrastructure/dto/time-group.dto.ts new file mode 100644 index 0000000..f7a5bd9 --- /dev/null +++ b/src/modules/item-related/time-group/infrastructure/dto/time-group.dto.ts @@ -0,0 +1,47 @@ +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { TimeGroupEntity } from '../../domain/entities/time-group.entity'; +import { IsString, ValidateIf } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateTimeGroupDto + extends BaseStatusDto + implements TimeGroupEntity +{ + @ApiProperty({ name: 'name', required: true, example: 'Morning' }) + @IsString() + name: string; + + @ApiProperty({ name: 'start_time', required: true, example: '09:00' }) + @IsString() + start_time: string; + + @ApiProperty({ name: 'end_time', required: true, example: '10:00' }) + @IsString() + end_time: string; + + @ApiProperty({ name: 'max_usage_time', required: true, example: '10:30' }) + @IsString() + max_usage_time: string; +} + +export class EditTimeGroupDto extends BaseStatusDto implements TimeGroupEntity { + @ApiProperty({ name: 'name', example: 'Morning' }) + @IsString() + @ValidateIf((body) => body.name) + name: string; + + @ApiProperty({ name: 'start_time', example: '09:00' }) + @IsString() + @ValidateIf((body) => body.start_time) + start_time: string; + + @ApiProperty({ name: 'end_time', example: '10:00' }) + @IsString() + @ValidateIf((body) => body.end_time) + end_time: string; + + @ApiProperty({ name: 'max_usage_time', example: '10:30' }) + @IsString() + @ValidateIf((body) => body.max_usage_time) + max_usage_time: string; +} diff --git a/src/modules/item-related/time-group/infrastructure/time-group-data.controller.ts b/src/modules/item-related/time-group/infrastructure/time-group-data.controller.ts new file mode 100644 index 0000000..4de60fa --- /dev/null +++ b/src/modules/item-related/time-group/infrastructure/time-group-data.controller.ts @@ -0,0 +1,78 @@ +import { + Body, + Controller, + Delete, + Param, + Patch, + Post, + Put, +} from '@nestjs/common'; +import { TimeGroupDataOrchestrator } from '../domain/usecases/time-group-data.orchestrator'; +import { CreateTimeGroupDto, EditTimeGroupDto } from './dto/time-group.dto'; +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { TimeGroupEntity } from '../domain/entities/time-group.entity'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'; +import { Public } from 'src/core/guards'; + +@ApiTags(`${MODULE_NAME.TIME_GROUPS.split('-').join(' ')} - data`) +@Controller(`v1/${MODULE_NAME.TIME_GROUPS}`) +@Public(false) +@ApiBearerAuth('JWT') +export class TimeGroupDataController { + constructor(private orchestrator: TimeGroupDataOrchestrator) {} + + @Post() + async create(@Body() data: CreateTimeGroupDto): 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: EditTimeGroupDto, + ): Promise { + return await this.orchestrator.update(dataId, data); + } + + @Delete(':id') + async delete(@Param('id') dataId: string): Promise { + return await this.orchestrator.delete(dataId); + } +} diff --git a/src/modules/item-related/time-group/infrastructure/time-group-read.controller.ts b/src/modules/item-related/time-group/infrastructure/time-group-read.controller.ts new file mode 100644 index 0000000..212558d --- /dev/null +++ b/src/modules/item-related/time-group/infrastructure/time-group-read.controller.ts @@ -0,0 +1,30 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { FilterTimeGroupDto } from './dto/filter-time-group.dto'; +import { Pagination } from 'src/core/response'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { TimeGroupEntity } from '../domain/entities/time-group.entity'; +import { TimeGroupReadOrchestrator } from '../domain/usecases/time-group-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.TIME_GROUPS.split('-').join(' ')} - read`) +@Controller(`v1/${MODULE_NAME.TIME_GROUPS}`) +@Public(false) +@ApiBearerAuth('JWT') +export class TimeGroupReadController { + constructor(private orchestrator: TimeGroupReadOrchestrator) {} + + @Get() + @Pagination() + async index( + @Query() params: FilterTimeGroupDto, + ): 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/item-related/time-group/time-group.module.ts b/src/modules/item-related/time-group/time-group.module.ts new file mode 100644 index 0000000..45455c0 --- /dev/null +++ b/src/modules/item-related/time-group/time-group.module.ts @@ -0,0 +1,54 @@ +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 { TimeGroupDataService } from './data/services/time-group-data.service'; +import { TimeGroupReadService } from './data/services/time-group-read.service'; +import { TimeGroupReadController } from './infrastructure/time-group-read.controller'; +import { TimeGroupReadOrchestrator } from './domain/usecases/time-group-read.orchestrator'; +import { TimeGroupDataController } from './infrastructure/time-group-data.controller'; +import { TimeGroupDataOrchestrator } from './domain/usecases/time-group-data.orchestrator'; +import { CreateTimeGroupManager } from './domain/usecases/managers/create-time-group.manager'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexTimeGroupManager } from './domain/usecases/managers/index-time-group.manager'; +import { DeleteTimeGroupManager } from './domain/usecases/managers/delete-time-group.manager'; +import { UpdateTimeGroupManager } from './domain/usecases/managers/update-time-group.manager'; +import { ActiveTimeGroupManager } from './domain/usecases/managers/active-time-group.manager'; +import { ConfirmTimeGroupManager } from './domain/usecases/managers/confirm-time-group.manager'; +import { InactiveTimeGroupManager } from './domain/usecases/managers/inactive-time-group.manager'; +import { DetailTimeGroupManager } from './domain/usecases/managers/detail-time-group.manager'; +import { BatchDeleteTimeGroupManager } from './domain/usecases/managers/batch-delete-time-group.manager'; +import { BatchActiveTimeGroupManager } from './domain/usecases/managers/batch-active-time-group.manager'; +import { BatchConfirmTimeGroupManager } from './domain/usecases/managers/batch-confirm-time-group.manager'; +import { BatchInactiveTimeGroupManager } from './domain/usecases/managers/batch-inactive-time-group.manager'; +import { TimeGroupModel } from './data/models/time-group.model'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature([TimeGroupModel], CONNECTION_NAME.DEFAULT), + CqrsModule, + ], + controllers: [TimeGroupDataController, TimeGroupReadController], + providers: [ + IndexTimeGroupManager, + DetailTimeGroupManager, + CreateTimeGroupManager, + DeleteTimeGroupManager, + UpdateTimeGroupManager, + ActiveTimeGroupManager, + ConfirmTimeGroupManager, + InactiveTimeGroupManager, + BatchDeleteTimeGroupManager, + BatchActiveTimeGroupManager, + BatchConfirmTimeGroupManager, + BatchInactiveTimeGroupManager, + + TimeGroupDataService, + TimeGroupReadService, + + TimeGroupDataOrchestrator, + TimeGroupReadOrchestrator, + ], +}) +export class TimeGroupModule {} From a80e2e341941749d8d329a109060f269b7b1ddaf Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 May 2025 15:08:38 +0700 Subject: [PATCH 2/7] feat: memasangkan time group di item rate, item, dan item antrian --- .../usecases/managers/detail-item-queue.manager.ts | 5 ++++- .../usecases/managers/index-item-queue.manager.ts | 5 ++++- .../usecases/managers/index-item-rate.manager.ts | 4 ++++ .../domain/usecases/managers/detail-item.manager.ts | 10 +++++++++- .../domain/usecases/managers/index-item.manager.ts | 10 +++++++++- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/modules/item-related/item-queue/domain/usecases/managers/detail-item-queue.manager.ts b/src/modules/item-related/item-queue/domain/usecases/managers/detail-item-queue.manager.ts index 2686425..7c62c0c 100644 --- a/src/modules/item-related/item-queue/domain/usecases/managers/detail-item-queue.manager.ts +++ b/src/modules/item-related/item-queue/domain/usecases/managers/detail-item-queue.manager.ts @@ -20,7 +20,7 @@ export class DetailItemQueueManager extends BaseDetailManager { get relations(): RelationParam { return { joinRelations: [], - selectRelations: ['items'], + selectRelations: ['items', 'items.time_group'], countRelations: [], }; } @@ -53,6 +53,9 @@ export class DetailItemQueueManager extends BaseDetailManager { `items.share_profit`, `items.play_estimation`, `items.video_url`, + + 'time_group.id', + 'time_group.name', ]; } diff --git a/src/modules/item-related/item-queue/domain/usecases/managers/index-item-queue.manager.ts b/src/modules/item-related/item-queue/domain/usecases/managers/index-item-queue.manager.ts index a0c94b5..d3ba4d8 100644 --- a/src/modules/item-related/item-queue/domain/usecases/managers/index-item-queue.manager.ts +++ b/src/modules/item-related/item-queue/domain/usecases/managers/index-item-queue.manager.ts @@ -24,7 +24,7 @@ export class IndexItemQueueManager extends BaseIndexManager { get relations(): RelationParam { return { joinRelations: [], - selectRelations: ['items'], + selectRelations: ['items', 'items.time_group'], countRelations: [], }; } @@ -55,6 +55,9 @@ export class IndexItemQueueManager extends BaseIndexManager { `items.base_price`, `items.share_profit`, `items.play_estimation`, + + 'time_group.id', + 'time_group.name', ]; } diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts index bb403c1..34c5314 100644 --- a/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts +++ b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts @@ -76,6 +76,7 @@ export class IndexItemRateManager extends BaseIndexManager { 'item_rates', 'item_rates.season_period', 'season_period.season_type', + 'time_group', ], // relation yang hanya ingin dihitung (akan return number) @@ -113,6 +114,9 @@ export class IndexItemRateManager extends BaseIndexManager { 'season_type.id', 'season_type.name', + + 'time_group.id', + 'time_group.name', ]; } diff --git a/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts index 3488d9d..b091df9 100644 --- a/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts @@ -23,7 +23,12 @@ export class DetailItemManager extends BaseDetailManager { joinRelations: [], // relation join and select (relasi yang ingin ditampilkan), - selectRelations: ['item_category', 'bundling_items', 'tenant'], + selectRelations: [ + 'item_category', + 'bundling_items', + 'tenant', + 'time_group', + ], // relation yang hanya ingin dihitung (akan return number) countRelations: [], @@ -61,6 +66,9 @@ export class DetailItemManager extends BaseDetailManager { 'tenant.id', 'tenant.name', + + 'time_group.id', + 'time_group.name', ]; } diff --git a/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts index b34b1ca..41c12a3 100644 --- a/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts @@ -27,7 +27,12 @@ export class IndexItemManager extends BaseIndexManager { joinRelations: [], // relation join and select (relasi yang ingin ditampilkan), - selectRelations: ['item_category', 'bundling_items', 'tenant'], + selectRelations: [ + 'item_category', + 'bundling_items', + 'tenant', + 'time_group', + ], // relation yang hanya ingin dihitung (akan return number) countRelations: [], @@ -57,6 +62,9 @@ export class IndexItemManager extends BaseIndexManager { 'tenant.id', 'tenant.name', + + 'time_group.id', + 'time_group.name', ]; } From 339b2bdab6b2805af57c85dbe3382f3772087f38 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 May 2025 15:48:36 +0700 Subject: [PATCH 3/7] feat: menambahkan validasi end time untuk create dan update time group --- .../usecases/managers/create-time-group.manager.ts | 10 ++++++++++ .../usecases/managers/update-time-group.manager.ts | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts index efa3885..8dc54a5 100644 --- a/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts +++ b/src/modules/item-related/time-group/domain/usecases/managers/create-time-group.manager.ts @@ -40,6 +40,16 @@ export class CreateTimeGroupManager extends BaseCreateManager { 'Waktu maksimum penggunaan harus lebih kecil dari waktu selesai.', ); } + return; + } else if (this.data.start_time && this.data.end_time) { + const format = 'HH:mm'; + const start_time = moment(this.data.start_time, format); + const end_time = moment(this.data.end_time, format); + + if (end_time.isBefore(start_time)) { + throw new Error('Waktu akhir harus lebih besar dari waktu mulai.'); + } + return; } return; } diff --git a/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts b/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts index 399130a..904bfb4 100644 --- a/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts +++ b/src/modules/item-related/time-group/domain/usecases/managers/update-time-group.manager.ts @@ -41,6 +41,16 @@ export class UpdateTimeGroupManager extends BaseUpdateManager { 'Waktu maksimum penggunaan harus lebih kecil dari waktu selesai.', ); } + return; + } else if (this.data.start_time && this.data.end_time) { + const format = 'HH:mm'; + const start_time = moment(this.data.start_time, format); + const end_time = moment(this.data.end_time, format); + + if (end_time.isBefore(start_time)) { + throw new Error('Waktu akhir harus lebih besar dari waktu mulai.'); + } + return; } return; } From 16bbb1f02bd40490d300f515b0ec12ec3e9b0688 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 May 2025 16:00:58 +0700 Subject: [PATCH 4/7] feat(SPG-1140): menambahkan validasi end time untuk create dan update time group --- .../managers/index-season-period-item.manager.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/season-related/season-period/domain/usecases/managers/index-season-period-item.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/index-season-period-item.manager.ts index e082a3f..7a35c5d 100644 --- a/src/modules/season-related/season-period/domain/usecases/managers/index-season-period-item.manager.ts +++ b/src/modules/season-related/season-period/domain/usecases/managers/index-season-period-item.manager.ts @@ -28,7 +28,12 @@ export class IndexSeasonPeriodeItemManager extends BaseIndexManager Date: Wed, 28 May 2025 17:04:26 +0700 Subject: [PATCH 5/7] feat: add relation time group at handle update item --- .../configuration/couch/domain/managers/item.handler.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/configuration/couch/domain/managers/item.handler.ts b/src/modules/configuration/couch/domain/managers/item.handler.ts index 1c02076..e94a99c 100644 --- a/src/modules/configuration/couch/domain/managers/item.handler.ts +++ b/src/modules/configuration/couch/domain/managers/item.handler.ts @@ -48,6 +48,7 @@ export class ItemUpdatedHandler 'item_rates.item', 'item_rates.season_period', 'item_rates.season_period.season_type', + 'time_group', ], }); @@ -109,6 +110,7 @@ export class ItemPriceUpdatedHandler 'item_rates.item', 'item_rates.season_period', 'item_rates.season_period.season_type', + 'time_group', ], }); @@ -150,6 +152,7 @@ export class ItemRateUpdatedHandler 'item_rates.item', 'item_rates.season_period', 'item_rates.season_period.season_type', + 'time_group', ], }); From 9e5d59baacb88d411233b523fd8c0f3c52f91db9 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 May 2025 17:13:13 +0700 Subject: [PATCH 6/7] feat: add table relation at event handler item --- .../configuration/couch/domain/managers/item.handler.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/configuration/couch/domain/managers/item.handler.ts b/src/modules/configuration/couch/domain/managers/item.handler.ts index e94a99c..75e4c78 100644 --- a/src/modules/configuration/couch/domain/managers/item.handler.ts +++ b/src/modules/configuration/couch/domain/managers/item.handler.ts @@ -44,6 +44,7 @@ export class ItemUpdatedHandler 'item_category', 'bundling_items', 'bundling_items.item_category', + 'bundling_items.time_group', 'item_rates', 'item_rates.item', 'item_rates.season_period', @@ -106,6 +107,7 @@ export class ItemPriceUpdatedHandler 'item_category', 'bundling_items', 'bundling_items.item_category', + 'bundling_items.time_group', 'item_rates', 'item_rates.item', 'item_rates.season_period', @@ -148,6 +150,7 @@ export class ItemRateUpdatedHandler 'item_category', 'bundling_items', 'bundling_items.item_category', + 'bundling_items.time_group', 'item_rates', 'item_rates.item', 'item_rates.season_period', From 845e0547ab7b7d691f775cd2812b6692e768a7b0 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 28 May 2025 18:26:52 +0700 Subject: [PATCH 7/7] feat(SPG-1133): penambahan kolom dan filter time group di report income per item dan report income per item master --- .../configs/income-per-item-master.ts | 14 ++++++++++++++ .../transaction-report/configs/income-per-item.ts | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts index 62d46e8..9ab4af5 100644 --- a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts +++ b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts @@ -19,6 +19,7 @@ export default { LEFT JOIN refunds refund ON refund.transaction_id = main.id LEFT JOIN refund_items refund_item ON refund_item.refund_item_id = tr_item.item_id::uuid LEFT JOIN items item ON item.id::text = tr_item.item_id::text + LEFT JOIN time_groups tg on tg.id = item.time_group_id LEFT JOIN users tenant ON tenant.id::text = item.tenant_id::text`, main_table_alias: 'main', whereDefaultConditions: [ @@ -111,6 +112,13 @@ export default { type: DATA_TYPE.DIMENSION, format: DATA_FORMAT.TEXT, }, + { + column: 'tg__name', + query: 'tg.name', + label: 'Time Group', + type: DATA_TYPE.DIMENSION, + format: DATA_FORMAT.TEXT, + }, { column: 'tr_item__item_name', query: `CASE WHEN tr_item.item_type = 'bundling' THEN tr_item_bundling.item_name ELSE tr_item.item_name END`, @@ -338,6 +346,12 @@ export default { field_type: FILTER_FIELD_TYPE.input_tag, filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS, }, + { + filed_label: 'Time Group', + filter_column: 'tg__name', + field_type: FILTER_FIELD_TYPE.input_tag, + filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS, + }, { filed_label: 'Tipe Pelanggan', filter_column: 'main__customer_type', diff --git a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts index e091d99..277b090 100644 --- a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts +++ b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts @@ -18,6 +18,7 @@ export default { LEFT JOIN refunds refund ON refund.transaction_id = main.id LEFT JOIN refund_items refund_item ON refund_item.refund_item_id = tr_item.item_id::uuid LEFT JOIN items item ON item.id::text = tr_item.item_id::text + LEFT JOIN time_groups tg on tg.id = item.time_group_id LEFT JOIN users tenant ON tenant.id::text = item.tenant_id::text`, main_table_alias: 'main', whereDefaultConditions: [ @@ -109,6 +110,13 @@ export default { type: DATA_TYPE.DIMENSION, format: DATA_FORMAT.TEXT, }, + { + column: 'tg__name', + query: 'tg.name', + label: 'Time Group', + type: DATA_TYPE.DIMENSION, + format: DATA_FORMAT.TEXT, + }, { column: 'main__customer_type', query: 'main.customer_type', @@ -296,6 +304,12 @@ export default { field_type: FILTER_FIELD_TYPE.input_tag, filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS, }, + { + filed_label: 'Time Group', + filter_column: 'tg__name', + field_type: FILTER_FIELD_TYPE.input_tag, + filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS, + }, { filed_label: 'Tipe Pelanggan', filter_column: 'main__customer_type',