diff --git a/package.json b/package.json index 944ef0f..eeee924 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "dotenv": "^16.4.5", "elastic-apm-node": "^4.5.4", "exceljs": "^4.4.0", + "fs-extra": "^11.2.0", "googleapis": "^140.0.0", "handlebars": "^4.7.8", "mathjs": "^13.0.2", diff --git a/src/core/helpers/path/move-file-path.helper.ts b/src/core/helpers/path/move-file-path.helper.ts index 040ad09..4fe3c41 100644 --- a/src/core/helpers/path/move-file-path.helper.ts +++ b/src/core/helpers/path/move-file-path.helper.ts @@ -1,39 +1,28 @@ -import * as fs from 'fs'; +import * as fs from 'fs-extra'; +import * as path from 'path'; -export function MoveFilePathHelper() {} +export async function MoveFilePathHelper(data) { + const imagePath = data['qr_image'] ?? data['image_url']; + const sourcePath = path.join(__dirname, '../../../../uploads/', imagePath); + const movePath = + 'data/' + + imagePath + .split('/') + .filter((item) => !['uploads', 'tmp'].includes(item)) + .join('/'); + const destinationPath = path.join( + __dirname, + '../../../../uploads/', + movePath, + ); -// export class MoveFileToDirIdHelper { -// public srcDir: string; -// public fileName: string; + try { + await fs.move(sourcePath, destinationPath); -// constructor(private file_url: string = null) {} - -// execute(): string { -// try { -// this.getSrcDir(); -// this.getFileName(); - -// const copyFile = `${this.fileName}-copy`; -// fs.mkdirSync(`./uploads/${this.srcDir}`, { recursive: true }); -// fs.copyFileSync( -// this.file_url, -// `./uploads/${this.srcDir}/${this.fileName}`, -// ); -// fs.unlinkSync(`${this.file_url}`); - -// this.file_url = `uploads/${this.srcDir}/${this.fileName}`; -// } catch (error) { -// console.error('Error moving file:', error); -// } - -// return this.file_url; -// } - -// private getSrcDir(): void { -// this.srcDir = this.file_url.split('/').slice(2, -1).join('/'); -// } - -// private getFileName(): void { -// this.fileName = this.file_url.split('/').slice(-1).join('/'); -// } -// } + Object.assign(data, { + image_url: movePath, + }); + } catch (error) { + console.log(`Failed! Error move file data`); + } +} 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 c30052d..92f0019 100644 --- a/src/core/modules/domain/usecase/managers/base-create.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-create.manager.ts @@ -6,6 +6,7 @@ import { columnUniques, validateRelations, } from 'src/core/strings/constants/interface.constants'; +import { MoveFilePathHelper } from 'src/core/helpers/path/move-file-path.helper'; export abstract class BaseCreateManager extends BaseManager { protected result: Entity; @@ -43,6 +44,15 @@ export abstract class BaseCreateManager extends BaseManager { } async process(): Promise { + const keys = Object.keys(this.data); + if ( + (keys.includes('qr_image') || keys.includes('image_url')) && + (this.data['image_url']?.includes('tmp') || + this.data['qr_image']?.includes('tmp')) + ) { + await MoveFilePathHelper(this.data); + } + this.result = await this.dataService.create( this.queryRunner, this.entityTarget, diff --git a/src/core/modules/domain/usecase/managers/base-custom.manager.ts b/src/core/modules/domain/usecase/managers/base-custom.manager.ts index 2fd87d7..58215f2 100644 --- a/src/core/modules/domain/usecase/managers/base-custom.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-custom.manager.ts @@ -5,7 +5,7 @@ export abstract class BaseCustomManager extends BaseManager { protected result: any; abstract get entityTarget(): any; - setData(entity: Entity): void { + setData(entity: any): void { this.data = entity; } diff --git a/src/database/migrations/1721647955446-update-image-column-item.ts b/src/database/migrations/1721647955446-update-image-column-item.ts new file mode 100644 index 0000000..f337d7e --- /dev/null +++ b/src/database/migrations/1721647955446-update-image-column-item.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateImageColumnItem1721647955446 implements MigrationInterface { + name = 'UpdateImageColumnItem1721647955446'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "items" RENAME COLUMN "image" TO "image_url"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "items" RENAME COLUMN "image_url" TO "image"`, + ); + } +} 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 73205e4..61b645e 100644 --- a/src/modules/item-related/item/data/models/item.model.ts +++ b/src/modules/item-related/item/data/models/item.model.ts @@ -25,8 +25,8 @@ export class ItemModel @Column('varchar', { name: 'name' }) name: string; - @Column('varchar', { name: 'image', nullable: true }) - image: string; + @Column('varchar', { name: 'image_url', nullable: true }) + image_url: string; @Column('enum', { name: 'item_type', diff --git a/src/modules/item-related/item/domain/entities/item.entity.ts b/src/modules/item-related/item/domain/entities/item.entity.ts index b0ab24b..53620cd 100644 --- a/src/modules/item-related/item/domain/entities/item.entity.ts +++ b/src/modules/item-related/item/domain/entities/item.entity.ts @@ -5,7 +5,7 @@ import { LimitType } from '../../constants'; export interface ItemEntity extends BaseStatusEntity { name: string; item_type: ItemType; - image: string; + image_url: string; hpp: number; sales_margin: number; diff --git a/src/modules/item-related/item/domain/usecases/item-data.orchestrator.ts b/src/modules/item-related/item/domain/usecases/item-data.orchestrator.ts index 69a2463..0c9296e 100644 --- a/src/modules/item-related/item/domain/usecases/item-data.orchestrator.ts +++ b/src/modules/item-related/item/domain/usecases/item-data.orchestrator.ts @@ -15,6 +15,8 @@ import { BatchInactiveItemManager } from './managers/batch-inactive-item.manager import { BatchActiveItemManager } from './managers/batch-active-item.manager'; import { BatchDeleteItemManager } from './managers/batch-delete-item.manager'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UpdateItemRatePriceManager } from './managers/update-item-rate-price.manager'; +import { ItemRateReadService } from 'src/modules/item-related/item-rate/data/services/item-rate-read.service'; @Injectable() export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator { @@ -28,8 +30,10 @@ export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator { + this.updatePriceManager.setData(data); + this.updatePriceManager.setService(this.serviceRateData, TABLE_NAME.ITEM); + await this.updatePriceManager.execute(); + return this.updatePriceManager.getResult(); + } + async delete(dataId, tenantId?: string): Promise { this.deleteManager.setData(dataId); this.deleteManager.setService(this.serviceData, TABLE_NAME.ITEM); diff --git a/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts index c1ed614..591ff7a 100644 --- a/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/detail-item.manager.ts @@ -33,6 +33,7 @@ export class DetailItemManager extends BaseDetailManager { get selects(): string[] { return [ `${this.tableName}.id`, + `${this.tableName}.image_url`, `${this.tableName}.created_at`, `${this.tableName}.status`, `${this.tableName}.item_type`, diff --git a/src/modules/item-related/item/domain/usecases/managers/update-item-rate-price.manager.ts b/src/modules/item-related/item/domain/usecases/managers/update-item-rate-price.manager.ts new file mode 100644 index 0000000..e11f875 --- /dev/null +++ b/src/modules/item-related/item/domain/usecases/managers/update-item-rate-price.manager.ts @@ -0,0 +1,72 @@ +import { Injectable } from '@nestjs/common'; +import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; +import { ItemEntity } from '../../entities/item.entity'; +import { EventTopics } from 'src/core/strings/constants/interface.constants'; +import { ItemModel } from '../../../data/models/item.model'; +import { In, LessThanOrEqual, MoreThanOrEqual } from 'typeorm'; + +@Injectable() +export class UpdateItemRatePriceManager extends BaseCustomManager { + protected rates = []; + get entityTarget(): any { + return ItemModel; + } + + get eventTopics(): EventTopics[] { + return []; + } + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + let query; + const item_ids = this.data.items.map((item) => { + return item.item.id; + }); + + if (this.data.season_period_id) { + query = { + item_id: In(item_ids), + season_period: { + id: this.data.season_period_id, + }, + }; + } else { + query = { + item_id: In(item_ids), + season_period: { + start_date: MoreThanOrEqual(this.data.booking_date), + end_date: LessThanOrEqual(this.data.booking_date), + }, + }; + } + + this.rates = await this.dataService.getManyByOptions({ + where: query, + }); + return; + } + + async process(): Promise { + this.data.items.map((item) => { + const current_price = this.rates.find( + (rate) => rate.item_id == item.item.id, + ); + + Object.assign(item, { + total_price: current_price?.price ?? item.item.base_price, + }); + }); + return; + } + + async afterProcess(): Promise { + return; + } + + async getResult() { + return this.data.items; + } +} 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 cc2067e..dfdd8dd 100644 --- a/src/modules/item-related/item/infrastructure/dto/item.dto.ts +++ b/src/modules/item-related/item/infrastructure/dto/item.dto.ts @@ -29,7 +29,7 @@ export class ItemDto extends BaseStatusDto implements ItemEntity { }) @IsString() @ValidateIf((body) => body.image) - image: string; + image_url: string; @ApiProperty({ type: 'string', diff --git a/src/modules/item-related/item/infrastructure/dto/update-item-price.dto.ts b/src/modules/item-related/item/infrastructure/dto/update-item-price.dto.ts new file mode 100644 index 0000000..b8fa0f5 --- /dev/null +++ b/src/modules/item-related/item/infrastructure/dto/update-item-price.dto.ts @@ -0,0 +1,43 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UpdateItemPriceDto { + @ApiProperty({ + type: [Object], + required: true, + example: [ + { + item: { + id: 'bee5c493-fb35-4ceb-b7a1-7bc3edb3c63b', + name: 'TEnant 2 wahana air', + item_type: 'wahana', + base_price: '100000', + hpp: '0', + tenant: { + id: 'e19a4637-d4db-48cc-89ce-501913d07cdd', + name: 'e19a4637-d4db-48cc-89ce-501913d07cdd', + share_margin: null, + }, + item_category: { + id: '88633772-ec34-4645-bc04-6cfdce6af0cf', + name: 'Wahana Air', + }, + }, + qty: 1, + total_price: '100000', + }, + ], + }) + items: Object[]; + + @ApiProperty({ + type: String, + example: 'uuid', + }) + season_period_id: string; + + @ApiProperty({ + type: Date, + example: '2024-08-17', + }) + booking_date: Date; +} diff --git a/src/modules/item-related/item/infrastructure/item-data.controller.ts b/src/modules/item-related/item/infrastructure/item-data.controller.ts index 44a855d..f416665 100644 --- a/src/modules/item-related/item/infrastructure/item-data.controller.ts +++ b/src/modules/item-related/item/infrastructure/item-data.controller.ts @@ -15,6 +15,7 @@ import { ItemEntity } from '../domain/entities/item.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 { UpdateItemPriceDto } from './dto/update-item-price.dto'; @ApiTags(`${MODULE_NAME.ITEM.split('-').join(' ')} - data`) @Controller(`v1/${MODULE_NAME.ITEM}`) @@ -28,6 +29,11 @@ export class ItemDataController { return await this.orchestrator.create(data); } + @Post('update-price') + async updatePrice(@Body() body: UpdateItemPriceDto): Promise { + return await this.orchestrator.updatePrice(body); + } + @Put('/batch-delete') async batchDeleted(@Body() body: BatchIdsDto): Promise { return await this.orchestrator.batchDelete(body.ids); diff --git a/src/modules/item-related/item/item.module.ts b/src/modules/item-related/item/item.module.ts index 456f7a2..bb35be2 100644 --- a/src/modules/item-related/item/item.module.ts +++ b/src/modules/item-related/item/item.module.ts @@ -25,6 +25,7 @@ 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'; +import { UpdateItemRatePriceManager } from './domain/usecases/managers/update-item-rate-price.manager'; @Global() @Module({ @@ -51,6 +52,7 @@ import { IndexItemRatesManager } from './domain/usecases/managers/index-item-rat BatchActiveItemManager, BatchConfirmItemManager, BatchInactiveItemManager, + UpdateItemRatePriceManager, ItemDataService, ItemReadService, diff --git a/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts index 5b630e8..8e65106 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts @@ -30,6 +30,19 @@ export class CreateRefundManager extends BaseCreateManager { refund_items: refund_items, }); + const exist = await this.dataService.getOneByOptions({ + where: { + transaction_id: this.data.transaction.id, + }, + }); + if (exist) { + throw new UnprocessableEntityException({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: `Failed! refund transaction with invoice ${this.data.transaction.invoice_code} already exist`, + error: 'Unprocessable Entity', + }); + } + const transaction = await this.dataServiceFirstOpt.getOneByOptions({ where: { id: this.data.transaction.id, diff --git a/yarn.lock b/yarn.lock index cc75c97..640a173 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3423,6 +3423,15 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"