find all
This commit is contained in:
@@ -1,25 +1,18 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Query } from '@nestjs/common';
|
||||
import {
|
||||
Controller, Get, Post, Body,
|
||||
Param,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { PaymentService } from '../services/payments.service';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiNotFoundResponse,
|
||||
ApiBody,
|
||||
ApiParam,
|
||||
ApiBearerAuth,
|
||||
ApiHeader,
|
||||
} from '@nestjs/swagger';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
||||
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
||||
import { VerifyPaymentDto } from '../dto/verify-payment.dto';
|
||||
import { PaymentChartDto } from '../dto/payment-chart.dto';
|
||||
import { UserId } from 'src/common/decorators';
|
||||
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
|
||||
|
||||
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||
import { Permission } from 'src/common/enums/permission.enum';
|
||||
import { PayOrderDto } from '../dto/pay-order.dto';
|
||||
import { FindPaymentsDto } from '../dto/find-payments.dto';
|
||||
|
||||
@ApiTags('payments')
|
||||
@Controller()
|
||||
@@ -37,76 +30,38 @@ export class PaymentController {
|
||||
return this.paymentsService.payOrder(orderId, dto);
|
||||
}
|
||||
|
||||
// @Post('public/payments/verify')
|
||||
// @ApiOperation({ summary: 'Verify a payment' })
|
||||
@Post('public/payments/:token/verify/online')
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Verify online payment' })
|
||||
verifyOnlinePayment(@Param('paymentId') token: string) {
|
||||
return this.paymentsService.verifyOnlinePayment(token);
|
||||
}
|
||||
|
||||
// @ApiBody({ type: VerifyPaymentDto })
|
||||
// @ApiNotFoundResponse({ description: 'Payment not found' })
|
||||
// verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) {
|
||||
// return this.paymentsService.verifyOnlinePayment(verifyPaymentDto.authority, verifyPaymentDto.orderId);
|
||||
// }
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments')
|
||||
@ApiOperation({ summary: 'get all payments as admin' })
|
||||
findAllByUser(@UserId() userId: string, @Body() dto: FindPaymentsDto) {
|
||||
return this.paymentsService.findAllByUser(dto, userId);
|
||||
}
|
||||
|
||||
/*===============Admin=============== */
|
||||
|
||||
@Post('admin/payments/:paymentId/verify/cash')
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Verify cash payment' })
|
||||
verifyPayment(@Param('paymentId') paymentId: string) {
|
||||
return this.paymentsService.verifyCashPayment(paymentId);
|
||||
}
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Permissions(Permission.MANAGE_PAYMENTS)
|
||||
// @ApiBearerAuth()
|
||||
// @Post('admin/payments/methods')
|
||||
// @ApiOperation({ summary: 'Create a new restaurant payment method' })
|
||||
// @ApiBody({ type: CreatePaymentMethodDto })
|
||||
// createRestaurantPaymentMethod( @Body() createPaymentMethodDto: CreatePaymentMethodDto) {
|
||||
// return this.paymentMethodService.create( createPaymentMethodDto);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Permissions(Permission.MANAGE_PAYMENTS)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/payments/methods/:paymentMethodId')
|
||||
// @ApiOperation({ summary: 'Get restaurant payment method by ID' })
|
||||
// @ApiParam({ name: 'paymentMethodId', type: 'string', description: 'Payment method ID' })
|
||||
// findOneRestaurantPaymentMethod(@Param('paymentMethodId') paymentMethodId: string) {
|
||||
// return this.paymentMethodService.findOne(paymentMethodId);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Permissions(Permission.MANAGE_PAYMENTS)
|
||||
// @ApiBearerAuth()
|
||||
// @Patch('admin/payments/methods/:paymentMethodId')
|
||||
// @ApiOperation({ summary: 'Update a restaurant payment method' })
|
||||
// @ApiParam({ name: 'paymentMethodId', type: 'string', description: 'Payment method ID' })
|
||||
// @ApiBody({ type: UpdatePaymentMethodDto })
|
||||
// updateRestaurantPaymentMethod(
|
||||
// @Param('paymentMethodId') paymentMethodId: string,
|
||||
// @Body() updatePaymentMethodDto: UpdatePaymentMethodDto,
|
||||
// ) {
|
||||
// return this.paymentMethodService.update(paymentMethodId, updatePaymentMethodDto);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Permissions(Permission.MANAGE_PAYMENTS)
|
||||
// @ApiBearerAuth()
|
||||
// @Delete('admin/payments/methods/:paymentMethodId')
|
||||
// @ApiOperation({ summary: 'Delete a restaurant payment method' })
|
||||
// @ApiParam({ name: 'paymentMethodId', type: 'string', description: 'Payment method ID' })
|
||||
// removeRestaurantPaymentMethod(@Param('paymentMethodId') paymentMethodId: string) {
|
||||
// return this.paymentMethodService.remove(paymentMethodId);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Permissions(Permission.MANAGE_PAYMENTS)
|
||||
// @ApiBearerAuth()
|
||||
// @Post('admin/payments/verify-cash-method/:paymentId')
|
||||
// @ApiOperation({ summary: 'Verify cash payment ' })
|
||||
// @ApiParam({ name: 'paymentId', type: 'string', description: 'Payment ID' })
|
||||
// verifyCashPayment(@Param('paymentId') paymentId: string) {
|
||||
// return this.paymentsService.verifyCashPayment(paymentId);
|
||||
// }
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @Permissions(Permission.VIEW_REPORTS)
|
||||
// @ApiBearerAuth()
|
||||
// @Get('admin/payments/chart')
|
||||
// @ApiOperation({ summary: 'Get payment chart data with date and period filters' })
|
||||
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments')
|
||||
@ApiOperation({ summary: 'get all payments as admin' })
|
||||
findAllPayments(@Body() dto: FindPaymentsDto) {
|
||||
return this.paymentsService.findAll(dto);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { IsString, IsOptional, IsBoolean, IsNumber, IsEnum } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaymentMethodEnum, PaymentGatewayEnum } from '../interface/payment';
|
||||
|
||||
export class CreatePaymentMethodDto {
|
||||
|
||||
@ApiProperty({ description: 'Payment method', enum: PaymentMethodEnum })
|
||||
@IsEnum(PaymentMethodEnum)
|
||||
method!: PaymentMethodEnum;
|
||||
|
||||
@ApiProperty({ description: 'Payment gateway', enum: PaymentGatewayEnum, required: false })
|
||||
@IsOptional()
|
||||
@IsEnum(PaymentGatewayEnum)
|
||||
gateway?: PaymentGatewayEnum | null;
|
||||
|
||||
@ApiProperty({ description: 'Payment method description', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: 'Is payment method enabled', required: false, default: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
enabled?: boolean;
|
||||
|
||||
@ApiProperty({ description: 'Display order', required: false, default: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
order?: number;
|
||||
|
||||
@ApiProperty({ description: 'Merchant ID', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
merchantId?: string;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsNumber,
|
||||
Min,
|
||||
IsIn,
|
||||
IsEnum,
|
||||
IsDateString,
|
||||
IsArray,
|
||||
} from 'class-validator';
|
||||
import { Type, Transform } from 'class-transformer';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
import { PaymentStatusEnum } from '../interface/payment';
|
||||
|
||||
const sortOrderOptions = ['asc', 'desc'] as const;
|
||||
type SortOrder = (typeof sortOrderOptions)[number];
|
||||
|
||||
export class FindPaymentsDto {
|
||||
@ApiPropertyOptional({ default: 1, minimum: 1 })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
page: number = 1;
|
||||
|
||||
@ApiPropertyOptional({ default: 10, minimum: 1 })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
limit: number = 10;
|
||||
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Filter by order statuses',
|
||||
enum: PaymentStatusEnum,
|
||||
isArray: true,
|
||||
})
|
||||
@IsOptional()
|
||||
@Transform(({ value }) =>
|
||||
Array.isArray(value) ? value : value?.split(',')
|
||||
)
|
||||
@IsArray()
|
||||
@IsEnum(PaymentStatusEnum, { each: true })
|
||||
statuses?: PaymentStatusEnum[];
|
||||
|
||||
// @ApiPropertyOptional({ enum: PaymentStatusEnum })
|
||||
// @IsOptional()
|
||||
// @IsEnum(PaymentStatusEnum)
|
||||
// paymentStatus?: PaymentStatusEnum;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
search?: string;
|
||||
|
||||
@ApiPropertyOptional({ format: 'date-time' })
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
from?: string;
|
||||
|
||||
@ApiPropertyOptional({ format: 'date-time' })
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
to?: string;
|
||||
|
||||
@ApiPropertyOptional({ default: 'createdAt' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
orderBy: string = 'createdAt';
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: sortOrderOptions,
|
||||
default: 'desc',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsIn(sortOrderOptions)
|
||||
order: SortOrder = 'desc';
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { IsOptional, IsString, IsEnum, IsDateString } from 'class-validator';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export enum ChartPeriodEnum {
|
||||
Daily = 'daily',
|
||||
Weekly = 'weekly',
|
||||
Monthly = 'monthly',
|
||||
}
|
||||
|
||||
export class PaymentChartDto {
|
||||
@ApiPropertyOptional({
|
||||
description: 'Start date for filtering (ISO date string)',
|
||||
type: String,
|
||||
format: 'date-time',
|
||||
example: '2024-01-01T00:00:00Z',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
startDate?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'End date for filtering (ISO date string)',
|
||||
type: String,
|
||||
format: 'date-time',
|
||||
example: '2024-12-31T23:59:59Z',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
endDate?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Period type for grouping data',
|
||||
enum: ChartPeriodEnum,
|
||||
default: ChartPeriodEnum.Daily,
|
||||
example: ChartPeriodEnum.Daily,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(ChartPeriodEnum)
|
||||
type?: ChartPeriodEnum = ChartPeriodEnum.Daily;
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreatePaymentMethodDto } from './create-payment-method.dto';
|
||||
|
||||
export class UpdatePaymentMethodDto extends PartialType(CreatePaymentMethodDto) {}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { IsNumber } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UpdatePaymentDto {
|
||||
@ApiProperty({ description: 'Payment ID' })
|
||||
@IsNumber()
|
||||
id: number;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { IsString } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class VerifyPaymentDto {
|
||||
@ApiProperty({ description: 'Payment authority token' })
|
||||
@IsString()
|
||||
authority: string;
|
||||
|
||||
@ApiProperty({ description: 'Payment order id' })
|
||||
@IsString()
|
||||
orderId: string;
|
||||
}
|
||||
@@ -1,10 +1,105 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||
import { EntityManager, EntityRepository, FilterQuery } from '@mikro-orm/postgresql';
|
||||
import { Payment } from '../entities/payment.entity';
|
||||
import { FindPaymentsDto } from '../dto/find-payments.dto';
|
||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||
import { PaymentStatusEnum } from '../interface/payment';
|
||||
|
||||
class FindPaymentsOpts extends FindPaymentsDto {
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class PaymentRepository extends EntityRepository<Payment> {
|
||||
constructor(readonly em: EntityManager) {
|
||||
super(em, Payment);
|
||||
}
|
||||
|
||||
async findAllPaginated(opts: FindPaymentsOpts): Promise<PaginatedResult<Payment>> {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
statuses,
|
||||
search,
|
||||
orderBy = 'createdAt',
|
||||
order = 'desc',
|
||||
userId,
|
||||
from,
|
||||
to
|
||||
} = opts;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const where: FilterQuery<Payment> = {};
|
||||
|
||||
// Filter by statuses
|
||||
if (statuses) {
|
||||
// Ensure statuses is always an array and normalize it
|
||||
const normalizedStatuses = Array.isArray(statuses) ? statuses : [statuses];
|
||||
// Filter out any empty values
|
||||
const validStatuses = normalizedStatuses.filter((s): s is PaymentStatusEnum => !!s);
|
||||
|
||||
if (validStatuses.length > 0) {
|
||||
// Use direct equality for single value, $in for multiple values to avoid SQL generation issues
|
||||
if (validStatuses.length === 1) {
|
||||
where.status = validStatuses[0];
|
||||
} else {
|
||||
where.status = { $in: validStatuses };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
where.order = { user: { id: userId } };
|
||||
}
|
||||
|
||||
// Filter by date range
|
||||
if (from || to) {
|
||||
where.createdAt = {};
|
||||
if (from) {
|
||||
where.createdAt.$gte = new Date(from);
|
||||
}
|
||||
if (to) {
|
||||
where.createdAt.$lte = new Date(to);
|
||||
}
|
||||
}
|
||||
|
||||
// Search by order number or user information
|
||||
if (search) {
|
||||
const searchPattern = `%${search}%`;
|
||||
const searchConditions: any[] = [
|
||||
{ user: { firstName: { $ilike: searchPattern } } },
|
||||
{ user: { lastName: { $ilike: searchPattern } } },
|
||||
{ user: { phone: { $ilike: searchPattern } } },
|
||||
];
|
||||
|
||||
// If search is numeric, also search by orderNumber
|
||||
const numericSearch = Number(search);
|
||||
if (!isNaN(numericSearch) && isFinite(numericSearch)) {
|
||||
searchConditions.push({ orderNumber: numericSearch });
|
||||
}
|
||||
|
||||
where.$or = searchConditions;
|
||||
}
|
||||
|
||||
// First, fetch orders without reviews
|
||||
const [data, total] = await this.findAndCount(where, {
|
||||
limit,
|
||||
offset,
|
||||
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||
populate: [],
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
return {
|
||||
data,
|
||||
meta: {
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { CreditRepository } from 'src/modules/user/repositories/credit.repositor
|
||||
import { CreditService } from 'src/modules/user/providers/credit.service';
|
||||
import { CreditTransactionType } from 'src/modules/user/interface/credit';
|
||||
import { Payment } from '../entities/payment.entity';
|
||||
import { FindPaymentsDto } from '../dto/find-payments.dto';
|
||||
|
||||
|
||||
|
||||
@@ -170,11 +171,10 @@ export class PaymentService {
|
||||
};
|
||||
}
|
||||
|
||||
async verifyOnlinePayment(transactionId: string): Promise<Payment> {
|
||||
async verifyOnlinePayment(token: string): Promise<Payment> {
|
||||
const payment = await this.em.transactional(async em => {
|
||||
const payment = await em.findOne(
|
||||
Payment,
|
||||
{ transactionId },
|
||||
const payment = await this.paymentRepository.findOne(
|
||||
{ token },
|
||||
{ populate: ['order'] },
|
||||
);
|
||||
|
||||
@@ -222,17 +222,17 @@ export class PaymentService {
|
||||
return payment;
|
||||
}
|
||||
|
||||
// findAllPaymentsBy(: string, userId: string) {
|
||||
// return this.em.find(
|
||||
// Payment,
|
||||
// { order: { restaurant: { id: }, user: { id: userId } } },
|
||||
// { populate: ['order', 'order.paymentMethod'] },
|
||||
// );
|
||||
// }
|
||||
findAll(dto: FindPaymentsDto) {
|
||||
return this.paymentRepository.findAllPaginated(dto)
|
||||
}
|
||||
|
||||
async verifyCashPayment(id: string): Promise<Payment> {
|
||||
findAllByUser(dto: FindPaymentsDto, userId: string) {
|
||||
return this.paymentRepository.findAllPaginated({ ...dto, userId })
|
||||
}
|
||||
|
||||
async verifyCashPayment(paymentId: string): Promise<Payment> {
|
||||
const payment = await this.em.transactional(async em => {
|
||||
const payment = await em.findOne(Payment, id, { populate: ['order'] });
|
||||
const payment = await em.findOne(Payment, paymentId, { populate: ['order'] });
|
||||
if (!payment) {
|
||||
throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user