Compare commits

...

2 Commits

Author SHA1 Message Date
shancheas b476c92b70 feat: add time group information to detail booking 2025-06-11 11:04:41 +07:00
shancheas dc926d84e4 refactor: streamline rescheduling logic and enhance transaction retrieval
- Replaced direct transaction retrieval with a dedicated method for better clarity.
- Consolidated validation checks for rescheduling into the controller.
- Added booking date to the transaction data being created.
- Improved error handling for various transaction states during rescheduling.
2025-06-11 10:35:06 +07:00
3 changed files with 65 additions and 33 deletions

View File

@ -14,38 +14,11 @@ export class RescheduleManager {
constructor(private serviceData: TransactionDataService) {} constructor(private serviceData: TransactionDataService) {}
async reschedule(data: RescheduleVerificationModel) { async reschedule(data: RescheduleVerificationModel) {
const transaction = await this.serviceData.getRepository().findOne({ const transaction = await this.serviceData.getTransactionWithReschedule(
relations: ['children_transactions', 'items'], data.booking_id,
where: { id: data.booking_id }, );
});
const today = moment().startOf('day');
const rescheduleDate = moment(data.reschedule_date, 'DD-MM-YYYY'); 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 id = uuidv4(); const id = uuidv4();
const invoiceCode = await generateInvoiceCodeHelper( const invoiceCode = await generateInvoiceCodeHelper(
@ -62,6 +35,7 @@ export class RescheduleManager {
invoice_code: invoiceCode, invoice_code: invoiceCode,
status: STATUS.SETTLED, status: STATUS.SETTLED,
invoice_date: rescheduleDate.format('YYYY-MM-DD'), invoice_date: rescheduleDate.format('YYYY-MM-DD'),
booking_date: rescheduleDate.format('YYYY-MM-DD'),
created_at: moment().unix() * 1000, created_at: moment().unix() * 1000,
updated_at: moment().unix() * 1000, updated_at: moment().unix() * 1000,
items, items,
@ -75,7 +49,7 @@ export class RescheduleManager {
name: transactionData.customer_name, name: transactionData.customer_name,
phone: transactionData.customer_phone, phone: transactionData.customer_phone,
time: moment(transactionData.invoice_date).unix() * 1000, time: moment(transactionData.invoice_date).unix() * 1000,
code: data.code.toString(), code: transactionData.invoice_code,
}); });
return transactionData; return transactionData;

View File

@ -1,4 +1,12 @@
import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common'; import {
Body,
Controller,
Get,
Param,
Post,
Res,
UnprocessableEntityException,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { Public } from 'src/core/guards'; import { Public } from 'src/core/guards';
import { TransactionDto } from './dto/booking-order.dto'; import { TransactionDto } from './dto/booking-order.dto';
@ -16,6 +24,8 @@ import {
} from './dto/reschedule.dto'; } from './dto/reschedule.dto';
import { RescheduleVerificationManager } from '../domain/usecases/managers/reschedule-verification.manager'; import { RescheduleVerificationManager } from '../domain/usecases/managers/reschedule-verification.manager';
import { RescheduleManager } from '../domain/usecases/managers/reschedule.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') @ApiTags('Booking Order')
@Controller('v1/booking') @Controller('v1/booking')
@ -60,6 +70,38 @@ export class BookingOrderController {
@Post('reschedule') @Post('reschedule')
async reschedule(@Body() data: RescheduleRequestDTO) { 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 result = await this.rescheduleVerification.saveVerification(data);
const maskedPhoneNumber = result.phone_number.replace(/.(?=.{4})/g, '*'); const maskedPhoneNumber = result.phone_number.replace(/.(?=.{4})/g, '*');
result.phone_number = maskedPhoneNumber; result.phone_number = maskedPhoneNumber;
@ -89,7 +131,7 @@ export class BookingOrderController {
@Get(':id') @Get(':id')
async get(@Param('id') transactionId: string) { async get(@Param('id') transactionId: string) {
const data = await this.serviceData.getOneByOptions({ const data = await this.serviceData.getOneByOptions({
relations: ['items'], relations: ['items', 'items.item', 'items.item.time_group'],
where: { id: transactionId }, where: { id: transactionId },
}); });
@ -104,6 +146,9 @@ export class BookingOrderController {
} = data; } = data;
const usageItems = items.map((item) => { const usageItems = items.map((item) => {
const itemData = item.item;
const timeGroupData = itemData.time_group;
const { id: groupId, name, start_time, end_time } = timeGroupData;
const { const {
id, id,
item_id, item_id,
@ -125,6 +170,12 @@ export class BookingOrderController {
total_net_price, total_net_price,
qty, qty,
qty_remaining, qty_remaining,
time_group: {
id: groupId,
name,
start_time,
end_time,
},
}; };
}); });

View File

@ -13,4 +13,11 @@ export class TransactionDataService extends BaseDataService<TransactionModel> {
) { ) {
super(repo); super(repo);
} }
async getTransactionWithReschedule(booking_id: string) {
return this.repo.findOne({
relations: ['children_transactions', 'items'],
where: { id: booking_id },
});
}
} }