pos-be/src/modules/booking-online/order/infrastructure/order.controller.ts

245 lines
6.7 KiB
TypeScript

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<TransactionEntity> = 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);
}
}