diff --git a/src/app.module.ts b/src/app.module.ts index 7bf0036..f430b37 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -37,6 +37,11 @@ import { SalesPriceFormulaModel } from './modules/transaction/sales-price-formul import { ProfitShareFormulaModule } from './modules/transaction/profit-share-formula/profit-share-formula.module'; import { PaymentMethodModule } from './modules/transaction/payment-method/payment-method.module'; import { PaymentMethodModel } from './modules/transaction/payment-method/data/models/payment-method.model'; +import { SeasonPeriodModule } from './modules/season-related/season-period/season-period.module'; +import { SeasonPeriodModel } from './modules/season-related/season-period/data/models/season-period.model'; +import { ItemRateModule } from './modules/item-related/item-rate/item-rate.module'; +import { ItemRateModel } from './modules/item-related/item-rate/data/models/item-rate.model'; +import { GoogleCalendarModule } from './modules/configuration/google-calendar/google-calendar.module'; @Module({ imports: [ @@ -57,9 +62,11 @@ import { PaymentMethodModel } from './modules/transaction/payment-method/data/mo ErrorLogModel, ItemModel, ItemCategoryModel, + ItemRateModel, LogModel, PaymentMethodModel, SalesPriceFormulaModel, + SeasonPeriodModel, SeasonTypeModel, TaxModel, UserModel, @@ -68,12 +75,13 @@ import { PaymentMethodModel } from './modules/transaction/payment-method/data/mo ], synchronize: false, }), + AuthModule, ConstantModule, CqrsModule, - SessionModule, - AuthModule, CouchModule, + GoogleCalendarModule, LogModule, + SessionModule, // user TenantModule, @@ -83,6 +91,7 @@ import { PaymentMethodModel } from './modules/transaction/payment-method/data/mo // Item ItemCategoryModule, ItemModule, + ItemRateModule, // transaction PaymentMethodModule, @@ -94,6 +103,7 @@ import { PaymentMethodModel } from './modules/transaction/payment-method/data/mo // session SeasonTypeModule, + SeasonPeriodModule, ], controllers: [], providers: [ diff --git a/src/core/modules/data/service/base-data.service.ts b/src/core/modules/data/service/base-data.service.ts index b957808..dafd592 100644 --- a/src/core/modules/data/service/base-data.service.ts +++ b/src/core/modules/data/service/base-data.service.ts @@ -21,6 +21,15 @@ export abstract class BaseDataService { return await queryRunner.manager.save(newEntity); } + async createBatch( + queryRunner: QueryRunner, + entityTarget: EntityTarget, + entity: Entity[], + ): Promise { + const newEntity = queryRunner.manager.create(entityTarget, entity); + return await queryRunner.manager.save(newEntity); + } + async update( queryRunner: QueryRunner, entityTarget: EntityTarget, diff --git a/src/core/modules/domain/usecase/managers/base-create.manager.ts b/src/core/modules/domain/usecase/managers/base-create.manager.ts index 61e08a4..fdfb694 100644 --- a/src/core/modules/domain/usecase/managers/base-create.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-create.manager.ts @@ -73,6 +73,19 @@ export abstract class BaseCreateManager extends BaseManager { }), ); - // if (!this.eventTopics.length) return; + if (!this.eventTopics.length) return; + for (const topic of this.eventTopics) { + this.eventBus.publishAll([ + new topic.topic({ + id: this.result['id'], + old: null, + data: topic.data, + user: this.user, + description: '', + module: this.tableName, + op: OPERATION.CREATE, + }), + ]); + } } } diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index d5579b7..ccc5889 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -1,8 +1,10 @@ export enum MODULE_NAME { ITEM = 'items', ITEM_CATEGORY = 'item-categories', + ITEM_RATE = 'item-rates', PAYMENT_METHOD = 'payment-methods', SEASON_TYPE = 'season-types', + SEASON_PERIOD = 'season-periods', TAX = 'taxes', TENANT = 'tenants', USER = 'users', diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index aeec40a..16f7897 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -2,10 +2,12 @@ export enum TABLE_NAME { ERROR_LOG = 'log_errors', ITEM = 'items', ITEM_CATEGORY = 'item_categories', + ITEM_RATE = 'item_rates', LOG = 'logs', PAYMENT_METHOD = 'payment_methods', PRICE_FORMULA = 'price_formulas', SEASON_TYPE = 'season_types', + SEASON_PERIOD = 'season_periods', TAX = 'taxes', TENANT = 'tenants', USER = 'users', diff --git a/src/database/migrations/1718675739425-update-column-tax.ts b/src/database/migrations/1718675739425-update-column-tax.ts index 78a58a9..2cd829c 100644 --- a/src/database/migrations/1718675739425-update-column-tax.ts +++ b/src/database/migrations/1718675739425-update-column-tax.ts @@ -1,16 +1,19 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; +import { MigrationInterface, QueryRunner } from 'typeorm'; export class UpdateColumnTax1718675739425 implements MigrationInterface { - name = 'UpdateColumnTax1718675739425' + name = 'UpdateColumnTax1718675739425'; - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "value"`); - await queryRunner.query(`ALTER TABLE "taxes" ADD "value" double precision NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "value"`); - await queryRunner.query(`ALTER TABLE "taxes" ADD "value" integer NOT NULL DEFAULT '0'`); - } + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "value"`); + await queryRunner.query( + `ALTER TABLE "taxes" ADD "value" double precision NOT NULL DEFAULT '0'`, + ); + } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "value"`); + await queryRunner.query( + `ALTER TABLE "taxes" ADD "value" integer NOT NULL DEFAULT '0'`, + ); + } } diff --git a/src/database/migrations/1718699373958-season-period.ts b/src/database/migrations/1718699373958-season-period.ts new file mode 100644 index 0000000..32f479d --- /dev/null +++ b/src/database/migrations/1718699373958-season-period.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SeasonPeriod1718699373958 implements MigrationInterface { + name = 'SeasonPeriod1718699373958'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."season_periods_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`, + ); + await queryRunner.query( + `CREATE TABLE "season_periods" ("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"."season_periods_status_enum" NOT NULL DEFAULT 'draft', "start_date" date, "end_date" date, "days" text, "holiday_name" character varying, "season_type_id" uuid, CONSTRAINT "PK_8e25cedd8ffb18516de871fb4e0" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "season_periods" ADD CONSTRAINT "FK_4e9e71a640b450d23177c2add46" FOREIGN KEY ("season_type_id") REFERENCES "season_types"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "season_periods" DROP CONSTRAINT "FK_4e9e71a640b450d23177c2add46"`, + ); + await queryRunner.query(`DROP TABLE "season_periods"`); + await queryRunner.query(`DROP TYPE "public"."season_periods_status_enum"`); + } +} diff --git a/src/database/migrations/1718792479432-item-rate.ts b/src/database/migrations/1718792479432-item-rate.ts new file mode 100644 index 0000000..5c97cb2 --- /dev/null +++ b/src/database/migrations/1718792479432-item-rate.ts @@ -0,0 +1,27 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ItemRate1718792479432 implements MigrationInterface { + name = 'ItemRate1718792479432'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "item_rates" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "item_id" uuid, "season_period_id" uuid, "price" bigint, CONSTRAINT "PK_e3e0cf3b098409533dc20bf1992" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "item_rates" ADD CONSTRAINT "FK_2bff76c50f678bdf0c4f93990fc" FOREIGN KEY ("item_id") REFERENCES "items"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "item_rates" ADD CONSTRAINT "FK_d309461c7e5a7f83ca596203afa" FOREIGN KEY ("season_period_id") REFERENCES "season_periods"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "item_rates" DROP CONSTRAINT "FK_d309461c7e5a7f83ca596203afa"`, + ); + await queryRunner.query( + `ALTER TABLE "item_rates" DROP CONSTRAINT "FK_2bff76c50f678bdf0c4f93990fc"`, + ); + await queryRunner.query(`DROP TABLE "item_rates"`); + } +} diff --git a/src/modules/configuration/google-calendar/constants.ts b/src/modules/configuration/google-calendar/constants.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/configuration/google-calendar/domain/usecases/google-calendar.orchestrator.ts b/src/modules/configuration/google-calendar/domain/usecases/google-calendar.orchestrator.ts new file mode 100644 index 0000000..3dfcad7 --- /dev/null +++ b/src/modules/configuration/google-calendar/domain/usecases/google-calendar.orchestrator.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IndexHolidayCalendarManager } from './managers/index-holiday-google-calendar.manager'; + +@Injectable() +export class GoogleCalendarOrchestrator { + constructor(private indexHoliday: IndexHolidayCalendarManager) {} + + async holiday() { + return await this.indexHoliday.execute(); + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/index-holiday-google-calendar.manager.ts b/src/modules/configuration/google-calendar/domain/usecases/managers/index-holiday-google-calendar.manager.ts similarity index 91% rename from src/modules/season-related/season-period/domain/usecases/managers/index-holiday-google-calendar.manager.ts rename to src/modules/configuration/google-calendar/domain/usecases/managers/index-holiday-google-calendar.manager.ts index e1c52c3..191b755 100644 --- a/src/modules/season-related/season-period/domain/usecases/managers/index-holiday-google-calendar.manager.ts +++ b/src/modules/configuration/google-calendar/domain/usecases/managers/index-holiday-google-calendar.manager.ts @@ -30,7 +30,9 @@ export class IndexHolidayCalendarManager { eventName = 'Hari Raya Natal'; const exist = events.find((event) => { - return event.name.toLowerCase().includes(eventName.toLowerCase()); + return event.holiday_name + .toLowerCase() + .includes(eventName.toLowerCase()); }); if (exist) { @@ -40,7 +42,7 @@ export class IndexHolidayCalendarManager { }); } else { events.push({ - name: eventName, + holiday_name: eventName, start_date: item.start?.date, end_date: item.start?.date, total_day: 1, diff --git a/src/modules/configuration/google-calendar/google-calendar.module.ts b/src/modules/configuration/google-calendar/google-calendar.module.ts new file mode 100644 index 0000000..e3659cc --- /dev/null +++ b/src/modules/configuration/google-calendar/google-calendar.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexHolidayCalendarManager } from '../../configuration/google-calendar/domain/usecases/managers/index-holiday-google-calendar.manager'; +import { GoogleCalendarController } from './infrastructure/google-calendar.controller'; +import { GoogleCalendarOrchestrator } from './domain/usecases/google-calendar.orchestrator'; + +@Module({ + imports: [ConfigModule.forRoot(), CqrsModule], + controllers: [GoogleCalendarController], + providers: [IndexHolidayCalendarManager, GoogleCalendarOrchestrator], +}) +export class GoogleCalendarModule {} diff --git a/src/modules/configuration/google-calendar/index.ts b/src/modules/configuration/google-calendar/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/configuration/google-calendar/infrastructure/google-calendar.controller.ts b/src/modules/configuration/google-calendar/infrastructure/google-calendar.controller.ts new file mode 100644 index 0000000..0cee860 --- /dev/null +++ b/src/modules/configuration/google-calendar/infrastructure/google-calendar.controller.ts @@ -0,0 +1,16 @@ +import { GoogleCalendarOrchestrator } from './../domain/usecases/google-calendar.orchestrator'; +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Public } from 'src/core/guards'; + +@ApiTags(`google calendar - read`) +@Controller('google-calendar') +@Public(true) +export class GoogleCalendarController { + constructor(private orchestrator: GoogleCalendarOrchestrator) {} + + @Get('/holiday') + async calendar() { + return await this.orchestrator.holiday(); + } +} diff --git a/src/modules/item-related/item-rate/constants.ts b/src/modules/item-related/item-rate/constants.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/item-related/item-rate/data/models/item-rate.model.ts b/src/modules/item-related/item-rate/data/models/item-rate.model.ts new file mode 100644 index 0000000..cf1bf78 --- /dev/null +++ b/src/modules/item-related/item-rate/data/models/item-rate.model.ts @@ -0,0 +1,35 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { ItemRateEntity } from '../../domain/entities/item-rate.entity'; +import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; +import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; +import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; +import { SeasonPeriodModel } from 'src/modules/season-related/season-period/data/models/season-period.model'; + +@Entity(TABLE_NAME.ITEM_RATE) +export class ItemRateModel + extends BaseCoreModel + implements ItemRateEntity +{ + @Column('varchar', { name: 'item_id', nullable: true }) + item_id: string; + + @ManyToOne(() => ItemModel, (model) => model.item_rates, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + @JoinColumn({ name: 'item_id' }) + item: ItemModel; + + @Column('varchar', { name: 'season_period_id', nullable: true }) + season_period_id: string; + + @ManyToOne(() => SeasonPeriodModel, (model) => model.item_rates, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + @JoinColumn({ name: 'season_period_id' }) + season_period: SeasonPeriodModel; + + @Column('bigint', { name: 'price', nullable: true }) + price: number; +} diff --git a/src/modules/item-related/item-rate/data/services/item-rate-data.service.ts b/src/modules/item-related/item-rate/data/services/item-rate-data.service.ts new file mode 100644 index 0000000..1c8862f --- /dev/null +++ b/src/modules/item-related/item-rate/data/services/item-rate-data.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { ItemRateEntity } from '../../domain/entities/item-rate.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { ItemRateModel } from '../models/item-rate.model'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; + +@Injectable() +export class ItemRateDataService extends BaseDataService { + constructor( + @InjectRepository(ItemRateModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/item-related/item-rate/data/services/item-rate-read.service.ts b/src/modules/item-related/item-rate/data/services/item-rate-read.service.ts new file mode 100644 index 0000000..3273f11 --- /dev/null +++ b/src/modules/item-related/item-rate/data/services/item-rate-read.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { ItemRateEntity } from '../../domain/entities/item-rate.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { ItemRateModel } from '../models/item-rate.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 ItemRateReadService extends BaseReadService { + constructor( + @InjectRepository(ItemRateModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/item-related/item-rate/domain/entities/event/item-rate-created.event.ts b/src/modules/item-related/item-rate/domain/entities/event/item-rate-created.event.ts new file mode 100644 index 0000000..72232fc --- /dev/null +++ b/src/modules/item-related/item-rate/domain/entities/event/item-rate-created.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class ItemRateCreatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/item-rate/domain/entities/event/item-rate-deleted.event.ts b/src/modules/item-related/item-rate/domain/entities/event/item-rate-deleted.event.ts new file mode 100644 index 0000000..948d976 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/entities/event/item-rate-deleted.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class ItemRateDeletedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/item-rate/domain/entities/event/item-rate-updated.event.ts b/src/modules/item-related/item-rate/domain/entities/event/item-rate-updated.event.ts new file mode 100644 index 0000000..270967e --- /dev/null +++ b/src/modules/item-related/item-rate/domain/entities/event/item-rate-updated.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class ItemRateUpdatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/item-related/item-rate/domain/entities/filter-item-rate.entity.ts b/src/modules/item-related/item-rate/domain/entities/filter-item-rate.entity.ts new file mode 100644 index 0000000..4f40752 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/entities/filter-item-rate.entity.ts @@ -0,0 +1,8 @@ +import { BaseFilterEntity } from 'src/core/modules/domain/entities/base-filter.entity'; + +export interface FilterItemRateEntity extends BaseFilterEntity { + item_ids: string[]; + season_period_ids: string[]; + start_date: Date; + end_date: Date; +} diff --git a/src/modules/item-related/item-rate/domain/entities/item-rate.entity.ts b/src/modules/item-related/item-rate/domain/entities/item-rate.entity.ts new file mode 100644 index 0000000..b290f0d --- /dev/null +++ b/src/modules/item-related/item-rate/domain/entities/item-rate.entity.ts @@ -0,0 +1,7 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; + +export interface ItemRateEntity extends BaseCoreEntity { + item_id: string; + season_period_id: string; + price: number; +} diff --git a/src/modules/item-related/item-rate/domain/usecases/item-rate-data.orchestrator.ts b/src/modules/item-related/item-rate/domain/usecases/item-rate-data.orchestrator.ts new file mode 100644 index 0000000..0745326 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/item-rate-data.orchestrator.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@nestjs/common'; +import { CreateItemRateManager } from './managers/create-item-rate.manager'; +import { ItemRateDataService } from '../../data/services/item-rate-data.service'; +import { ItemRateEntity } from '../entities/item-rate.entity'; +import { DeleteItemRateManager } from './managers/delete-item-rate.manager'; +import { UpdateItemRateManager } from './managers/update-item-rate.manager'; +import { BaseDataOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-data.orchestrator'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchDeleteItemRateManager } from './managers/batch-delete-item-rate.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class ItemRateDataOrchestrator extends BaseDataOrchestrator { + constructor( + private createManager: CreateItemRateManager, + private updateManager: UpdateItemRateManager, + private deleteManager: DeleteItemRateManager, + private batchDeleteManager: BatchDeleteItemRateManager, + private serviceData: ItemRateDataService, + ) { + super(); + } + + async create(data): Promise { + this.createManager.setData(data); + this.createManager.setService(this.serviceData, TABLE_NAME.ITEM_RATE); + await this.createManager.execute(); + return this.createManager.getResult(); + } + + async update(dataId, data): Promise { + this.updateManager.setData(dataId, data); + this.updateManager.setService(this.serviceData, TABLE_NAME.ITEM_RATE); + await this.updateManager.execute(); + return this.updateManager.getResult(); + } + + async delete(dataId): Promise { + this.deleteManager.setData(dataId); + this.deleteManager.setService(this.serviceData, TABLE_NAME.ITEM_RATE); + await this.deleteManager.execute(); + return this.deleteManager.getResult(); + } + + async batchDelete(dataIds: string[]): Promise { + this.batchDeleteManager.setData(dataIds); + this.batchDeleteManager.setService(this.serviceData, TABLE_NAME.ITEM_RATE); + await this.batchDeleteManager.execute(); + return this.batchDeleteManager.getResult(); + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/item-rate-read.orchestrator.ts b/src/modules/item-related/item-rate/domain/usecases/item-rate-read.orchestrator.ts new file mode 100644 index 0000000..aa25a00 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/item-rate-read.orchestrator.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@nestjs/common'; +import { IndexItemRateManager } from './managers/index-item-rate.manager'; +import { ItemRateReadService } from '../../data/services/item-rate-read.service'; +import { ItemRateEntity } from '../entities/item-rate.entity'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator'; +import { DetailItemRateManager } from './managers/detail-item-rate.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { ItemReadService } from 'src/modules/item-related/item/data/services/item-read.service'; +import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity'; + +@Injectable() +export class ItemRateReadOrchestrator extends BaseReadOrchestrator { + constructor( + private indexManager: IndexItemRateManager, + private detailManager: DetailItemRateManager, + private serviceData: ItemRateReadService, + private itemServiceRead: ItemReadService, + ) { + super(); + } + + // untuk sementara function ini tidak terpakai + async index(params): Promise> { + // this.indexManager.setFilterParam(params); + // this.indexManager.setService(this.itemServiceRead, TABLE_NAME.ITEM_RATE); + // await this.indexManager.execute(); + // return this.indexManager.getResult(); + return + } + + async indexItem(params): Promise> { + params.ia_all = true; + + this.indexManager.setFilterParam(params); + this.indexManager.setService(this.itemServiceRead, TABLE_NAME.ITEM); + await this.indexManager.execute(); + return this.indexManager.getResult(); + } + + async detail(dataId: string): Promise { + this.detailManager.setData(dataId); + this.detailManager.setService(this.serviceData, TABLE_NAME.ITEM_RATE); + await this.detailManager.execute(); + return this.detailManager.getResult(); + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/batch-delete-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/batch-delete-item-rate.manager.ts new file mode 100644 index 0000000..f8f1e2f --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/managers/batch-delete-item-rate.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchDeleteManager } from 'src/core/modules/domain/usecase/managers/base-batch-delete.manager'; +import { ItemRateEntity } from '../../entities/item-rate.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { ItemRateModel } from '../../../data/models/item-rate.model'; +import { ItemRateDeletedEvent } from '../../entities/event/item-rate-deleted.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchDeleteItemRateManager extends BaseBatchDeleteManager { + async beforeProcess(): Promise { + return; + } + + async validateData(data: ItemRateEntity): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return ItemRateModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: ItemRateDeletedEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/create-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/create-item-rate.manager.ts new file mode 100644 index 0000000..31d1453 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/managers/create-item-rate.manager.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@nestjs/common'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { ItemRateEntity } from '../../entities/item-rate.entity'; +import { ItemRateModel } from '../../../data/models/item-rate.model'; +import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager'; +import { ItemRateCreatedEvent } from '../../entities/event/item-rate-created.event'; + +@Injectable() +export class CreateItemRateManager extends BaseCreateManager { + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return []; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: ItemRateCreatedEvent, + data: this.data, + }, + ]; + } + + get entityTarget(): any { + return ItemRateModel; + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/delete-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/delete-item-rate.manager.ts new file mode 100644 index 0000000..2e0b3dc --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/managers/delete-item-rate.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; +import { ItemRateEntity } from '../../entities/item-rate.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { ItemRateModel } from '../../../data/models/item-rate.model'; +import { ItemRateDeletedEvent } from '../../entities/event/item-rate-deleted.event'; + +@Injectable() +export class DeleteItemRateManager 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 ItemRateModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: ItemRateDeletedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/detail-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/detail-item-rate.manager.ts new file mode 100644 index 0000000..1d20cb1 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/managers/detail-item-rate.manager.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDetailManager } from 'src/core/modules/domain/usecase/managers/base-detail.manager'; +import { ItemRateEntity } from '../../entities/item-rate.entity'; +import { RelationParam } from 'src/core/modules/domain/entities/base-filter.entity'; + +@Injectable() +export class DetailItemRateManager extends BaseDetailManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: [], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: [], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return []; + } + + get setFindProperties(): any { + return { + id: this.dataId, + }; + } +} 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 new file mode 100644 index 0000000..d5c42f5 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts @@ -0,0 +1,103 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity'; + +@Injectable() +export class IndexItemRateManager extends BaseIndexManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + + this.result.data?.map(item => { + let prices = [] + for (let d = new Date(this.filterParam.start_date); d <= new Date(this.filterParam.end_date); d.setDate(d.getDate() + 1)) { + console.log(d, 'tanggal') + + const rate = item['item_rates']?.find( + rate => d >= new Date(rate.season_period.start_date) && d <= new Date(rate.season_period.end_date), + ); + + prices.push({ + date: new Date(d), + price: rate?.price ?? item.base_price, + season_type: rate?.season_period?.season_type ?? null, + holiday_name: rate?.season_period?.holiday_name ?? null, + }); + } + + delete item['item_rates']; + Object.assign(item, { + dates: prices + }) + }) + + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: [], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: [ + 'bundling_items', + 'tenant', + 'item_rates', + 'item_rates.season_period', + 'season_period.season_type' + ], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${ this.tableName }.id`, + `${ this.tableName }.created_at`, + `${ this.tableName }.name`, + `${ this.tableName }.base_price`, + + 'tenant.id', + 'tenant.name', + + 'bundling_items.id', + 'bundling_items.name', + + 'item_rates.id', + 'item_rates.price', + + 'season_period.id', + 'season_period.holiday_name', + 'season_period.start_date', + 'season_period.end_date', + + 'season_type.id', + 'season_type.name', + ]; + } + + get specificFilter(): Param[] { + return []; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + + return queryBuilder; + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/update-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/update-item-rate.manager.ts new file mode 100644 index 0000000..963ea85 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/managers/update-item-rate.manager.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager'; +import { ItemRateEntity } from '../../entities/item-rate.entity'; +import { ItemRateModel } from '../../../data/models/item-rate.model'; +import { ItemRateUpdatedEvent } from '../../entities/event/item-rate-updated.event'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; + +@Injectable() +export class UpdateItemRateManager extends BaseUpdateManager { + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return []; + } + + get entityTarget(): any { + return ItemRateModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: ItemRateUpdatedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/item-related/item-rate/index.ts b/src/modules/item-related/item-rate/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/item-related/item-rate/infrastructure/dto/filter-item-rate.dto.ts b/src/modules/item-related/item-rate/infrastructure/dto/filter-item-rate.dto.ts new file mode 100644 index 0000000..27041b5 --- /dev/null +++ b/src/modules/item-related/item-rate/infrastructure/dto/filter-item-rate.dto.ts @@ -0,0 +1,36 @@ +import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.dto'; +import { FilterItemRateEntity } from '../../domain/entities/filter-item-rate.entity'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class FilterItemRateDto + extends BaseFilterDto + implements FilterItemRateEntity { + @ApiProperty({ + required: true, + type: Date, + example: '2024-04-01' + }) + start_date: Date; + + @ApiProperty({ + required: true, + type: Date, + example: '2024-04-30' + }) + end_date: Date; + + @ApiProperty({ type: ['string'], required: false }) + @Transform((body) => { + return Array.isArray(body.value) ? body.value : [body.value]; + }) + item_ids: string[]; + + @ApiProperty({ type: ['string'], required: false }) + @Transform((body) => { + return Array.isArray(body.value) ? body.value : [body.value]; + }) + season_period_ids: string[]; + + +} diff --git a/src/modules/item-related/item-rate/infrastructure/dto/item-rate.dto.ts b/src/modules/item-related/item-rate/infrastructure/dto/item-rate.dto.ts new file mode 100644 index 0000000..29cfe14 --- /dev/null +++ b/src/modules/item-related/item-rate/infrastructure/dto/item-rate.dto.ts @@ -0,0 +1,31 @@ +import { BaseCoreDto } from 'src/core/modules/infrastructure/dto/base-core.dto'; +import { ItemRateEntity } from '../../domain/entities/item-rate.entity'; +import { ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; +import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; +import { IsObject } from 'class-validator'; + +export class ItemRateDto extends BaseCoreDto implements ItemRateEntity { + @Exclude() + item_id: string; + + @ApiProperty({ + type: [Object], + required: true, + example: { + id: 'uuid', + name: 'Entrace Ticket', + }, + }) + @IsObject() + item: ItemModel; + + @Exclude() + season_period_id: string; + + @ApiProperty({ + type: Number, + required: true, + }) + price: number; +} diff --git a/src/modules/item-related/item-rate/infrastructure/dto/update-item-rate.dto.ts b/src/modules/item-related/item-rate/infrastructure/dto/update-item-rate.dto.ts new file mode 100644 index 0000000..1b4a4ea --- /dev/null +++ b/src/modules/item-related/item-rate/infrastructure/dto/update-item-rate.dto.ts @@ -0,0 +1,18 @@ +import { BaseCoreDto } from 'src/core/modules/infrastructure/dto/base-core.dto'; +import { ItemRateEntity } from '../../domain/entities/item-rate.entity'; +import { ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; + +export class UpdateItemRateDto extends BaseCoreDto implements ItemRateEntity { + @Exclude() + item_id: string; + + @Exclude() + season_period_id: string; + + @ApiProperty({ + type: Number, + required: true, + }) + price: number; +} diff --git a/src/modules/item-related/item-rate/infrastructure/item-rate-data.controller.ts b/src/modules/item-related/item-rate/infrastructure/item-rate-data.controller.ts new file mode 100644 index 0000000..d319957 --- /dev/null +++ b/src/modules/item-related/item-rate/infrastructure/item-rate-data.controller.ts @@ -0,0 +1,44 @@ +import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common'; +import { ItemRateDataOrchestrator } from '../domain/usecases/item-rate-data.orchestrator'; +import { ItemRateDto } from './dto/item-rate.dto'; +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { ItemRateEntity } from '../domain/entities/item-rate.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.ITEM_RATE.split('-').join(' ') } - data`) +@Controller(MODULE_NAME.ITEM_RATE) +@Public(false) +@ApiBearerAuth('JWT') +export class ItemRateDataController { + constructor(private orchestrator: ItemRateDataOrchestrator) { } + + // untuk sementara, tidak dapat create + // @Post() + // async create(@Body() data: ItemRateDto): Promise { + // return await this.orchestrator.create(data); + // } + + // untuk sementara, tidak dapat delete + // @Put('/batch-delete') + // async batchDeleted(@Body() body: BatchIdsDto): Promise { + // return await this.orchestrator.batchDelete(body.ids); + // } + + // update endpoint digunakan untuk update single price + // pada halaman detail item -> list item rates (dapat update single row) + @Put(':id') + async update( + @Param('id') dataId: string, + @Body() data: ItemRateDto, + ): 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/item-rate/infrastructure/item-rate-read.controller.ts b/src/modules/item-related/item-rate/infrastructure/item-rate-read.controller.ts new file mode 100644 index 0000000..8d3aa83 --- /dev/null +++ b/src/modules/item-related/item-rate/infrastructure/item-rate-read.controller.ts @@ -0,0 +1,31 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { FilterItemRateDto } from './dto/filter-item-rate.dto'; +import { Pagination } from 'src/core/response'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { ItemRateEntity } from '../domain/entities/item-rate.entity'; +import { ItemRateReadOrchestrator } from '../domain/usecases/item-rate-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 { ItemEntity } from '../../item/domain/entities/item.entity'; + +@ApiTags(`${ MODULE_NAME.ITEM_RATE.split('-').join(' ') } - read`) +@Controller(MODULE_NAME.ITEM_RATE) +@Public(false) +@ApiBearerAuth('JWT') +export class ItemRateReadController { + constructor(private orchestrator: ItemRateReadOrchestrator) { } + + @Get() + @Pagination() + async index( + @Query() params: FilterItemRateDto, + ): Promise> { + return await this.orchestrator.indexItem(params); + } + + // @Get(':id') + // async detail(@Param('id') id: string): Promise { + // return await this.orchestrator.detail(id); + // } +} diff --git a/src/modules/item-related/item-rate/item-rate.module.ts b/src/modules/item-related/item-rate/item-rate.module.ts new file mode 100644 index 0000000..17e224f --- /dev/null +++ b/src/modules/item-related/item-rate/item-rate.module.ts @@ -0,0 +1,42 @@ +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 { ItemRateDataService } from './data/services/item-rate-data.service'; +import { ItemRateReadService } from './data/services/item-rate-read.service'; +import { ItemRateReadController } from './infrastructure/item-rate-read.controller'; +import { ItemRateReadOrchestrator } from './domain/usecases/item-rate-read.orchestrator'; +import { ItemRateDataController } from './infrastructure/item-rate-data.controller'; +import { ItemRateDataOrchestrator } from './domain/usecases/item-rate-data.orchestrator'; +import { CreateItemRateManager } from './domain/usecases/managers/create-item-rate.manager'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexItemRateManager } from './domain/usecases/managers/index-item-rate.manager'; +import { DeleteItemRateManager } from './domain/usecases/managers/delete-item-rate.manager'; +import { UpdateItemRateManager } from './domain/usecases/managers/update-item-rate.manager'; +import { DetailItemRateManager } from './domain/usecases/managers/detail-item-rate.manager'; +import { BatchDeleteItemRateManager } from './domain/usecases/managers/batch-delete-item-rate.manager'; +import { ItemRateModel } from './data/models/item-rate.model'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature([ItemRateModel], CONNECTION_NAME.DEFAULT), + CqrsModule, + ], + controllers: [ItemRateDataController, ItemRateReadController], + providers: [ + IndexItemRateManager, + DetailItemRateManager, + CreateItemRateManager, + DeleteItemRateManager, + UpdateItemRateManager, + BatchDeleteItemRateManager, + + ItemRateDataService, + ItemRateReadService, + + ItemRateDataOrchestrator, + ItemRateReadOrchestrator, + ], +}) +export class ItemRateModule {} 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 e1cd7ec..0b9bf82 100644 --- a/src/modules/item-related/item/data/models/item.model.ts +++ b/src/modules/item-related/item/data/models/item.model.ts @@ -7,12 +7,14 @@ import { JoinTable, ManyToMany, ManyToOne, + OneToMany, } from 'typeorm'; import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; import { ItemType } from 'src/modules/item-related/item-category/constants'; import { LimitType } from '../../constants'; import { ItemCategoryModel } from 'src/modules/item-related/item-category/data/models/item-category.model'; 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'; @Entity(TABLE_NAME.ITEM) export class ItemModel @@ -99,4 +101,11 @@ export class ItemModel }, }) bundling_items: ItemModel[]; + + // relasi ke item rates + @OneToMany(() => ItemRateModel, (model) => model.item, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + item_rates: ItemRateModel[]; } diff --git a/src/modules/item-related/item/domain/entities/filter-item.entity.ts b/src/modules/item-related/item/domain/entities/filter-item.entity.ts index 7d10d72..1bde43f 100644 --- a/src/modules/item-related/item/domain/entities/filter-item.entity.ts +++ b/src/modules/item-related/item/domain/entities/filter-item.entity.ts @@ -5,4 +5,5 @@ export interface FilterItemEntity extends BaseFilterEntity { item_types: string[]; limit_types: string[]; tenant_ids: string[]; + all_item: boolean; } diff --git a/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts b/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts index 8737793..72d9978 100644 --- a/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts +++ b/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts @@ -6,13 +6,19 @@ import { PaginationResponse } from 'src/core/response/domain/ok-response.interfa import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator'; import { DetailItemManager } from './managers/detail-item.manager'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { ItemRateReadService } from 'src/modules/item-related/item-rate/data/services/item-rate-read.service'; +import { FilterItemRateDto } from 'src/modules/item-related/item-rate/infrastructure/dto/filter-item-rate.dto'; +import { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entities/item-rate.entity'; +import { IndexItemRatesManager } from './managers/index-item-rates.manager'; @Injectable() export class ItemReadOrchestrator extends BaseReadOrchestrator { constructor( private indexManager: IndexItemManager, private detailManager: DetailItemManager, + private indexRateManager: IndexItemRatesManager, private serviceData: ItemReadService, + private serviceItemData: ItemRateReadService, ) { super(); } @@ -30,4 +36,16 @@ export class ItemReadOrchestrator extends BaseReadOrchestrator { await this.detailManager.execute(); return this.detailManager.getResult(); } + + async indexRate(id): Promise> { + const params = new FilterItemRateDto(); + params.item_ids = [id]; + + console.log(params, 'param') + + this.indexRateManager.setFilterParam(params); + this.indexRateManager.setService(this.serviceItemData, TABLE_NAME.ITEM); + await this.indexRateManager.execute(); + return this.indexRateManager.getResult(); + } } diff --git a/src/modules/item-related/item/domain/usecases/managers/index-item-rates.manager.ts b/src/modules/item-related/item/domain/usecases/managers/index-item-rates.manager.ts new file mode 100644 index 0000000..6c7e576 --- /dev/null +++ b/src/modules/item-related/item/domain/usecases/managers/index-item-rates.manager.ts @@ -0,0 +1,74 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entities/item-rate.entity'; + +@Injectable() +export class IndexItemRatesManager extends BaseIndexManager { + async prepareData(): Promise { + this.filterParam.order_by = `${ this.tableName }.id` + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: [], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: ['season_period', 'season_period.season_type'], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${ this.tableName }.id`, + `${ this.tableName }.price`, + + `season_period.id`, + `season_period.created_at`, + `season_period.creator_name`, + `season_period.editor_name`, + `season_period.updated_at`, + `season_period.status`, + `season_period.start_date`, + `season_period.end_date`, + `season_period.days`, + `season_period.holiday_name`, + + 'season_type.id', + 'season_type.name', + ]; + } + + get specificFilter(): Param[] { + return []; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + + if (this.filterParam.item_ids) { + queryBuilder.andWhere(`${ this.tableName }.item_id In (:...itemIds)`, { + itemIds: this.filterParam.item_ids + }) + } + return queryBuilder; + } +} 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 ffa0adb..281ab07 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 @@ -82,7 +82,7 @@ export class IndexItemManager extends BaseIndexManager { queryBuilder.andWhere(`${this.tableName}.tenant_id In (:...tenantIds)`, { tenantIds: this.filterParam.tenant_ids, }); - } else { + } else if (!this.filterParam.all_item) { queryBuilder.andWhere(`${this.tableName}.tenant_id Is Null`); } diff --git a/src/modules/item-related/item/infrastructure/dto/filter-item.dto.ts b/src/modules/item-related/item/infrastructure/dto/filter-item.dto.ts index 5270b63..5a0d70f 100644 --- a/src/modules/item-related/item/infrastructure/dto/filter-item.dto.ts +++ b/src/modules/item-related/item/infrastructure/dto/filter-item.dto.ts @@ -27,4 +27,11 @@ export class FilterItemDto extends BaseFilterDto implements FilterItemEntity { return Array.isArray(body.value) ? body.value : [body.value]; }) tenant_ids: string[]; + + @ApiProperty({ + type: Boolean, + required: false, + }) + @Transform((body) => body.value == 'true') + all_item: boolean; } diff --git a/src/modules/item-related/item/infrastructure/dto/item.dto.ts b/src/modules/item-related/item/infrastructure/dto/item.dto.ts index 65a6b88..cc2067e 100644 --- a/src/modules/item-related/item/infrastructure/dto/item.dto.ts +++ b/src/modules/item-related/item/infrastructure/dto/item.dto.ts @@ -108,7 +108,11 @@ export class ItemDto extends BaseStatusDto implements ItemEntity { example: 60, }) @IsNumber() - @ValidateIf((body) => body.item_type.toLowerCase() == ItemType.WAHANA && body.limit_type.toLowerCase() != LimitType.NO_LIMIT) + @ValidateIf( + (body) => + body.item_type.toLowerCase() == ItemType.WAHANA && + body.limit_type.toLowerCase() != LimitType.NO_LIMIT, + ) limit_value: number; @ApiProperty({ type: Boolean, required: false }) diff --git a/src/modules/item-related/item/infrastructure/item-read.controller.ts b/src/modules/item-related/item/infrastructure/item-read.controller.ts index 985f4bb..4e960aa 100644 --- a/src/modules/item-related/item/infrastructure/item-read.controller.ts +++ b/src/modules/item-related/item/infrastructure/item-read.controller.ts @@ -7,13 +7,14 @@ import { ItemReadOrchestrator } from '../domain/usecases/item-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 { ItemRateEntity } from '../../item-rate/domain/entities/item-rate.entity'; -@ApiTags(`${MODULE_NAME.ITEM.split('-').join(' ')} - read`) +@ApiTags(`${ MODULE_NAME.ITEM.split('-').join(' ') } - read`) @Controller(MODULE_NAME.ITEM) @Public(false) @ApiBearerAuth('JWT') export class ItemReadController { - constructor(private orchestrator: ItemReadOrchestrator) {} + constructor(private orchestrator: ItemReadOrchestrator) { } @Get() @Pagination() @@ -27,4 +28,13 @@ export class ItemReadController { async detail(@Param('id') id: string): Promise { return await this.orchestrator.detail(id); } + + @Get(':id/rates') + @Pagination() + async indexRate( + @Param('id') id: string + ): Promise> { + console.log(id, 'id') + return await this.orchestrator.indexRate(id); + } } diff --git a/src/modules/item-related/item/item.module.ts b/src/modules/item-related/item/item.module.ts index ed35180..9ae46da 100644 --- a/src/modules/item-related/item/item.module.ts +++ b/src/modules/item-related/item/item.module.ts @@ -22,17 +22,21 @@ import { BatchActiveItemManager } from './domain/usecases/managers/batch-active- import { BatchConfirmItemManager } from './domain/usecases/managers/batch-confirm-item.manager'; import { BatchInactiveItemManager } from './domain/usecases/managers/batch-inactive-item.manager'; import { ItemModel } from './data/models/item.model'; +import { ItemRateModel } from '../item-rate/data/models/item-rate.model'; +import { ItemRateReadService } from '../item-rate/data/services/item-rate-read.service'; +import { IndexItemRatesManager } from './domain/usecases/managers/index-item-rates.manager'; @Global() @Module({ imports: [ ConfigModule.forRoot(), - TypeOrmModule.forFeature([ItemModel], CONNECTION_NAME.DEFAULT), + TypeOrmModule.forFeature([ItemModel, ItemRateModel], CONNECTION_NAME.DEFAULT), CqrsModule, ], controllers: [ItemDataController, ItemReadController], providers: [ IndexItemManager, + IndexItemRatesManager, DetailItemManager, CreateItemManager, DeleteItemManager, @@ -47,12 +51,14 @@ import { ItemModel } from './data/models/item.model'; ItemDataService, ItemReadService, + ItemRateReadService, ItemDataOrchestrator, ItemReadOrchestrator, ], exports: [ IndexItemManager, + IndexItemRatesManager, DetailItemManager, CreateItemManager, DeleteItemManager, @@ -65,6 +71,7 @@ import { ItemModel } from './data/models/item.model'; BatchConfirmItemManager, BatchInactiveItemManager, + ItemRateReadService, ItemDataService, ItemReadService, @@ -72,4 +79,4 @@ import { ItemModel } from './data/models/item.model'; ItemReadOrchestrator, ], }) -export class ItemModule {} +export class ItemModule { } diff --git a/src/modules/season-related/season-period/constants.ts b/src/modules/season-related/season-period/constants.ts new file mode 100644 index 0000000..eb97255 --- /dev/null +++ b/src/modules/season-related/season-period/constants.ts @@ -0,0 +1,9 @@ +export enum EnumDays { + MONDAY = 'senin', + TUESDAY = 'selasa', + WEDNESDAY = 'rabu', + THURSDAY = 'kamis', + FRIDAY = 'jumat', + SATURDAY = 'sabtu', + SUNDAY = 'minggu', +} diff --git a/src/modules/season-related/season-period/data/models/season-period.model.ts b/src/modules/season-related/season-period/data/models/season-period.model.ts new file mode 100644 index 0000000..b364ee8 --- /dev/null +++ b/src/modules/season-related/season-period/data/models/season-period.model.ts @@ -0,0 +1,42 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { SeasonPeriodEntity } from '../../domain/entities/season-period.entity'; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; +import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; +import { EnumDays } from '../../constants'; +import { SeasonTypeModel } from 'src/modules/season-related/season-type/data/models/season-type.model'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; + +@Entity(TABLE_NAME.SEASON_PERIOD) +export class SeasonPeriodModel + extends BaseStatusModel + implements SeasonPeriodEntity +{ + @Column('date', { name: 'start_date', nullable: true }) + start_date: Date; + + @Column('date', { name: 'end_date', nullable: true }) + end_date: Date; + + @Column('simple-array', { name: 'days', nullable: true }) + days: EnumDays[]; + + @Column('varchar', { name: 'holiday_name', nullable: true }) + holiday_name: string; + + @Column('varchar', { name: 'season_type_id', nullable: true }) + season_type_id: string; + @ManyToOne(() => SeasonTypeModel, (model) => model.season_periods, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + @JoinColumn({ name: 'season_type_id' }) + season_type: SeasonTypeModel; + + // relasi ke item rates + @OneToMany(() => ItemRateModel, (model) => model.season_period, { + cascade: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + item_rates: ItemRateModel[]; +} diff --git a/src/modules/season-related/season-period/data/services/season-period-data.service.ts b/src/modules/season-related/season-period/data/services/season-period-data.service.ts new file mode 100644 index 0000000..f0e76cd --- /dev/null +++ b/src/modules/season-related/season-period/data/services/season-period-data.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { SeasonPeriodEntity } from '../../domain/entities/season-period.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { SeasonPeriodModel } from '../models/season-period.model'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; + +@Injectable() +export class SeasonPeriodDataService extends BaseDataService { + constructor( + @InjectRepository(SeasonPeriodModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/season-related/season-period/data/services/season-period-read.service.ts b/src/modules/season-related/season-period/data/services/season-period-read.service.ts new file mode 100644 index 0000000..8384f0a --- /dev/null +++ b/src/modules/season-related/season-period/data/services/season-period-read.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { SeasonPeriodEntity } from '../../domain/entities/season-period.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { SeasonPeriodModel } from '../models/season-period.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 SeasonPeriodReadService extends BaseReadService { + constructor( + @InjectRepository(SeasonPeriodModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/season-related/season-period/domain/entities/event/season-period-change-status.event.ts b/src/modules/season-related/season-period/domain/entities/event/season-period-change-status.event.ts new file mode 100644 index 0000000..b470de9 --- /dev/null +++ b/src/modules/season-related/season-period/domain/entities/event/season-period-change-status.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class SeasonPeriodChangeStatusEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/season-related/season-period/domain/entities/event/season-period-created.event.ts b/src/modules/season-related/season-period/domain/entities/event/season-period-created.event.ts new file mode 100644 index 0000000..85efc72 --- /dev/null +++ b/src/modules/season-related/season-period/domain/entities/event/season-period-created.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class SeasonPeriodCreatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/season-related/season-period/domain/entities/event/season-period-deleted.event.ts b/src/modules/season-related/season-period/domain/entities/event/season-period-deleted.event.ts new file mode 100644 index 0000000..220e025 --- /dev/null +++ b/src/modules/season-related/season-period/domain/entities/event/season-period-deleted.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class SeasonPeriodDeletedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/season-related/season-period/domain/entities/event/season-period-updated.event.ts b/src/modules/season-related/season-period/domain/entities/event/season-period-updated.event.ts new file mode 100644 index 0000000..381c333 --- /dev/null +++ b/src/modules/season-related/season-period/domain/entities/event/season-period-updated.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class SeasonPeriodUpdatedEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/season-related/season-period/domain/entities/filter-season-period.entity.ts b/src/modules/season-related/season-period/domain/entities/filter-season-period.entity.ts new file mode 100644 index 0000000..0dc158d --- /dev/null +++ b/src/modules/season-related/season-period/domain/entities/filter-season-period.entity.ts @@ -0,0 +1,9 @@ +import { BaseFilterEntity } from 'src/core/modules/domain/entities/base-filter.entity'; +import { EnumDays } from '../../constants'; + +export interface FilterSeasonPeriodEntity extends BaseFilterEntity { + start_date: Date; + end_date: Date; + holiday_names: string[]; + days: EnumDays[]; +} diff --git a/src/modules/season-related/season-period/domain/entities/season-period.entity.ts b/src/modules/season-related/season-period/domain/entities/season-period.entity.ts new file mode 100644 index 0000000..58298e5 --- /dev/null +++ b/src/modules/season-related/season-period/domain/entities/season-period.entity.ts @@ -0,0 +1,9 @@ +import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity'; +import { EnumDays } from '../../constants'; + +export interface SeasonPeriodEntity extends BaseStatusEntity { + start_date: Date; + end_date: Date; + days: EnumDays[]; + holiday_name: string; +} diff --git a/src/modules/season-related/season-period/domain/usecases/handlers/season-period-created.handler.ts b/src/modules/season-related/season-period/domain/usecases/handlers/season-period-created.handler.ts new file mode 100644 index 0000000..0ed9424 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/handlers/season-period-created.handler.ts @@ -0,0 +1,51 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { SeasonPeriodCreatedEvent } from '../../entities/event/season-period-created.event'; +import { SeasonPeriodDataService } from '../../../data/services/season-period-data.service'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; + +@EventsHandler(SeasonPeriodCreatedEvent) +export class SeasonPeriodHolidayHandler + implements IEventHandler +{ + constructor(private dataService: SeasonPeriodDataService) {} + + async handle(event: SeasonPeriodCreatedEvent) { + const queryRunner = this.dataService + .getRepository() + .manager.connection.createQueryRunner(); + const holidayDates = []; + + if (event.data.data.holidays?.length) { + // foreach holiday + for (const holiday of event.data.data.holidays) { + const holidayDate = new SeasonPeriodModel(); + + holidayDate.holiday_name = holiday.holiday_name; + holidayDate.start_date = holiday.start_date; + holidayDate.end_date = holiday.end_date; + holidayDate.created_at = event.data.data.created_at; + holidayDate.creator_id = event.data.data.creator_id; + holidayDate.creator_name = event.data.data.creator_name; + holidayDate.updated_at = event.data.data.updated_at; + holidayDate.season_type = event.data.data.season_type; + holidayDate.item_rates = event.data.data.item_rates; + + holidayDates.push(holidayDate); + } + + // create batch + await this.dataService.createBatch( + queryRunner, + SeasonPeriodModel, + holidayDates, + ); + + // delete data + await this.dataService.deleteById( + queryRunner, + SeasonPeriodModel, + event.data.id, + ); + } + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/active-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/active-season-period.manager.ts new file mode 100644 index 0000000..e0ab96a --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/active-season-period.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 { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodChangeStatusEvent } from '../../entities/event/season-period-change-status.event'; + +@Injectable() +export class ActiveSeasonPeriodManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success active data ${this.result.id}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/batch-active-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/batch-active-season-period.manager.ts new file mode 100644 index 0000000..388bbd9 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/batch-active-season-period.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodChangeStatusEvent } from '../../entities/event/season-period-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchActiveSeasonPeriodManager extends BaseBatchUpdateStatusManager { + validateData(data: SeasonPeriodEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/batch-confirm-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/batch-confirm-season-period.manager.ts new file mode 100644 index 0000000..c641fe5 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/batch-confirm-season-period.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodChangeStatusEvent } from '../../entities/event/season-period-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchConfirmSeasonPeriodManager extends BaseBatchUpdateStatusManager { + validateData(data: SeasonPeriodEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/batch-delete-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/batch-delete-season-period.manager.ts new file mode 100644 index 0000000..61cdd16 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/batch-delete-season-period.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchDeleteManager } from 'src/core/modules/domain/usecase/managers/base-batch-delete.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodDeletedEvent } from '../../entities/event/season-period-deleted.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchDeleteSeasonPeriodManager extends BaseBatchDeleteManager { + async beforeProcess(): Promise { + return; + } + + async validateData(data: SeasonPeriodEntity): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodDeletedEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/batch-inactive-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/batch-inactive-season-period.manager.ts new file mode 100644 index 0000000..8b460c4 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/batch-inactive-season-period.manager.ts @@ -0,0 +1,45 @@ +import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodChangeStatusEvent } from '../../entities/event/season-period-change-status.event'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BatchInactiveSeasonPeriodManager extends BaseBatchUpdateStatusManager { + validateData(data: SeasonPeriodEntity): Promise { + return; + } + + beforeProcess(): Promise { + return; + } + + afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodChangeStatusEvent, + }, + ]; + } + + getResult(): BatchResult { + return this.result; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/confirm-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/confirm-season-period.manager.ts new file mode 100644 index 0000000..8146592 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/confirm-season-period.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 { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodChangeStatusEvent } from '../../entities/event/season-period-change-status.event'; + +@Injectable() +export class ConfirmSeasonPeriodManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success active data ${this.result.id}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodChangeStatusEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/create-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/create-season-period.manager.ts new file mode 100644 index 0000000..98a332a --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/create-season-period.manager.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@nestjs/common'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager'; +import { SeasonPeriodCreatedEvent } from '../../entities/event/season-period-created.event'; + +@Injectable() +export class CreateSeasonPeriodManager extends BaseCreateManager { + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return []; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodCreatedEvent, + data: { + ...this.data, + }, + }, + ]; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/delete-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/delete-season-period.manager.ts new file mode 100644 index 0000000..87cefe1 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/delete-season-period.manager.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodDeletedEvent } from '../../entities/event/season-period-deleted.event'; + +@Injectable() +export class DeleteSeasonPeriodManager 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 SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodDeletedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/detail-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/detail-season-period.manager.ts new file mode 100644 index 0000000..c99528c --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/detail-season-period.manager.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDetailManager } from 'src/core/modules/domain/usecase/managers/base-detail.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { RelationParam } from 'src/core/modules/domain/entities/base-filter.entity'; + +@Injectable() +export class DetailSeasonPeriodManager extends BaseDetailManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: ['season_type'], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: [], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.created_at`, + `${this.tableName}.creator_name`, + `${this.tableName}.editor_name`, + `${this.tableName}.updated_at`, + `${this.tableName}.status`, + `${this.tableName}.start_date`, + `${this.tableName}.end_date`, + `${this.tableName}.days`, + `${this.tableName}.holiday_name`, + + 'season_type.id', + 'season_type.name', + ]; + } + + get setFindProperties(): any { + return { + id: this.dataId, + }; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/inactive-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/inactive-season-period.manager.ts new file mode 100644 index 0000000..767c9eb --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/inactive-season-period.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 { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { + EventTopics, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodChangeStatusEvent } from '../../entities/event/season-period-change-status.event'; + +@Injectable() +export class InactiveSeasonPeriodManager extends BaseUpdateStatusManager { + getResult(): string { + return `Success inactive data ${this.result.id}`; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodChangeStatusEvent, + data: this.data, + }, + ]; + } +} 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 new file mode 100644 index 0000000..083c9f5 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/index-season-period-item.manager.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entities/item-rate.entity'; + +@Injectable() +export class IndexSeasonPeriodeItemManager extends BaseIndexManager { + async prepareData(): Promise { + this.filterParam.order_by = `${ this.tableName }.id` + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: [], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: ['item', 'item.item_category', 'item.bundling_items'], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${ this.tableName }.id`, + `${ this.tableName }.price`, + + `item.id`, + `item.created_at`, + `item.status`, + `item.item_type`, + `item.name`, + `item.hpp`, + `item.limit_type`, + `item.limit_value`, + `item.base_price`, + + `item_category.id`, + `item_category.name`, + + 'bundling_items.id', + 'bundling_items.name', + ]; + } + + get specificFilter(): Param[] { + return [ + { + cols: `${ this.tableName }.name`, + data: this.filterParam.names, + }, + ]; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + + if (this.filterParam.season_period_ids) { + queryBuilder.andWhere(`${ this.tableName }.season_period_id In (:...periodIds)`, { + periodIds: this.filterParam.season_period_ids + }) + } + + return queryBuilder; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/index-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/index-season-period.manager.ts new file mode 100644 index 0000000..62dec43 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/index-season-period.manager.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; + +@Injectable() +export class IndexSeasonPeriodManager extends BaseIndexManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: ['season_type'], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: [], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.created_at`, + `${this.tableName}.creator_name`, + `${this.tableName}.editor_name`, + `${this.tableName}.updated_at`, + `${this.tableName}.status`, + `${this.tableName}.start_date`, + `${this.tableName}.end_date`, + `${this.tableName}.days`, + `${this.tableName}.holiday_name`, + + 'season_type.id', + 'season_type.name', + ]; + } + + get specificFilter(): Param[] { + return [ + { + cols: `${this.tableName}.holiday_name`, + data: this.filterParam.holiday_names, + }, + ]; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + if (this.filterParam.start_date && this.filterParam.end_date) { + queryBuilder.andWhere( + `${this.tableName}.start_date BETWEEN :from AND :to`, + { + from: this.filterParam.start_date, + to: this.filterParam.end_date, + }, + ); + } + + return queryBuilder; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/managers/update-season-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/update-season-period.manager.ts new file mode 100644 index 0000000..3653184 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/update-season-period.manager.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@nestjs/common'; +import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager'; +import { SeasonPeriodEntity } from '../../entities/season-period.entity'; +import { SeasonPeriodModel } from '../../../data/models/season-period.model'; +import { SeasonPeriodUpdatedEvent } from '../../entities/event/season-period-updated.event'; +import { + EventTopics, + columnUniques, + validateRelations, +} from 'src/core/strings/constants/interface.constants'; + +@Injectable() +export class UpdateSeasonPeriodManager extends BaseUpdateManager { + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get validateRelations(): validateRelations[] { + return []; + } + + get uniqueColumns(): columnUniques[] { + return []; + } + + get entityTarget(): any { + return SeasonPeriodModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: SeasonPeriodUpdatedEvent, + data: this.data, + }, + ]; + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/season-period-data.orchestrator.ts b/src/modules/season-related/season-period/domain/usecases/season-period-data.orchestrator.ts new file mode 100644 index 0000000..804ddaf --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/season-period-data.orchestrator.ts @@ -0,0 +1,118 @@ +import { Injectable } from '@nestjs/common'; +import { CreateSeasonPeriodManager } from './managers/create-season-period.manager'; +import { SeasonPeriodDataService } from '../../data/services/season-period-data.service'; +import { SeasonPeriodEntity } from '../entities/season-period.entity'; +import { DeleteSeasonPeriodManager } from './managers/delete-season-period.manager'; +import { UpdateSeasonPeriodManager } from './managers/update-season-period.manager'; +import { BaseDataTransactionOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-data-transaction.orchestrator'; +import { ActiveSeasonPeriodManager } from './managers/active-season-period.manager'; +import { InactiveSeasonPeriodManager } from './managers/inactive-season-period.manager'; +import { ConfirmSeasonPeriodManager } from './managers/confirm-season-period.manager'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { BatchResult } from 'src/core/response/domain/ok-response.interface'; +import { BatchConfirmSeasonPeriodManager } from './managers/batch-confirm-season-period.manager'; +import { BatchInactiveSeasonPeriodManager } from './managers/batch-inactive-season-period.manager'; +import { BatchActiveSeasonPeriodManager } from './managers/batch-active-season-period.manager'; +import { BatchDeleteSeasonPeriodManager } from './managers/batch-delete-season-period.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class SeasonPeriodDataOrchestrator extends BaseDataTransactionOrchestrator { + constructor( + private createManager: CreateSeasonPeriodManager, + private updateManager: UpdateSeasonPeriodManager, + private deleteManager: DeleteSeasonPeriodManager, + private activeManager: ActiveSeasonPeriodManager, + private confirmManager: ConfirmSeasonPeriodManager, + private inactiveManager: InactiveSeasonPeriodManager, + private batchDeleteManager: BatchDeleteSeasonPeriodManager, + private batchActiveManager: BatchActiveSeasonPeriodManager, + private batchConfirmManager: BatchConfirmSeasonPeriodManager, + private batchInactiveManager: BatchInactiveSeasonPeriodManager, + private serviceData: SeasonPeriodDataService, + ) { + super(); + } + + async create(data): Promise { + this.createManager.setData(data); + this.createManager.setService(this.serviceData, TABLE_NAME.SEASON_PERIOD); + await this.createManager.execute(); + return this.createManager.getResult(); + } + + async update(dataId, data): Promise { + this.updateManager.setData(dataId, data); + this.updateManager.setService(this.serviceData, TABLE_NAME.SEASON_PERIOD); + await this.updateManager.execute(); + return this.updateManager.getResult(); + } + + async delete(dataId): Promise { + this.deleteManager.setData(dataId); + this.deleteManager.setService(this.serviceData, TABLE_NAME.SEASON_PERIOD); + await this.deleteManager.execute(); + return this.deleteManager.getResult(); + } + + async batchDelete(dataIds: string[]): Promise { + this.batchDeleteManager.setData(dataIds); + this.batchDeleteManager.setService( + this.serviceData, + TABLE_NAME.SEASON_PERIOD, + ); + 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.SEASON_PERIOD); + 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.SEASON_PERIOD, + ); + 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.SEASON_PERIOD); + 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.SEASON_PERIOD, + ); + 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.SEASON_PERIOD); + 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.SEASON_PERIOD, + ); + await this.batchInactiveManager.execute(); + return this.batchInactiveManager.getResult(); + } +} diff --git a/src/modules/season-related/season-period/domain/usecases/season-period-read.orchestrator.ts b/src/modules/season-related/season-period/domain/usecases/season-period-read.orchestrator.ts new file mode 100644 index 0000000..551f71d --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/season-period-read.orchestrator.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; +import { IndexSeasonPeriodManager } from './managers/index-season-period.manager'; +import { SeasonPeriodReadService } from '../../data/services/season-period-read.service'; +import { SeasonPeriodEntity } from '../entities/season-period.entity'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator'; +import { DetailSeasonPeriodManager } from './managers/detail-season-period.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { IndexSeasonPeriodeItemManager } from './managers/index-season-period-item.manager'; +import { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entities/item-rate.entity'; +import { FilterItemRateDto } from 'src/modules/item-related/item-rate/infrastructure/dto/filter-item-rate.dto'; +import { ItemRateReadService } from 'src/modules/item-related/item-rate/data/services/item-rate-read.service'; + +@Injectable() +export class SeasonPeriodReadOrchestrator extends BaseReadOrchestrator { + constructor( + private indexManager: IndexSeasonPeriodManager, + private detailManager: DetailSeasonPeriodManager, + private indexItemManager: IndexSeasonPeriodeItemManager, + private serviceData: SeasonPeriodReadService, + private itemServiceRead: ItemRateReadService, + ) { + super(); + } + + async index(params): Promise> { + this.indexManager.setFilterParam(params); + this.indexManager.setService(this.serviceData, TABLE_NAME.SEASON_PERIOD); + await this.indexManager.execute(); + return this.indexManager.getResult(); + } + + async detail(dataId: string): Promise { + this.detailManager.setData(dataId); + this.detailManager.setService(this.serviceData, TABLE_NAME.SEASON_PERIOD); + await this.detailManager.execute(); + return this.detailManager.getResult(); + } + + async indexItem(id): Promise> { + const params = new FilterItemRateDto(); + params.season_period_ids = [id]; + + this.indexItemManager.setFilterParam(params); + this.indexItemManager.setService(this.itemServiceRead, TABLE_NAME.SEASON_PERIOD); + await this.indexItemManager.execute(); + return this.indexItemManager.getResult(); + } +} diff --git a/src/modules/season-related/season-period/index.ts b/src/modules/season-related/season-period/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/season-related/season-period/infrastructure/dto/filter-season-period.dto.ts b/src/modules/season-related/season-period/infrastructure/dto/filter-season-period.dto.ts new file mode 100644 index 0000000..b2a438a --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/dto/filter-season-period.dto.ts @@ -0,0 +1,28 @@ +import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.dto'; +import { FilterSeasonPeriodEntity } from '../../domain/entities/filter-season-period.entity'; +import { EnumDays } from '../../constants'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class FilterSeasonPeriodDto + extends BaseFilterDto + implements FilterSeasonPeriodEntity +{ + @ApiProperty({ type: Date, required: false }) + start_date: Date; + + @ApiProperty({ type: Date, required: false }) + end_date: Date; + + @ApiProperty({ type: ['string'], required: false }) + @Transform((body) => { + return Array.isArray(body.value) ? body.value : [body.value]; + }) + holiday_names: string[]; + + @ApiProperty({ type: ['string'], required: false }) + @Transform((body) => { + return Array.isArray(body.value) ? body.value : [body.value]; + }) + days: EnumDays[]; +} diff --git a/src/modules/season-related/season-period/infrastructure/dto/season-period-holiday.dto.ts b/src/modules/season-related/season-period/infrastructure/dto/season-period-holiday.dto.ts new file mode 100644 index 0000000..db9aa24 --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/dto/season-period-holiday.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class SeasonPeriodHolidayDto { + @ApiProperty({ + type: Date, + required: true, + example: '01/01/2024', + }) + start_date: Date; + + @ApiProperty({ + type: Date, + required: true, + example: '30/12/2024', + }) + end_date: Date; + + @ApiProperty({ + type: Date, + required: true, + example: 'Hari Raya', + }) + holiday_name: string; +} diff --git a/src/modules/season-related/season-period/infrastructure/dto/season-period.dto.ts b/src/modules/season-related/season-period/infrastructure/dto/season-period.dto.ts new file mode 100644 index 0000000..241a3fe --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/dto/season-period.dto.ts @@ -0,0 +1,80 @@ +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { SeasonPeriodEntity } from '../../domain/entities/season-period.entity'; +import { EnumDays } from '../../constants'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsObject, ValidateIf, ValidateNested } from 'class-validator'; +import { Exclude } from 'class-transformer'; +import { SeasonTypeModel } from 'src/modules/season-related/season-type/data/models/season-type.model'; +import { SeasonPeriodHolidayDto } from './season-period-holiday.dto'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; +import { ItemDto } from 'src/modules/item-related/item/infrastructure/dto/item.dto'; + +export class SeasonPeriodDto + extends BaseStatusDto + implements SeasonPeriodEntity +{ + @ApiProperty({ + type: Object, + required: true, + example: { + id: 'uuid', + name: 'High Season', + }, + }) + @IsObject() + season_type: SeasonTypeModel; + + @ApiProperty({ + type: Date, + required: true, + example: '2024/01/01', + }) + start_date: Date; + + @ApiProperty({ + type: Date, + required: true, + example: '2024/12/30', + }) + end_date: Date; + + @ApiProperty({ + type: [String], + required: false, + example: [EnumDays.FRIDAY, EnumDays.MONDAY], + }) + @ValidateIf((body) => body.days) + days: EnumDays[]; + + @ApiProperty({ + type: [SeasonPeriodHolidayDto], + required: false, + example: [ + { + start_date: '2024/01/01', + end_date: '2024/12/30', + holiday_name: 'Hari Raya', + }, + ], + }) + @ValidateNested({ each: true }) + @ValidateIf((body) => body.holidays) + holidays: SeasonPeriodHolidayDto[]; + + @Exclude() + holiday_name: string; + + @ApiProperty({ + type: [Object], + example: [ + { + item: { + id: 'uuid', + name: 'Entrance Ticket', + }, + price: 10000, + }, + ], + }) + item_rates: ItemRateModel[]; +} diff --git a/src/modules/season-related/season-period/infrastructure/dto/update-season-period-item.dto.ts b/src/modules/season-related/season-period/infrastructure/dto/update-season-period-item.dto.ts new file mode 100644 index 0000000..7fb5052 --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/dto/update-season-period-item.dto.ts @@ -0,0 +1,46 @@ +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { SeasonPeriodEntity } from '../../domain/entities/season-period.entity'; +import { EnumDays } from '../../constants'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsObject, ValidateIf, ValidateNested } from 'class-validator'; +import { Exclude } from 'class-transformer'; +import { SeasonTypeModel } from 'src/modules/season-related/season-type/data/models/season-type.model'; +import { SeasonPeriodHolidayDto } from './season-period-holiday.dto'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; + +export class UpdateSeasonPeriodItemDto + extends BaseStatusDto + implements SeasonPeriodEntity +{ + @Exclude() + season_type: SeasonTypeModel; + + @Exclude() + start_date: Date; + + @Exclude() + end_date: Date; + + @Exclude() + days: EnumDays[]; + + @Exclude() + holidays: SeasonPeriodHolidayDto[]; + + @Exclude() + holiday_name: string; + + @ApiProperty({ + type: [Object], + example: [ + { + item: { + id: 'uuid', + name: 'Entrance Ticket', + }, + price: 10000, + }, + ], + }) + item_rates: ItemRateModel[]; +} diff --git a/src/modules/season-related/season-period/infrastructure/dto/update-season-period.dto.ts b/src/modules/season-related/season-period/infrastructure/dto/update-season-period.dto.ts new file mode 100644 index 0000000..e383b62 --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/dto/update-season-period.dto.ts @@ -0,0 +1,60 @@ +import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; +import { SeasonPeriodEntity } from '../../domain/entities/season-period.entity'; +import { EnumDays } from '../../constants'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsObject, ValidateIf } from 'class-validator'; +import { SeasonTypeModel } from 'src/modules/season-related/season-type/data/models/season-type.model'; +import { Exclude } from 'class-transformer'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; +import { SeasonPeriodHolidayDto } from './season-period-holiday.dto'; + +export class UpdateSeasonPeriodDto + extends BaseStatusDto + implements SeasonPeriodEntity +{ + @ApiProperty({ + type: Object, + required: true, + example: { + id: 'uuid', + name: 'High Season', + }, + }) + @IsObject() + season_type: SeasonTypeModel; + + @ApiProperty({ + type: Date, + required: true, + example: '01/01/2024', + }) + start_date: Date; + + @ApiProperty({ + type: Date, + required: true, + example: '30/12/2024', + }) + end_date: Date; + + @ApiProperty({ + type: [String], + required: false, + example: [EnumDays.FRIDAY, EnumDays.MONDAY], + }) + @ValidateIf((body) => body.days) + days: EnumDays[]; + + @ApiProperty({ + type: String, + required: false, + example: 'Hari Raya', + }) + holiday_name: string; + + @Exclude() + holidays: SeasonPeriodHolidayDto[]; + + @Exclude() + item_rates: any[]; +} diff --git a/src/modules/season-related/season-period/infrastructure/season-period-data.controller.ts b/src/modules/season-related/season-period/infrastructure/season-period-data.controller.ts new file mode 100644 index 0000000..c6f4f05 --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/season-period-data.controller.ts @@ -0,0 +1,90 @@ +import { + Body, + Controller, + Delete, + Param, + Patch, + Post, + Put, + Get, +} from '@nestjs/common'; +import { SeasonPeriodDataOrchestrator } from '../domain/usecases/season-period-data.orchestrator'; +import { SeasonPeriodDto } from './dto/season-period.dto'; +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { SeasonPeriodEntity } from '../domain/entities/season-period.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'; +import { UpdateSeasonPeriodDto } from './dto/update-season-period.dto'; +import { UpdateSeasonPeriodItemDto } from './dto/update-season-period-item.dto'; + +@ApiTags(`${ MODULE_NAME.SEASON_PERIOD.split('-').join(' ') } - data`) +@Controller(MODULE_NAME.SEASON_PERIOD) +@Public(false) +@ApiBearerAuth('JWT') +export class SeasonPeriodDataController { + constructor(private orchestrator: SeasonPeriodDataOrchestrator) { } + + @Post() + async create(@Body() data: SeasonPeriodDto): 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: UpdateSeasonPeriodDto, + ): Promise { + return await this.orchestrator.update(dataId, data); + } + + // pemisahan update data dengan update items dikarenakan payload (based on tampilan) berbeda + @Put(':id/items') + async updateItems( + @Param('id') dataId: string, + @Body() data: UpdateSeasonPeriodItemDto, + ): 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/season-related/season-period/infrastructure/season-period-read.controller.ts b/src/modules/season-related/season-period/infrastructure/season-period-read.controller.ts new file mode 100644 index 0000000..2fd09fb --- /dev/null +++ b/src/modules/season-related/season-period/infrastructure/season-period-read.controller.ts @@ -0,0 +1,37 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { FilterSeasonPeriodDto } from './dto/filter-season-period.dto'; +import { Pagination } from 'src/core/response'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { SeasonPeriodEntity } from '../domain/entities/season-period.entity'; +import { SeasonPeriodReadOrchestrator } from '../domain/usecases/season-period-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 { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entities/item-rate.entity'; + +@ApiTags(`${ MODULE_NAME.SEASON_PERIOD.split('-').join(' ') } - read`) +@Controller(MODULE_NAME.SEASON_PERIOD) +@Public(false) +@ApiBearerAuth('JWT') +export class SeasonPeriodReadController { + constructor(private orchestrator: SeasonPeriodReadOrchestrator) { } + + @Get() + @Pagination() + async index( + @Query() params: FilterSeasonPeriodDto, + ): Promise> { + return await this.orchestrator.index(params); + } + + @Get(':id') + async detail(@Param('id') id: string): Promise { + return await this.orchestrator.detail(id); + } + + @Get(':id/items') + @Pagination() + async indexItem(@Param('id') id: string): Promise> { + return await this.orchestrator.indexItem(id); + } +} diff --git a/src/modules/season-related/season-period/season-period.module.ts b/src/modules/season-related/season-period/season-period.module.ts new file mode 100644 index 0000000..ee0f64a --- /dev/null +++ b/src/modules/season-related/season-period/season-period.module.ts @@ -0,0 +1,62 @@ +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 { SeasonPeriodDataService } from './data/services/season-period-data.service'; +import { SeasonPeriodReadService } from './data/services/season-period-read.service'; +import { SeasonPeriodReadController } from './infrastructure/season-period-read.controller'; +import { SeasonPeriodReadOrchestrator } from './domain/usecases/season-period-read.orchestrator'; +import { SeasonPeriodDataController } from './infrastructure/season-period-data.controller'; +import { SeasonPeriodDataOrchestrator } from './domain/usecases/season-period-data.orchestrator'; +import { CreateSeasonPeriodManager } from './domain/usecases/managers/create-season-period.manager'; +import { CqrsModule } from '@nestjs/cqrs'; +import { IndexSeasonPeriodManager } from './domain/usecases/managers/index-season-period.manager'; +import { DeleteSeasonPeriodManager } from './domain/usecases/managers/delete-season-period.manager'; +import { UpdateSeasonPeriodManager } from './domain/usecases/managers/update-season-period.manager'; +import { ActiveSeasonPeriodManager } from './domain/usecases/managers/active-season-period.manager'; +import { ConfirmSeasonPeriodManager } from './domain/usecases/managers/confirm-season-period.manager'; +import { InactiveSeasonPeriodManager } from './domain/usecases/managers/inactive-season-period.manager'; +import { DetailSeasonPeriodManager } from './domain/usecases/managers/detail-season-period.manager'; +import { BatchDeleteSeasonPeriodManager } from './domain/usecases/managers/batch-delete-season-period.manager'; +import { BatchActiveSeasonPeriodManager } from './domain/usecases/managers/batch-active-season-period.manager'; +import { BatchConfirmSeasonPeriodManager } from './domain/usecases/managers/batch-confirm-season-period.manager'; +import { BatchInactiveSeasonPeriodManager } from './domain/usecases/managers/batch-inactive-season-period.manager'; +import { SeasonPeriodModel } from './data/models/season-period.model'; +import { SeasonPeriodHolidayHandler } from './domain/usecases/handlers/season-period-created.handler'; +import { IndexSeasonPeriodeItemManager } from './domain/usecases/managers/index-season-period-item.manager'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; +import { ItemRateReadService } from 'src/modules/item-related/item-rate/data/services/item-rate-read.service'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature([SeasonPeriodModel, ItemRateModel], CONNECTION_NAME.DEFAULT), + CqrsModule, + ], + controllers: [SeasonPeriodDataController, SeasonPeriodReadController], + providers: [ + SeasonPeriodHolidayHandler, + + IndexSeasonPeriodManager, + IndexSeasonPeriodeItemManager, + DetailSeasonPeriodManager, + CreateSeasonPeriodManager, + DeleteSeasonPeriodManager, + UpdateSeasonPeriodManager, + ActiveSeasonPeriodManager, + ConfirmSeasonPeriodManager, + InactiveSeasonPeriodManager, + BatchDeleteSeasonPeriodManager, + BatchActiveSeasonPeriodManager, + BatchConfirmSeasonPeriodManager, + BatchInactiveSeasonPeriodManager, + + SeasonPeriodDataService, + SeasonPeriodReadService, + ItemRateReadService, + + SeasonPeriodDataOrchestrator, + SeasonPeriodReadOrchestrator, + ], +}) +export class SeasonPeriodModule { } diff --git a/src/modules/season-related/season-type/data/models/season-type.model.ts b/src/modules/season-related/season-type/data/models/season-type.model.ts index ad26020..f8ba5f7 100644 --- a/src/modules/season-related/season-type/data/models/season-type.model.ts +++ b/src/modules/season-related/season-type/data/models/season-type.model.ts @@ -1,7 +1,8 @@ import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { SeasonTypeEntity } from '../../domain/entities/season-type.entity'; -import { Column, Entity } from 'typeorm'; +import { Column, Entity, OneToMany } from 'typeorm'; import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; +import { SeasonPeriodModel } from 'src/modules/season-related/season-period/data/models/season-period.model'; @Entity(TABLE_NAME.SEASON_TYPE) export class SeasonTypeModel @@ -10,4 +11,10 @@ export class SeasonTypeModel { @Column('varchar', { name: 'name' }) name: string; + + @OneToMany(() => SeasonPeriodModel, (model) => model.season_type, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + season_periods: SeasonPeriodModel[]; } diff --git a/src/modules/user-related/tenant/infrastructure/tenant-item-data.controller.ts b/src/modules/user-related/tenant/infrastructure/tenant-item-data.controller.ts index f9745c6..7d9bfb6 100644 --- a/src/modules/user-related/tenant/infrastructure/tenant-item-data.controller.ts +++ b/src/modules/user-related/tenant/infrastructure/tenant-item-data.controller.ts @@ -16,12 +16,12 @@ import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.e import { ItemDataOrchestrator } from 'src/modules/item-related/item/domain/usecases/item-data.orchestrator'; import { ItemDto } from 'src/modules/item-related/item/infrastructure/dto/item.dto'; -@ApiTags(`${MODULE_NAME.TENANT.split('-').join(' ')} item - data`) -@Controller(`${MODULE_NAME.TENANT}/:tenant_id/item`) +@ApiTags(`${ MODULE_NAME.TENANT.split('-').join(' ') } item - data`) +@Controller(`${ MODULE_NAME.TENANT }/:tenant_id/item`) @Public(false) @ApiBearerAuth('JWT') export class TenantItemDataController { - constructor(private orchestrator: ItemDataOrchestrator) {} + constructor(private orchestrator: ItemDataOrchestrator) { } @Post() async create( diff --git a/src/modules/user-related/tenant/infrastructure/tenant-item-read.controller.ts b/src/modules/user-related/tenant/infrastructure/tenant-item-read.controller.ts index 31c3799..ae42e37 100644 --- a/src/modules/user-related/tenant/infrastructure/tenant-item-read.controller.ts +++ b/src/modules/user-related/tenant/infrastructure/tenant-item-read.controller.ts @@ -8,12 +8,12 @@ import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.e import { ItemReadOrchestrator } from 'src/modules/item-related/item/domain/usecases/item-read.orchestrator'; import { FilterItemDto } from 'src/modules/item-related/item/infrastructure/dto/filter-item.dto'; -@ApiTags(`${MODULE_NAME.TENANT.split('-').join(' ')} item - read`) -@Controller(`${MODULE_NAME.TENANT}/:tenant_id/item`) +@ApiTags(`${ MODULE_NAME.TENANT.split('-').join(' ') } item - read`) +@Controller(`${ MODULE_NAME.TENANT }/:tenant_id/item`) @Public(false) @ApiBearerAuth('JWT') export class TenantItemReadController { - constructor(private orchestrator: ItemReadOrchestrator) {} + constructor(private orchestrator: ItemReadOrchestrator) { } @Get() @Pagination()