From cc7a345fff4d8a55409a628f82c31885f6d141fa Mon Sep 17 00:00:00 2001 From: Aswin Ashar Abdullah <98192542+Asharaswin@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:26:48 +0700 Subject: [PATCH] fix(SPG-549) BE - Validasi tgl periode unique based on level --- ...54657-add-column-priority-season-period.ts | 14 ++++++ .../data/models/season-period.model.ts | 6 ++- .../domain/entities/season-period.entity.ts | 1 + .../handlers/season-period-created.handler.ts | 3 +- .../managers/create-season-period.manager.ts | 8 +++- .../managers/helpers/validate.helper.ts | 46 +++++++++++++++++++ .../managers/update-season-period.manager.ts | 6 +++ 7 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/database/migrations/1719227554657-add-column-priority-season-period.ts create mode 100644 src/modules/season-related/season-period/domain/usecases/managers/helpers/validate.helper.ts diff --git a/src/database/migrations/1719227554657-add-column-priority-season-period.ts b/src/database/migrations/1719227554657-add-column-priority-season-period.ts new file mode 100644 index 0000000..711e155 --- /dev/null +++ b/src/database/migrations/1719227554657-add-column-priority-season-period.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddColumnPrioritySeasonPeriod1719227554657 implements MigrationInterface { + name = 'AddColumnPrioritySeasonPeriod1719227554657' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "season_periods" ADD "priority" integer NOT NULL DEFAULT '3'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "season_periods" DROP COLUMN "priority"`); + } + +} 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 index b364ee8..89902f1 100644 --- 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 @@ -9,8 +9,7 @@ import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/it @Entity(TABLE_NAME.SEASON_PERIOD) export class SeasonPeriodModel extends BaseStatusModel - implements SeasonPeriodEntity -{ + implements SeasonPeriodEntity { @Column('date', { name: 'start_date', nullable: true }) start_date: Date; @@ -23,6 +22,9 @@ export class SeasonPeriodModel @Column('varchar', { name: 'holiday_name', nullable: true }) holiday_name: string; + @Column('int', { name: 'priority', default: 3 }) + priority: number; + @Column('varchar', { name: 'season_type_id', nullable: true }) season_type_id: string; @ManyToOne(() => SeasonTypeModel, (model) => model.season_periods, { 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 index 58298e5..0a769ed 100644 --- 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 @@ -6,4 +6,5 @@ export interface SeasonPeriodEntity extends BaseStatusEntity { end_date: Date; days: EnumDays[]; holiday_name: string; + priority?: number; } 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 index 0ed9424..61e8ed9 100644 --- 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 @@ -7,7 +7,7 @@ import { SeasonPeriodModel } from '../../../data/models/season-period.model'; export class SeasonPeriodHolidayHandler implements IEventHandler { - constructor(private dataService: SeasonPeriodDataService) {} + constructor(private dataService: SeasonPeriodDataService) { } async handle(event: SeasonPeriodCreatedEvent) { const queryRunner = this.dataService @@ -29,6 +29,7 @@ export class SeasonPeriodHolidayHandler holidayDate.updated_at = event.data.data.updated_at; holidayDate.season_type = event.data.data.season_type; holidayDate.item_rates = event.data.data.item_rates; + holidayDate.priority = 1; holidayDates.push(holidayDate); } 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 index 98a332a..0fe1c41 100644 --- 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 @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { HttpStatus, Injectable, UnprocessableEntityException } from '@nestjs/common'; import { EventTopics, columnUniques, @@ -8,10 +8,16 @@ 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'; +import { ValidateSeasonPeriodHelper } from './helpers/validate.helper'; @Injectable() export class CreateSeasonPeriodManager extends BaseCreateManager { async beforeProcess(): Promise { + const priority = await ValidateSeasonPeriodHelper(this.dataService, this.data); + + Object.assign(this.data, { + priority: priority + }) return; } diff --git a/src/modules/season-related/season-period/domain/usecases/managers/helpers/validate.helper.ts b/src/modules/season-related/season-period/domain/usecases/managers/helpers/validate.helper.ts new file mode 100644 index 0000000..4e880c3 --- /dev/null +++ b/src/modules/season-related/season-period/domain/usecases/managers/helpers/validate.helper.ts @@ -0,0 +1,46 @@ +import { HttpStatus, UnprocessableEntityException } from "@nestjs/common"; + +// function ini bergungsi untuk validasi season period yang sama +export async function ValidateSeasonPeriodHelper(dataService, data) { + /* + singkatnya, ada 3 level priority season period + level 1 hari libur atau specific date ( mempunyai holiday_name atau start_date sama dengan end_date ) + level 2 specific day (array daysnya tidak kosong contoh [Senin, Selasa]) + level 3 range date ( array daynya kosong, bukan holiday ) + + priority 1,2,3 bisa saling tumpang tindih, dan diperhatikan tingkatnya ( semakin besar angkanya , maka semakin kecil priority ) + + tapi priority tidak dapat ditindih oleh season period dengan tingkat priority yang sama pada range tanggal yang sama + contohnya : + 1. Season period holiday start_date 2024-08-28, end_date 2024-08-28 tidak dapat ditindih oleh season period holiday lainnya pada range tanggal yang sama + 2. Season period start_date: 2024-08-15, end_date 2024-08-28, dan days [Senin, Selasa] tidak dapat ditindih oleh Season period 2024-07-08, 2024-08-20 days [Sabtu, Senin] + => akan tetap dapat ditindih oleh season period 2024-08-15, 2024-08-28, days [Rabu, Kamis] (karena beberapa day) + 3. Season period range 2024-08-15 hingga 2024-08-28, days [] tidak dapat ditindih oleh Season period 2024-07-08, 2024-08-20 days [] + => akan tetapi dapat ditindih oleh season period 2024-08-15, 2024-08-28 days [Sabtu, Senin] (karena ini naik prio menjadi priority 2) + */ + const query = dataService.getRepository().createQueryBuilder('data') + let priority: number = 3; + // libur / specific date + if (data.holidays?.length > 0 || data.start_date == data.end_date || data.holiday_name) priority = 1 + // specific day + else if (data.days?.length > 0) priority = 2 + + if (data.id) { + query.andWhere('data.id != :dataId', { dataId: data.id }) + } + + const datas = await query + .andWhere('data.priority = :priority', { priority: priority }) + .andWhere('data.start_date BETWEEN :inputStartDate AND :inputEndDate', { inputStartDate: data.start_date, inputEndDate: data.end_date }) + .andWhere('data.end_date BETWEEN :inputStartDate AND :inputEndDate', { inputStartDate: data.start_date, inputEndDate: data.end_date }).getCount(); + + if (datas > 0) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! there is another season period in same range date`, + error: 'Unprocessable Entity', + }); + } + + return priority; +} \ No newline at end of file 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 index 3653184..e49489e 100644 --- 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 @@ -8,10 +8,16 @@ import { columnUniques, validateRelations, } from 'src/core/strings/constants/interface.constants'; +import { ValidateSeasonPeriodHelper } from './helpers/validate.helper'; @Injectable() export class UpdateSeasonPeriodManager extends BaseUpdateManager { async validateProcess(): Promise { + const priority = await ValidateSeasonPeriodHelper(this.dataService, this.data); + + Object.assign(this.data, { + priority: priority + }) return; }