diff --git a/src/core/helpers/query/specific-search.helper.ts b/src/core/helpers/query/specific-search.helper.ts index 6ad02d0..4e8d463 100644 --- a/src/core/helpers/query/specific-search.helper.ts +++ b/src/core/helpers/query/specific-search.helper.ts @@ -20,12 +20,16 @@ export class SpecificSearchFilter { new Brackets((qb) => { params.forEach((param) => { const { cols, data, additional, leftJoin } = param; + const columns = cols.split('.'); + let arr = data; - const arr = data?.map((el) => - el.includes("'") - ? `'%${el.trim().replace(/'/g, "''").replace(/\s+/g, ' ')}%'` - : `'%${el.trim().replace(/\s+/g, ' ')}%'`, - ); + if (!columns.includes('status::text')) { + arr = data?.map((el) => + el.includes("'") + ? `'%${el.trim().replace(/'/g, "''").replace(/\s+/g, ' ')}%'` + : `'%${el.trim().replace(/\s+/g, ' ')}%'`, + ); + } const aliases = !cols.match(/\./g) ? this.table.concat(`.${cols}`) diff --git a/src/core/modules/domain/usecase/managers/base-index.manager.ts b/src/core/modules/domain/usecase/managers/base-index.manager.ts index 89c2f54..63a7c56 100644 --- a/src/core/modules/domain/usecase/managers/base-index.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-index.manager.ts @@ -50,7 +50,7 @@ export abstract class BaseIndexManager extends BaseReadManager { // jika searching status terdapat dalam enum, maka dia mencari specific data // ? karena jika tidak, ketika dia search "active" maka "inactive" juga ikut - return STATUS[statusData.toUpperCase()] ?? statusData; + return `'${STATUS[statusData.toUpperCase()]}'` ?? `'%${statusData}%'`; }); specificFilter.push({ cols: `${this.tableName}.status::text`, diff --git a/src/core/strings/constants/privilege.constants.ts b/src/core/strings/constants/privilege.constants.ts index 2018bea..842d240 100644 --- a/src/core/strings/constants/privilege.constants.ts +++ b/src/core/strings/constants/privilege.constants.ts @@ -51,7 +51,6 @@ export const PrivilegeAdminConstant = [ menu_label: 'Rekonsiliasi', actions: [ PrivilegeAction.VIEW, - PrivilegeAction.CREATE, PrivilegeAction.CONFIRM, PrivilegeAction.DELETE, PrivilegeAction.CANCEL, @@ -160,7 +159,7 @@ export const PrivilegePOSConstant = [ { menu: 'BOOKING', menu_label: 'Pemesanan', - actions: [PrivilegeAction.VIEW, PrivilegeAction.CREATE], + actions: [PrivilegeAction.VIEW], index: 16, }, { diff --git a/src/database/migrations/1722318939681-add-column-to-refund-table.ts b/src/database/migrations/1722318939681-add-column-to-refund-table.ts new file mode 100644 index 0000000..c731b00 --- /dev/null +++ b/src/database/migrations/1722318939681-add-column-to-refund-table.ts @@ -0,0 +1,27 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddColumnToRefundTable1722318939681 implements MigrationInterface { + name = 'AddColumnToRefundTable1722318939681'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."refunds_refund_reason_type_enum" AS ENUM('weather', 'ride malfunction', 'other')`, + ); + await queryRunner.query( + `ALTER TABLE "refunds" ADD "refund_reason_type" "public"."refunds_refund_reason_type_enum" NOT NULL DEFAULT 'ride malfunction'`, + ); + await queryRunner.query(`ALTER TABLE "refunds" ADD "refund_reason" text`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "refunds" DROP COLUMN "refund_reason"`, + ); + await queryRunner.query( + `ALTER TABLE "refunds" DROP COLUMN "refund_reason_type"`, + ); + await queryRunner.query( + `DROP TYPE "public"."refunds_refund_reason_type_enum"`, + ); + } +} diff --git a/src/modules/configuration/constant/infrastructure/constant.controller.ts b/src/modules/configuration/constant/infrastructure/constant.controller.ts index a388236..fd0e04b 100644 --- a/src/modules/configuration/constant/infrastructure/constant.controller.ts +++ b/src/modules/configuration/constant/infrastructure/constant.controller.ts @@ -5,7 +5,10 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; import { ItemType } from 'src/modules/item-related/item-category/constants'; import { LimitType } from 'src/modules/item-related/item/constants'; import { PaymentMethodType } from 'src/modules/transaction/payment-method/constants'; -import { RefundType } from 'src/modules/transaction/refund/constants'; +import { + RefundReasonType, + RefundType, +} from 'src/modules/transaction/refund/constants'; import { GateType } from 'src/modules/web-information/gate/constants'; @ApiTags('configuration - constant') @@ -52,6 +55,11 @@ export class ConstantController { return Object.values(RefundType); } + @Get('refund-reason-type') + async refundReasonType(): Promise { + return Object.values(RefundReasonType); + } + @Get('gate-type') async gateType(): Promise { return Object.values(GateType); diff --git a/src/modules/item-related/item-category/domain/usecases/managers/update-item-category.manager.ts b/src/modules/item-related/item-category/domain/usecases/managers/update-item-category.manager.ts index b7cec18..64e9e0c 100644 --- a/src/modules/item-related/item-category/domain/usecases/managers/update-item-category.manager.ts +++ b/src/modules/item-related/item-category/domain/usecases/managers/update-item-category.manager.ts @@ -27,7 +27,11 @@ export class UpdateItemCategoryManager extends BaseUpdateManager { get selects(): string[] { return [ `${this.tableName}.id`, + `${this.tableName}.item_type`, `${this.tableName}.status`, `${this.tableName}.created_at`, `${this.tableName}.name`, 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 index 2118cfa..1966eab 100644 --- 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 @@ -2,6 +2,7 @@ import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.d import { FilterItemRateEntity } from '../../domain/entities/filter-item-rate.entity'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; +import { ItemType } from 'src/modules/item-related/item-category/constants'; export class FilterItemRateDto extends BaseFilterDto @@ -21,6 +22,16 @@ export class FilterItemRateDto }) end_date: Date; + @ApiProperty({ + type: ['string'], + required: false, + description: `Select ["${Object.values(ItemType)}"]`, + }) + @Transform((body) => { + return Array.isArray(body.value) ? body.value : [body.value]; + }) + item_types: ItemType[]; + @ApiProperty({ type: ['string'], required: false }) @Transform((body) => { return Array.isArray(body.value) ? body.value : [body.value]; 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 d5182aa..d36aecd 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,11 +7,8 @@ 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 { - HttpStatus, - Injectable, - UnprocessableEntityException, -} from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; +import { validateRelation } from './helpers/validasi-relation.helper'; @Injectable() export class BatchDeleteItemManager extends BaseBatchDeleteManager { @@ -20,21 +17,7 @@ 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', - }); - } + await validateRelation(this.dataService, data.id); return; } diff --git a/src/modules/item-related/item/domain/usecases/managers/batch-inactive-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/batch-inactive-item.manager.ts index 19285af..7d74db7 100644 --- a/src/modules/item-related/item/domain/usecases/managers/batch-inactive-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/batch-inactive-item.manager.ts @@ -8,10 +8,12 @@ import { ItemModel } from '../../../data/models/item.model'; import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event'; import { BatchResult } from 'src/core/response/domain/ok-response.interface'; import { Injectable } from '@nestjs/common'; +import { validateRelation } from './helpers/validasi-relation.helper'; @Injectable() export class BatchInactiveItemManager extends BaseBatchUpdateStatusManager { - validateData(data: ItemEntity): Promise { + async validateData(data: ItemEntity): Promise { + await validateRelation(this.dataService, data.id); return; } 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 49b8e83..ea95c6f 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,8 +1,4 @@ -import { - HttpStatus, - Injectable, - UnprocessableEntityException, -} from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager'; import { ItemEntity } from '../../entities/item.entity'; import { @@ -11,6 +7,7 @@ import { } from 'src/core/strings/constants/interface.constants'; import { ItemModel } from '../../../data/models/item.model'; import { ItemDeletedEvent } from '../../entities/event/item-deleted.event'; +import { validateRelation } from './helpers/validasi-relation.helper'; @Injectable() export class DeleteItemManager extends BaseDeleteManager { @@ -19,21 +16,7 @@ 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', - }); - } + await validateRelation(this.dataService, this.dataId); return; } diff --git a/src/modules/item-related/item/domain/usecases/managers/helpers/validasi-relation.helper.ts b/src/modules/item-related/item/domain/usecases/managers/helpers/validasi-relation.helper.ts new file mode 100644 index 0000000..abe97a6 --- /dev/null +++ b/src/modules/item-related/item/domain/usecases/managers/helpers/validasi-relation.helper.ts @@ -0,0 +1,19 @@ +import { HttpStatus, UnprocessableEntityException } from '@nestjs/common'; + +export async function validateRelation(dataService, id) { + const haveRelation = await dataService.getOneByOptions({ + where: { + bundling_items: { + id: id, + }, + }, + }); + + if (haveRelation) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! this data already connected to bunding item`, + error: 'Unprocessable Entity', + }); + } +} diff --git a/src/modules/item-related/item/domain/usecases/managers/inactive-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/inactive-item.manager.ts index aef9e52..7644d03 100644 --- a/src/modules/item-related/item/domain/usecases/managers/inactive-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/inactive-item.manager.ts @@ -7,6 +7,7 @@ import { } from 'src/core/strings/constants/interface.constants'; import { ItemModel } from '../../../data/models/item.model'; import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event'; +import { validateRelation } from './helpers/validasi-relation.helper'; @Injectable() export class InactiveItemManager extends BaseUpdateStatusManager { @@ -15,6 +16,7 @@ export class InactiveItemManager extends BaseUpdateStatusManager { } async validateProcess(): Promise { + await validateRelation(this.dataService, this.dataId); return; } 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 index c18bc97..9306d0f 100644 --- 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 @@ -19,6 +19,16 @@ export class IndexItemRatesManager extends BaseIndexManager { } async afterProcess(): Promise { + this.result.data?.map((item) => { + const item_price = + Number(item['item']?.total_price ?? 0) == 0 + ? item['item']?.total_price + : item['item']?.base_price; + + Object.assign(item, { + price: item.price ?? item_price, + }); + }); return; } @@ -28,7 +38,7 @@ export class IndexItemRatesManager extends BaseIndexManager { joinRelations: [], // relation join and select (relasi yang ingin ditampilkan), - selectRelations: ['season_period', 'season_period.season_type'], + selectRelations: ['season_period', 'season_period.season_type', 'item'], // relation yang hanya ingin dihitung (akan return number) countRelations: [], @@ -41,6 +51,10 @@ export class IndexItemRatesManager extends BaseIndexManager { `${this.tableName}.item_id`, `${this.tableName}.price`, + 'item.id', + 'item.total_price', + 'item.base_price', + `season_period.id`, `season_period.priority`, `season_period.created_at`, diff --git a/src/modules/transaction/refund/constants.ts b/src/modules/transaction/refund/constants.ts index 0c9b67b..886b781 100644 --- a/src/modules/transaction/refund/constants.ts +++ b/src/modules/transaction/refund/constants.ts @@ -2,3 +2,9 @@ export enum RefundType { BOOKING = 'pengembalian booking', WAHANA = 'pengembalian wahana', } + +export enum RefundReasonType { + WEATHER = 'weather', + RIDE_MALFUNCTION = 'ride malfunction', + OTHER = 'other', +} diff --git a/src/modules/transaction/refund/data/models/refund.model.ts b/src/modules/transaction/refund/data/models/refund.model.ts index 4740870..e50043a 100644 --- a/src/modules/transaction/refund/data/models/refund.model.ts +++ b/src/modules/transaction/refund/data/models/refund.model.ts @@ -4,7 +4,7 @@ import { Column, Entity, JoinColumn, OneToMany, OneToOne } from 'typeorm'; import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; import { RefundItemModel } from './refund-item.model'; -import { RefundType } from '../../constants'; +import { RefundReasonType, RefundType } from '../../constants'; @Entity(TABLE_NAME.REFUND) export class RefundModel @@ -33,6 +33,16 @@ export class RefundModel @Column('decimal', { name: 'refund_total', nullable: true }) refund_total: number; + @Column('enum', { + name: 'refund_reason_type', + enum: RefundReasonType, + default: RefundReasonType.RIDE_MALFUNCTION, + }) + refund_reason_type: RefundReasonType; + + @Column('text', { name: 'refund_reason', nullable: true }) + refund_reason: string; + // bank info @Column('varchar', { name: 'bank_name', nullable: true }) bank_name: string; diff --git a/src/modules/transaction/refund/domain/entities/refund.entity.ts b/src/modules/transaction/refund/domain/entities/refund.entity.ts index c9bf720..5d9ae53 100644 --- a/src/modules/transaction/refund/domain/entities/refund.entity.ts +++ b/src/modules/transaction/refund/domain/entities/refund.entity.ts @@ -1,5 +1,5 @@ import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity'; -import { RefundType } from '../../constants'; +import { RefundReasonType, RefundType } from '../../constants'; export interface RefundEntity extends BaseStatusEntity { type: RefundType; @@ -8,6 +8,8 @@ export interface RefundEntity extends BaseStatusEntity { refund_date: Date; refund_sub_total: number; refund_total: number; + refund_reason_type: RefundReasonType; + refund_reason: string; bank_name: string; bank_account_name: string; diff --git a/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts index 678566a..3657a0f 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts @@ -17,7 +17,12 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class BatchConfirmRefundManager extends BaseBatchUpdateStatusManager { async validateData(data: RefundEntity): Promise { - if (data?.['transaction']?.status != STATUS.SETTLED) { + if ( + this.data.status == STATUS.DRAFT && + data['trnsaction']?.status != STATUS.SETTLED && + this.data.status == STATUS.PENDING && + data['trnsaction']?.status != STATUS.PROCESS_REFUND + ) { throw new UnprocessableEntityException({ statusCode: HttpStatus.UNPROCESSABLE_ENTITY, message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts index 1de469c..2d1e8a3 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts @@ -38,7 +38,12 @@ export class ConfirmRefundManager extends BaseUpdateStatusManager relations: ['transaction'], }); - if (data.transaction.status != STATUS.SETTLED) { + if ( + data.status == STATUS.DRAFT && + data.transaction.status != STATUS.SETTLED && + data.status == STATUS.PENDING && + data.transaction.status != STATUS.PROCESS_REFUND + ) { throw new UnprocessableEntityException({ statusCode: HttpStatus.UNPROCESSABLE_ENTITY, message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/detail-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/detail-refund.manager.ts index 88c3e89..486e581 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/detail-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/detail-refund.manager.ts @@ -39,6 +39,8 @@ export class DetailRefundManager extends BaseDetailManager { `${this.tableName}.status`, `${this.tableName}.type`, + `${this.tableName}.refund_reason`, + `${this.tableName}.refund_reason_type`, `${this.tableName}.request_date`, `${this.tableName}.refund_date`, `${this.tableName}.created_at`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/index-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/index-refund.manager.ts index 2fd80a0..1be1328 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/index-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/index-refund.manager.ts @@ -50,6 +50,8 @@ export class IndexRefundManager extends BaseIndexManager { `${this.tableName}.status`, `${this.tableName}.type`, + `${this.tableName}.refund_reason`, + `${this.tableName}.refund_reason_type`, `${this.tableName}.request_date`, `${this.tableName}.refund_date`, `${this.tableName}.created_at`, diff --git a/src/modules/transaction/refund/infrastructure/dto/refund.dto.ts b/src/modules/transaction/refund/infrastructure/dto/refund.dto.ts index 86a3b70..8e89845 100644 --- a/src/modules/transaction/refund/infrastructure/dto/refund.dto.ts +++ b/src/modules/transaction/refund/infrastructure/dto/refund.dto.ts @@ -4,9 +4,25 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNumber, IsString, ValidateIf } from 'class-validator'; import { Exclude } from 'class-transformer'; import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; -import { RefundType } from '../../constants'; +import { RefundReasonType, RefundType } from '../../constants'; export class RefundDto extends BaseStatusDto implements RefundEntity { + @ApiProperty({ + type: String, + required: true, + example: RefundReasonType.RIDE_MALFUNCTION, + }) + @IsString() + refund_reason_type: RefundReasonType; + + @ApiProperty({ + type: String, + required: false, + example: '', + }) + @ValidateIf((object) => object.refund_reason_type == RefundReasonType.OTHER) + refund_reason: string; + @ApiProperty({ type: String, required: true, 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 7b766e6..dff1766 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 @@ -51,6 +51,7 @@ export class BatchConfirmTransactionManager extends BaseBatchUpdateStatusManager Object.assign(data, { invoice_code: await generateInvoiceCodeHelper(this.dataService, 'INV'), status: freeTransaction ? STATUS.ACTIVE : STATUS.PENDING, + invoice_date: new Date(), }); return; } diff --git a/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts index 1ed8d98..89b6bbe 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts @@ -61,6 +61,7 @@ export class ConfirmTransactionManager extends BaseUpdateStatusManager { get specificFilter(): Param[] { return [ { - cols: `${this.tableName}.name`, - data: this.filterParam.names, + cols: `${this.tableName}.title`, + data: this.filterParam.titles, }, ]; } diff --git a/src/modules/web-information/banner/infrastructure/dto/filter-banner.dto.ts b/src/modules/web-information/banner/infrastructure/dto/filter-banner.dto.ts index b8fa4af..b5a3edf 100644 --- a/src/modules/web-information/banner/infrastructure/dto/filter-banner.dto.ts +++ b/src/modules/web-information/banner/infrastructure/dto/filter-banner.dto.ts @@ -1,6 +1,15 @@ import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.dto'; import { FilterBannerEntity } from '../../domain/entities/filter-banner.entity'; +import { Transform } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; export class FilterBannerDto extends BaseFilterDto - implements FilterBannerEntity {} + implements FilterBannerEntity +{ + @ApiProperty({ type: ['string'], required: false }) + @Transform((body) => { + return Array.isArray(body.value) ? body.value : [body.value]; + }) + titles: string[]; +}