diff --git a/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts b/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts index 37b5a5e..503b486 100644 --- a/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts @@ -15,6 +15,26 @@ export abstract class BaseBatchUpdateStatusManager extends BaseManager { abstract get entityTarget(): any; setData(ids: string[], status: STATUS): void { + /** + * // TODO: Handle case confirm multiple tabs; + * Pola ids yang dikirim dirubah menjadi data_id___updated_at + * Untuk mendapatkan value id nya saja dan menghindari breaking change + * karena sudah digunakan sebelumnya lakukan split dari data ids yang dikirim + * Example: + * this.dataIds = ids.map((i)=> { + * return i.split('___')[0] + * }) + * + * Simpan data ids yang mempunyai update_at kedalam valiable baru + * Example: + * this.dataIdsWithDate = ids.map((i)=> { + * return { + * id: i.split('___')[0], + * updated_at: i.split('___')[1] + * } + * }) + */ + this.dataIds = ids; this.dataStatus = status; } @@ -32,8 +52,21 @@ export abstract class BaseBatchUpdateStatusManager extends BaseManager { let totalSuccess = 0; const messages = []; + /** + * // TODO: Handle case confirm multiple tabs; + * Lopping data diambil dari dataIdsWithDate + * exp: for (const item of this.dataIdsWithDate) + */ + for (const id of this.dataIds) { try { + /** + * // TODO: Handle case confirm multiple tabs; + * buat variable: + * const id = item.id + * const updated_at = item.updated_at + */ + const entity = await this.dataService.getOneByOptions({ where: { id: id, @@ -64,6 +97,14 @@ export abstract class BaseBatchUpdateStatusManager extends BaseManager { this.tableName, ).execute(); + /** + * // TODO: Handle case confirm multiple tabs; + * lakukan update data dengan where condition id dan updated_at + * EXPECTATION => status akan berubah jika updated_at yang dikirim dari FE sama dengen yang di database + * IF => updated_at beda tidak perlu melakukan update status dan tidak perlu memanggil eventBus tetapi tetap dihitung sebagai aksi yang berhasil + * IF => FE tidak menambahkan updated_at makan lakukan update dan publishEvent + */ + const result = await this.dataService.update( this.queryRunner, this.entityTarget, diff --git a/src/core/modules/domain/usecase/managers/base-update-status.manager.ts b/src/core/modules/domain/usecase/managers/base-update-status.manager.ts index 3e4e0a9..64516db 100644 --- a/src/core/modules/domain/usecase/managers/base-update-status.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-update-status.manager.ts @@ -14,6 +14,22 @@ export abstract class BaseUpdateStatusManager extends BaseManager { abstract get entityTarget(): any; setData(id: string, status: STATUS): void { + /** + * // TODO: Handle case confirm multiple tabs; + * Pola id yang dikirim dirubah menjadi data_id___updated_at + * Untuk mendapatkan value id nya saja dan menghindari breaking change + * karena sudah digunakan sebelumnya lakukan split dari data ids yang dikirim + * Example: + * this.dataId = id.split('___')[0] + * + * Simpan data id yang mempunyai update_at kedalam valiable baru + * Example: + * this.dataIdsWithDate = { + * id: id.split('___')[0], + * updated_at: id.split('___')[1] + * } + */ + this.dataId = id; this.dataStatus = status; } @@ -43,6 +59,16 @@ export abstract class BaseUpdateStatusManager extends BaseManager { this.tableName, ).execute(); + /** + * // TODO: Handle case confirm multiple tabs; + * IF => updated_at sama dengen data yang di database + * THEN => + * - Lakukan update data dengan where condition id dan updated_at + * - EXPECTATION = > status akan berubah jika updated_at yang dikirim dari FE sama dengen yang di database + * IF => updated_at beda maka retun curent data tanpa harus malakukan update status dan publish event + * IF => FE tidak menambahkan updated_at makan lakukan update dan publishEvent + */ + this.result = await this.dataService.update( this.queryRunner, this.entityTarget, diff --git a/src/modules/configuration/couch/couch.module.ts b/src/modules/configuration/couch/couch.module.ts index 75e12a6..8de39ac 100644 --- a/src/modules/configuration/couch/couch.module.ts +++ b/src/modules/configuration/couch/couch.module.ts @@ -43,6 +43,13 @@ import { TransactionModel } from 'src/modules/transaction/transaction/data/model import { TransactionTaxModel } from 'src/modules/transaction/transaction/data/models/transaction-tax.model'; import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model'; import { VipCodeCreatedHandler } from './domain/managers/vip-code.handler'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; +import { + SeasonTypeDeletedHandler, + SeasonTypeUpdatedHandler, +} from './domain/managers/season-type.handler'; +import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service'; +import { SeasonPeriodModel } from 'src/modules/season-related/season-period/data/models/season-period.model'; @Module({ imports: [ @@ -50,6 +57,8 @@ import { VipCodeCreatedHandler } from './domain/managers/vip-code.handler'; TypeOrmModule.forFeature( [ ItemModel, + ItemRateModel, + SeasonPeriodModel, UserModel, TransactionModel, TransactionTaxModel, @@ -80,6 +89,10 @@ import { VipCodeCreatedHandler } from './domain/managers/vip-code.handler'; UserUpdatedHandler, UserPrivilegeUpdateHandler, + SeasonTypeDeletedHandler, + SeasonTypeUpdatedHandler, + + SeasonPeriodDataService, TransactionDataService, UserDataService, ItemDataService, diff --git a/src/modules/configuration/couch/domain/managers/season-type.handler.ts b/src/modules/configuration/couch/domain/managers/season-type.handler.ts new file mode 100644 index 0000000..811f6c3 --- /dev/null +++ b/src/modules/configuration/couch/domain/managers/season-type.handler.ts @@ -0,0 +1,52 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { CouchService } from '../../data/services/couch.service'; +import { SeasonTypeDeletedEvent } from 'src/modules/season-related/season-type/domain/entities/event/season-type-deleted.event'; +import { SeasonTypeChangeStatusEvent } from 'src/modules/season-related/season-type/domain/entities/event/season-type-change-status.event'; +import { SeasonTypeUpdatedEvent } from 'src/modules/season-related/season-type/domain/entities/event/season-type-updated.event'; +import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service'; + +@EventsHandler(SeasonTypeDeletedEvent) +export class SeasonTypeDeletedHandler + implements IEventHandler +{ + constructor(private couchService: CouchService) {} + + async handle(event: SeasonTypeDeletedEvent) { + console.log('deleted session type'); + } +} + +@EventsHandler(SeasonTypeChangeStatusEvent, SeasonTypeUpdatedEvent) +export class SeasonTypeUpdatedHandler + implements IEventHandler +{ + constructor( + private couchService: CouchService, + private seasonPeriodService: SeasonPeriodDataService, + ) {} + + async handle(event: SeasonTypeChangeStatusEvent) { + const data = event.data.data; + const typeID = data.id; + const periods = await this.seasonPeriodService.getManyByOptions({ + where: { + season_type_id: typeID, + }, + relations: ['season_type'], + }); + + for (const period of periods) { + const dataID = period.id; + const couchData = await this.couchService.getDoc(dataID, 'season_period'); + if (couchData) { + await this.couchService.updateDoc( + { + _id: dataID, + ...period, + }, + 'season_period', + ); + } + } + } +} diff --git a/src/modules/item-related/item/data/services/item-data.service.ts b/src/modules/item-related/item/data/services/item-data.service.ts index 367a883..534d542 100644 --- a/src/modules/item-related/item/data/services/item-data.service.ts +++ b/src/modules/item-related/item/data/services/item-data.service.ts @@ -5,13 +5,30 @@ import { InjectRepository } from '@nestjs/typeorm'; import { ItemModel } from '../models/item.model'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; import { Repository } from 'typeorm'; +import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model'; @Injectable() export class ItemDataService extends BaseDataService { constructor( @InjectRepository(ItemModel, CONNECTION_NAME.DEFAULT) private repo: Repository, + + @InjectRepository(ItemRateModel, CONNECTION_NAME.DEFAULT) + private repoItemRate: Repository, ) { super(repo); } + + async updateItemRatePrice( + oldPrice: number, + newPrice: number, + itemId: string, + ): Promise { + console.log({ oldPrice, newPrice, itemId }); + + this.repoItemRate.update( + { item_id: itemId, price: oldPrice }, + { price: newPrice }, + ); + } } diff --git a/src/modules/item-related/item/domain/usecases/managers/update-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/update-item.manager.ts index d4b2f40..12d972a 100644 --- a/src/modules/item-related/item/domain/usecases/managers/update-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/update-item.manager.ts @@ -11,15 +11,26 @@ import { @Injectable() export class UpdateItemManager extends BaseUpdateManager { + protected oldBasePrice: number; + async validateProcess(): Promise { return; } async beforeProcess(): Promise { + this.oldBasePrice = this.oldData.base_price; return; } async afterProcess(): Promise { + const newBasePrice = this.result.base_price; + const itemId = this.result.id; + + await this.dataService.updateItemRatePrice( + this.oldBasePrice, + newBasePrice, + itemId, + ); return; } diff --git a/src/modules/season-related/season-type/domain/usecases/managers/batch-delete-season-type.manager.ts b/src/modules/season-related/season-type/domain/usecases/managers/batch-delete-season-type.manager.ts index c046507..d000785 100644 --- a/src/modules/season-related/season-type/domain/usecases/managers/batch-delete-season-type.manager.ts +++ b/src/modules/season-related/season-type/domain/usecases/managers/batch-delete-season-type.manager.ts @@ -7,7 +7,11 @@ import { import { SeasonTypeModel } from '../../../data/models/season-type.model'; import { SeasonTypeDeletedEvent } from '../../entities/event/season-type-deleted.event'; import { BatchResult } from 'src/core/response/domain/ok-response.interface'; -import { Injectable } from '@nestjs/common'; +import { + HttpStatus, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; @Injectable() export class BatchDeleteSeasonTypeManager extends BaseBatchDeleteManager { @@ -16,6 +20,19 @@ export class BatchDeleteSeasonTypeManager extends BaseBatchDeleteManager { + const relationData = await this.dataServiceFirstOpt.getOneByOptions({ + where: { + season_type_id: data.id, + }, + }); + + if (relationData) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Gagagl! data sudah berelasi dengen season period.`, + error: 'Unprocessable Entity', + }); + } return; } diff --git a/src/modules/season-related/season-type/domain/usecases/managers/batch-inactive-season-type.manager.ts b/src/modules/season-related/season-type/domain/usecases/managers/batch-inactive-season-type.manager.ts index 6d6ea72..5bd5248 100644 --- a/src/modules/season-related/season-type/domain/usecases/managers/batch-inactive-season-type.manager.ts +++ b/src/modules/season-related/season-type/domain/usecases/managers/batch-inactive-season-type.manager.ts @@ -7,11 +7,28 @@ import { import { SeasonTypeModel } from '../../../data/models/season-type.model'; import { SeasonTypeChangeStatusEvent } from '../../entities/event/season-type-change-status.event'; import { BatchResult } from 'src/core/response/domain/ok-response.interface'; -import { Injectable } from '@nestjs/common'; +import { + HttpStatus, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; @Injectable() export class BatchInactiveSeasonTypeManager extends BaseBatchUpdateStatusManager { - validateData(data: SeasonTypeEntity): Promise { + async validateData(data: SeasonTypeEntity): Promise { + const relationData = await this.dataServiceFirstOpt.getOneByOptions({ + where: { + season_type_id: data.id, + }, + }); + + if (relationData) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Gagagl! data sudah berelasi dengen season period.`, + error: 'Unprocessable Entity', + }); + } return; } diff --git a/src/modules/season-related/season-type/domain/usecases/managers/delete-season-type.manager.ts b/src/modules/season-related/season-type/domain/usecases/managers/delete-season-type.manager.ts index deffb5c..b392efc 100644 --- a/src/modules/season-related/season-type/domain/usecases/managers/delete-season-type.manager.ts +++ b/src/modules/season-related/season-type/domain/usecases/managers/delete-season-type.manager.ts @@ -1,4 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { + HttpStatus, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; import { SeasonTypeEntity } from '../../entities/season-type.entity'; import { @@ -15,6 +19,19 @@ export class DeleteSeasonTypeManager extends BaseDeleteManager } async validateProcess(): Promise { + const relationData = await this.dataServiceFirstOpt.getOneByOptions({ + where: { + season_type_id: this.dataId, + }, + }); + + if (relationData) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Gagagl! data sudah berelasi dengen season period.`, + error: 'Unprocessable Entity', + }); + } return; } diff --git a/src/modules/season-related/season-type/domain/usecases/managers/inactive-season-type.manager.ts b/src/modules/season-related/season-type/domain/usecases/managers/inactive-season-type.manager.ts index a5a1c05..ea2ead8 100644 --- a/src/modules/season-related/season-type/domain/usecases/managers/inactive-season-type.manager.ts +++ b/src/modules/season-related/season-type/domain/usecases/managers/inactive-season-type.manager.ts @@ -1,4 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { + HttpStatus, + Injectable, + UnprocessableEntityException, +} from '@nestjs/common'; import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager'; import { SeasonTypeEntity } from '../../entities/season-type.entity'; import { @@ -15,6 +19,20 @@ export class InactiveSeasonTypeManager extends BaseUpdateStatusManager { + const relationData = await this.dataServiceFirstOpt.getOneByOptions({ + where: { + season_type_id: this.dataId, + }, + }); + + if (relationData) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Gagagl! data sudah berelasi dengen season period.`, + error: 'Unprocessable Entity', + }); + } + return; } diff --git a/src/modules/season-related/season-type/domain/usecases/season-type-data.orchestrator.ts b/src/modules/season-related/season-type/domain/usecases/season-type-data.orchestrator.ts index 52a07fa..5477cb0 100644 --- a/src/modules/season-related/season-type/domain/usecases/season-type-data.orchestrator.ts +++ b/src/modules/season-related/season-type/domain/usecases/season-type-data.orchestrator.ts @@ -15,6 +15,7 @@ import { BatchInactiveSeasonTypeManager } from './managers/batch-inactive-season import { BatchActiveSeasonTypeManager } from './managers/batch-active-season-type.manager'; import { BatchDeleteSeasonTypeManager } from './managers/batch-delete-season-type.manager'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service'; @Injectable() export class SeasonTypeDataOrchestrator extends BaseDataTransactionOrchestrator { @@ -30,6 +31,7 @@ export class SeasonTypeDataOrchestrator extends BaseDataTransactionOrchestrator< private batchConfirmManager: BatchConfirmSeasonTypeManager, private batchInactiveManager: BatchInactiveSeasonTypeManager, private serviceData: SeasonTypeDataService, + private servicePeriodData: SeasonPeriodDataService, ) { super(); } @@ -50,7 +52,11 @@ export class SeasonTypeDataOrchestrator extends BaseDataTransactionOrchestrator< async delete(dataId): Promise { this.deleteManager.setData(dataId); - this.deleteManager.setService(this.serviceData, TABLE_NAME.SEASON_TYPE); + this.deleteManager.setService( + this.serviceData, + TABLE_NAME.SEASON_TYPE, + this.servicePeriodData, + ); await this.deleteManager.execute(); return this.deleteManager.getResult(); } @@ -60,6 +66,7 @@ export class SeasonTypeDataOrchestrator extends BaseDataTransactionOrchestrator< this.batchDeleteManager.setService( this.serviceData, TABLE_NAME.SEASON_TYPE, + this.servicePeriodData, ); await this.batchDeleteManager.execute(); return this.batchDeleteManager.getResult(); @@ -101,7 +108,11 @@ export class SeasonTypeDataOrchestrator extends BaseDataTransactionOrchestrator< async inactive(dataId): Promise { this.inactiveManager.setData(dataId, STATUS.INACTIVE); - this.inactiveManager.setService(this.serviceData, TABLE_NAME.SEASON_TYPE); + this.inactiveManager.setService( + this.serviceData, + TABLE_NAME.SEASON_TYPE, + this.servicePeriodData, + ); await this.inactiveManager.execute(); return this.inactiveManager.getResult(); } @@ -111,6 +122,7 @@ export class SeasonTypeDataOrchestrator extends BaseDataTransactionOrchestrator< this.batchInactiveManager.setService( this.serviceData, TABLE_NAME.SEASON_TYPE, + this.servicePeriodData, ); await this.batchInactiveManager.execute(); return this.batchInactiveManager.getResult(); diff --git a/src/modules/season-related/season-type/season-type.module.ts b/src/modules/season-related/season-type/season-type.module.ts index 5ae1ef3..185e30a 100644 --- a/src/modules/season-related/season-type/season-type.module.ts +++ b/src/modules/season-related/season-type/season-type.module.ts @@ -22,11 +22,16 @@ import { BatchActiveSeasonTypeManager } from './domain/usecases/managers/batch-a import { BatchConfirmSeasonTypeManager } from './domain/usecases/managers/batch-confirm-season-type.manager'; import { BatchInactiveSeasonTypeManager } from './domain/usecases/managers/batch-inactive-season-type.manager'; import { SeasonTypeModel } from './data/models/season-type.model'; +import { SeasonPeriodDataService } from '../season-period/data/services/season-period-data.service'; +import { SeasonPeriodModel } from '../season-period/data/models/season-period.model'; @Module({ imports: [ ConfigModule.forRoot(), - TypeOrmModule.forFeature([SeasonTypeModel], CONNECTION_NAME.DEFAULT), + TypeOrmModule.forFeature( + [SeasonTypeModel, SeasonPeriodModel], + CONNECTION_NAME.DEFAULT, + ), CqrsModule, ], controllers: [SeasonTypeDataController, SeasonTypeReadController], @@ -49,6 +54,7 @@ import { SeasonTypeModel } from './data/models/season-type.model'; SeasonTypeDataOrchestrator, SeasonTypeReadOrchestrator, + SeasonPeriodDataService, ], }) export class SeasonTypeModule {} diff --git a/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts index 055bad9..9872b5b 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts @@ -26,6 +26,7 @@ export class IndexTransactionManager extends BaseIndexManager ); Object.assign(item, { + refund_type: activeRefund?.type ?? null, refund_code: activeRefund?.code ?? null, refund_date: activeRefund?.refund_date ?? null, }); @@ -86,6 +87,7 @@ export class IndexTransactionManager extends BaseIndexManager `refunds.code`, `refunds.refund_date`, `refunds.request_date`, + `refunds.type`, ]; }