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 92f0019..79bded9 100644 --- a/src/core/modules/domain/usecase/managers/base-create.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-create.manager.ts @@ -97,7 +97,7 @@ export abstract class BaseCreateManager extends BaseManager { this.eventBus.publishAll([ new topic.topic({ - id: data?.['id'] ?? topic?.data?.['id'], + id: this.result['id'], old: null, data: data ?? topic.data, user: this.user, diff --git a/src/core/strings/constants/base.constants.ts b/src/core/strings/constants/base.constants.ts index 0ce2413..0e1f7d4 100644 --- a/src/core/strings/constants/base.constants.ts +++ b/src/core/strings/constants/base.constants.ts @@ -20,6 +20,16 @@ export enum ORDER_TYPE { DESC = 'DESC', } +export const DAY = [ + 'minggu', + 'senin', + 'selasa', + 'rabu', + 'kamis', + 'jumat', + 'sabtu', +]; + export enum CONNECTION_NAME { DEFAULT = 'default', } diff --git a/src/modules/item-related/item-rate/domain/usecases/handlers/item-created.handler.ts b/src/modules/item-related/item-rate/domain/usecases/handlers/item-created.handler.ts new file mode 100644 index 0000000..1193c03 --- /dev/null +++ b/src/modules/item-related/item-rate/domain/usecases/handlers/item-created.handler.ts @@ -0,0 +1,43 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { EMPTY_UUID } from 'src/core/strings/constants/base.constants'; +import { ItemCreatedEvent } from 'src/modules/item-related/item/domain/entities/event/item-created.event'; +import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service'; +import { Not } from 'typeorm'; +import { ItemRateModel } from '../../../data/models/item-rate.model'; +import { ItemRateDataService } from '../../../data/services/item-rate-data.service'; + +@EventsHandler(ItemCreatedEvent) +export class SeasonPeriodHolidayHandler + implements IEventHandler +{ + constructor( + private seasonService: SeasonPeriodDataService, + private dataService: ItemRateDataService, + ) {} + + async handle(event: ItemCreatedEvent) { + const rates = []; + const seasons = await this.seasonService.getManyByOptions({ + where: { + id: Not(EMPTY_UUID), + }, + }); + + const queryRunner = this.dataService + .getRepository() + .manager.connection.createQueryRunner(); + + if (seasons.length) { + for (const season of seasons) { + const rate = new ItemRateModel(); + rate.item_id = event.data.id; + rate.season_period_id = season.id; + + rates.push(rate); + } + } + + // create batch + await this.dataService.createBatch(queryRunner, ItemRateModel, rates); + } +} diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts index 7c29f07..f0aae90 100644 --- a/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts +++ b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts @@ -6,7 +6,7 @@ import { RelationParam, } from 'src/core/modules/domain/entities/base-filter.entity'; import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity'; -import { STATUS } from 'src/core/strings/constants/base.constants'; +import { DAY, STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class IndexItemRateManager extends BaseIndexManager { @@ -26,18 +26,32 @@ export class IndexItemRateManager extends BaseIndexManager { d <= new Date(this.filterParam.end_date); d.setDate(d.getDate() + 1) ) { - const rate = item['item_rates']?.find( - (rate) => - rate.season_period?.status == STATUS.ACTIVE && - d >= new Date(rate.season_period.start_date) && - d <= new Date(rate.season_period.end_date), - ); + const day: string = DAY[d.getDay()]; + const rates = item['item_rates'] + ?.filter((rate) => { + const days: string[] = rate.season_period.days ?? []; + if (rate.season_period.priority == 2) { + return ( + rate.season_period?.status == STATUS.ACTIVE && + d >= new Date(rate.season_period.start_date) && + d <= new Date(rate.season_period.end_date) && + days.includes(day) + ); + } else { + return ( + rate.season_period?.status == STATUS.ACTIVE && + d >= new Date(rate.season_period.start_date) && + d <= new Date(rate.season_period.end_date) + ); + } + }) + .sort((a, b) => a.season_period.priority - b.season_period.priority); 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, + price: rates[0]?.price ?? item.base_price, + season_type: rates[0]?.season_period?.season_type ?? null, + holiday_name: rates[0]?.season_period?.holiday_name ?? null, }); } @@ -91,6 +105,8 @@ export class IndexItemRateManager extends BaseIndexManager { 'season_period.holiday_name', 'season_period.start_date', 'season_period.end_date', + 'season_period.priority', + 'season_period.days', 'season_type.id', 'season_type.name', diff --git a/src/modules/item-related/item-rate/item-rate.module.ts b/src/modules/item-related/item-rate/item-rate.module.ts index 5270a03..c0012e3 100644 --- a/src/modules/item-related/item-rate/item-rate.module.ts +++ b/src/modules/item-related/item-rate/item-rate.module.ts @@ -16,16 +16,24 @@ import { UpdateItemRateManager } from './domain/usecases/managers/update-item-ra 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'; +import { SeasonPeriodHolidayHandler } from './domain/usecases/handlers/item-created.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'; @Global() @Module({ imports: [ ConfigModule.forRoot(), - TypeOrmModule.forFeature([ItemRateModel], CONNECTION_NAME.DEFAULT), + TypeOrmModule.forFeature( + [ItemRateModel, SeasonPeriodModel], + CONNECTION_NAME.DEFAULT, + ), CqrsModule, ], controllers: [ItemRateDataController, ItemRateReadController], providers: [ + SeasonPeriodHolidayHandler, + IndexItemRateManager, DetailItemRateManager, CreateItemRateManager, @@ -33,6 +41,7 @@ import { ItemRateModel } from './data/models/item-rate.model'; UpdateItemRateManager, BatchDeleteItemRateManager, + SeasonPeriodDataService, ItemRateDataService, ItemRateReadService, diff --git a/src/modules/item-related/item/domain/usecases/managers/batch-delete-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/batch-delete-item.manager.ts index 03c5563..d5182aa 100644 --- a/src/modules/item-related/item/domain/usecases/managers/batch-delete-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/batch-delete-item.manager.ts @@ -7,7 +7,11 @@ import { import { ItemModel } from '../../../data/models/item.model'; import { ItemDeletedEvent } from '../../entities/event/item-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 BatchDeleteItemManager extends BaseBatchDeleteManager { @@ -16,6 +20,21 @@ export class BatchDeleteItemManager extends BaseBatchDeleteManager { } async validateData(data: ItemEntity): Promise { + const haveRelation = await this.dataService.getOneByOptions({ + where: { + bundling_items: { + id: data.id, + }, + }, + }); + + if (haveRelation) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! this data already connected to bunding item`, + error: 'Unprocessable Entity', + }); + } return; } @@ -24,7 +43,7 @@ export class BatchDeleteItemManager extends BaseBatchDeleteManager { } get validateRelations(): validateRelations[] { - return [{ relation: 'bundling_items' }]; + return []; } get entityTarget(): any { diff --git a/src/modules/item-related/item/domain/usecases/managers/delete-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/delete-item.manager.ts index 51db838..49b8e83 100644 --- a/src/modules/item-related/item/domain/usecases/managers/delete-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/delete-item.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 { ItemEntity } from '../../entities/item.entity'; import { @@ -15,6 +19,21 @@ export class DeleteItemManager extends BaseDeleteManager { } async validateProcess(): Promise { + const haveRelation = await this.dataService.getOneByOptions({ + where: { + bundling_items: { + id: this.dataId, + }, + }, + }); + + if (haveRelation) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! this data already connected to bunding item`, + error: 'Unprocessable Entity', + }); + } return; } @@ -27,7 +46,7 @@ export class DeleteItemManager extends BaseDeleteManager { } get validateRelations(): validateRelations[] { - return [{ relation: 'bundling_items' }]; + return []; } get entityTarget(): any { diff --git a/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts index ed74d61..f6e34dc 100644 --- a/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts +++ b/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts @@ -6,7 +6,7 @@ import { Param, RelationParam, } from 'src/core/modules/domain/entities/base-filter.entity'; -import { STATUS } from 'src/core/strings/constants/base.constants'; +import { DAY, STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class CurrentSeasonPeriodManager extends BaseIndexManager { @@ -20,8 +20,19 @@ export class CurrentSeasonPeriodManager extends BaseIndexManager { + const date = new Date(this.filterParam.date); + const day = DAY[date.getDay()]; Object.assign(this.result, { - data: this.result.data.sort((a, b) => a.priority - b.priority), + data: this.result.data + .filter((data) => { + const days: string[] = data.days ?? []; + if (data.priority == 2) { + return days.includes(day); + } else { + return true; + } + }) + .sort((a, b) => a.priority - b.priority), }); return; } 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 index d525a16..3c25ef5 100644 --- 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 @@ -46,7 +46,7 @@ export class SeasonPeriodDataOrchestrator extends BaseDataTransactionOrchestrato async update(dataId, data, updatePrice = false): Promise { this.updateManager.setData(dataId, { ...data, - isUpdatePrice: updatePrice + isUpdatePrice: updatePrice, }); this.updateManager.setService(this.serviceData, TABLE_NAME.SEASON_PERIOD); await this.updateManager.execute(); diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts index 44e87ff..abd89f6 100644 --- a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts @@ -23,7 +23,7 @@ export class BatchCancelReconciliationManager extends BaseBatchUpdateStatusManag }, }); - if (transaction.status != STATUS.SETTLED) { + if ([STATUS.SETTLED, STATUS.WAITING].includes(transaction.status)) { throw new UnprocessableEntityException({ statusCode: HttpStatus.UNPROCESSABLE_ENTITY, message: `Failed! cant cancel transaction not settled`, diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts index 3f3df81..c8ae532 100644 --- a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-confirm-reconciliation.manager.ts @@ -9,22 +9,20 @@ import { TransactionEntity } from 'src/modules/transaction/transaction/domain/en import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; import { STATUS } from 'src/core/strings/constants/base.constants'; import { TransactionChangeStatusEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-change-status.event'; +import { generateInvoiceCodeHelper } from 'src/modules/transaction/transaction/domain/usecases/managers/helpers/generate-invoice-code.helper'; @Injectable() export class BatchConfirmReconciliationManager extends BaseBatchUpdateStatusManager { async validateData(data: TransactionEntity): Promise { - const net_profit = data.reconciliation_mdr - ? Number(this.data.payment_total) - Number(this.data.reconciliation_mdr) - : null; - Object.assign(data, { - reconciliation_mdr: this.data.reconciliation_mdr ?? null, reconciliation_confirm_by: this.user.name, reconciliation_confirm_date: new Date().getTime(), status: STATUS.SETTLED, reconciliation_status: this.dataStatus, - payment_total_net_profit: net_profit, - payment_date: this.data.payment_date, + payment_code_reference: await generateInvoiceCodeHelper( + this.dataService, + 'PMY', + ), }); return; } diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts index 7550736..b3464b9 100644 --- a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts @@ -27,7 +27,7 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager { @@ -25,6 +26,10 @@ export class ConfirmReconciliationManager extends BaseUpdateStatusManager { @@ -145,7 +146,10 @@ export class IndexReconciliationManager extends BaseIndexManager { validateData(data: TransactionEntity): Promise { + if ( + [ + TransactionPaymentType.BANK_TRANSFER, + TransactionPaymentType.QRIS, + ].includes(this.data.payment_type) && + (!this.data.payment_date || + !this.data.payment_total || + !this.data.payment_type_method_number || + !this.data.payment_type_method_name) + ) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Gagal! tolong lengkapi data terlebih dahulu`, + error: 'Unprocessable Entity', + }); + } + if ( ![STATUS.PENDING, STATUS.REJECTED, STATUS.EXPIRED].includes(data.status) ) { throw new UnprocessableEntityException({ statusCode: HttpStatus.UNPROCESSABLE_ENTITY, - message: `Failed! only data booking with status ${STATUS.PENDING}, ${STATUS.REJECTED}, ${STATUS.EXPIRED} can be confirm`, + message: `Gagal! hanya pemesanan dengan status ${STATUS.PENDING}, ${STATUS.REJECTED}, ${STATUS.EXPIRED} dapat dikonfirmasi`, error: 'Unprocessable Entity', }); } diff --git a/src/modules/transaction/transaction/domain/usecases/managers/batch-confirm-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/batch-confirm-transaction.manager.ts index 47606d4..7b766e6 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/batch-confirm-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/batch-confirm-transaction.manager.ts @@ -49,7 +49,7 @@ export class BatchConfirmTransactionManager extends BaseBatchUpdateStatusManager } Object.assign(data, { - invoice_code: await generateInvoiceCodeHelper(this.dataService), + invoice_code: await generateInvoiceCodeHelper(this.dataService, 'INV'), status: freeTransaction ? STATUS.ACTIVE : STATUS.PENDING, }); return; diff --git a/src/modules/transaction/transaction/domain/usecases/managers/confirm-data-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/confirm-data-transaction.manager.ts index f860f9e..3961cbb 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/confirm-data-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/confirm-data-transaction.manager.ts @@ -21,6 +21,23 @@ export class ConfirmDataTransactionManager extends BaseUpdateStatusManager { + if ( + [ + TransactionPaymentType.BANK_TRANSFER, + TransactionPaymentType.QRIS, + ].includes(this.data.payment_type) && + (!this.data.payment_date || + !this.data.payment_total || + !this.data.payment_type_method_number || + !this.data.payment_type_method_name) + ) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Gagal! tolong lengkapi data terlebih dahulu`, + error: 'Unprocessable Entity', + }); + } + return; } @@ -32,7 +49,7 @@ export class ConfirmDataTransactionManager extends BaseUpdateStatusManager