import { Body, Controller, Get, Param, Post, Res, UnprocessableEntityException, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Public } from 'src/core/guards'; import { TransactionDto } from './dto/booking-order.dto'; import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity'; import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { MidtransService } from 'src/modules/configuration/midtrans/data/services/midtrans.service'; import { CreateBookingManager } from '../domain/usecases/managers/create-booking.manager'; import * as QRCode from 'qrcode'; import { Gate } from 'src/core/response/domain/decorators/pagination.response'; import { Response } from 'express'; import { RescheduleRequestDTO, RescheduleVerificationOTP, } from './dto/reschedule.dto'; import { RescheduleVerificationManager } from '../domain/usecases/managers/reschedule-verification.manager'; import { RescheduleManager } from '../domain/usecases/managers/reschedule.manager'; import { STATUS } from 'src/core/strings/constants/base.constants'; import * as moment from 'moment'; @ApiTags('Booking Order') @Controller('v1/booking') @Public(true) export class BookingOrderController { constructor( private createBooking: CreateBookingManager, private serviceData: TransactionDataService, private midtransService: MidtransService, private rescheduleVerification: RescheduleVerificationManager, private rescheduleManager: RescheduleManager, ) {} @Post() async create(@Body() data: TransactionDto) { const payload: Partial = data; this.createBooking.setData(payload as any); this.createBooking.setService( this.serviceData, TABLE_NAME.TRANSACTION, this.midtransService, ); await this.createBooking.execute(); const result = await this.createBooking.getResult(); const { invoice_code, status, payment_midtrans_token, payment_midtrans_url, id, } = result; return { id, invoice_code, status, payment_midtrans_token, payment_midtrans_url, }; } @Post('reschedule') async reschedule(@Body() data: RescheduleRequestDTO) { const transaction = await this.serviceData.getTransactionWithReschedule( data.booking_id, ); const today = moment().startOf('day'); const rescheduleDate = moment(data.reschedule_date, 'DD-MM-YYYY'); const rescheduleDateStartOfDay = rescheduleDate.startOf('day'); //TODO: validate session period priority if (rescheduleDateStartOfDay.isSameOrBefore(today)) { throw new UnprocessableEntityException( 'Reschedule date must be in the future', ); } if (!transaction) { throw new UnprocessableEntityException('Transaction not found'); } if (transaction.status !== STATUS.SETTLED) { throw new UnprocessableEntityException('Transaction is not settled'); } if (transaction.children_transactions.length > 0) { throw new UnprocessableEntityException('Transaction already rescheduled'); } if (transaction.parent_id) { throw new UnprocessableEntityException('Transaction is a reschedule'); } const result = await this.rescheduleVerification.saveVerification(data); const maskedPhoneNumber = result.phone_number.replace(/.(?=.{4})/g, '*'); result.phone_number = maskedPhoneNumber; return `Verification code sent to ${maskedPhoneNumber}`; } @Post('reschedule/verification') async verificationReschedule(@Body() data: RescheduleVerificationOTP) { const result = await this.rescheduleVerification.verifyOtp( data.booking_id, +data.code, ); const reschedule = await this.rescheduleManager.reschedule(result); const transaction = await this.get(reschedule.id); return { id: reschedule.id, phone_number: result.phone_number, name: result.name, reschedule_date: result.reschedule_date, transaction, }; } @Get(':id') async get(@Param('id') transactionId: string) { const data = await this.serviceData.getOneByOptions({ relations: [ 'items', 'parent_transaction', 'items.item', 'items.item.time_group', ], where: { id: transactionId }, }); const { parent_id, customer_name, customer_phone, booking_date, invoice_code, status, id, items, parent_transaction, } = data; let timeGroup = null; const usageItems = items.map((item) => { const itemData = item.item; const timeGroupData = itemData.time_group; const { id: groupId, name, start_time, end_time, max_usage_time, } = timeGroupData; timeGroup = { id: groupId, name, start_time, end_time, max_usage_time, }; const { id, item_id, item_name, item_price, item_category_name, total_price, total_net_price, qty, qty_remaining, } = item; return { id, item_id, item_name, item_price, item_category_name, total_price, total_net_price, qty, qty_remaining, }; }); // Mask customer_phone with * and keep last 4 numbers let maskedCustomerPhone = customer_phone; if (typeof customer_phone === 'string' && customer_phone.length > 4) { const last4 = customer_phone.slice(-4); maskedCustomerPhone = '*'.repeat(customer_phone.length - 4) + last4; } let parentTransaction = undefined; if (parent_transaction) { const { id: parentId, invoice_code: parentInvoiceCode, invoice_date: parentInvoiceDate, } = parent_transaction; parentTransaction = { id: parentId, invoice_code: parentInvoiceCode, invoice_date: parentInvoiceDate, }; } return { customer_name, customer_phone: maskedCustomerPhone, booking_date, invoice_code, status, id, is_reschedule: !!parent_id, items: usageItems, time_group: timeGroup, parent: parentTransaction, }; } @Gate() @Get('qrcode/:id') async getQRcode(@Param('id') id: string, @Res() res: Response) { console.log(QRCode); const qrData = id; const data = await QRCode.toDataURL(qrData); res.setHeader('Content-Type', 'image/png'); const base64Data = data.split(',')[1]; const buffer = Buffer.from(base64Data, 'base64'); res.send(buffer); } }