fix pagination et filter
This commit is contained in:
parent
0af15e26fc
commit
e4c4383ceb
31
src/common/dto/pagination.dto.ts
Normal file
31
src/common/dto/pagination.dto.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import { IsInt, IsOptional, Min, Max } from 'class-validator';
|
||||||
|
|
||||||
|
export class PaginationDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Page number',
|
||||||
|
minimum: 1,
|
||||||
|
default: 1,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
page?: number = 1;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Number of items per page',
|
||||||
|
minimum: 1,
|
||||||
|
maximum: 100,
|
||||||
|
default: 10,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(100)
|
||||||
|
limit?: number = 10;
|
||||||
|
}
|
||||||
11
src/common/interfaces/paginated-response.interface.ts
Normal file
11
src/common/interfaces/paginated-response.interface.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface PaginatedResponse<T> {
|
||||||
|
data: T[];
|
||||||
|
meta: {
|
||||||
|
total: number;
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
totalPages: number;
|
||||||
|
hasNextPage: boolean;
|
||||||
|
hasPreviousPage: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -7,9 +7,12 @@ import {
|
|||||||
IsEnum,
|
IsEnum,
|
||||||
IsDateString,
|
IsDateString,
|
||||||
isNumber,
|
isNumber,
|
||||||
|
IsInt,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
|
import { PaymentType, TransactionStatus } from 'generated/prisma';
|
||||||
|
import { PaginationDto } from 'src/common/dto/pagination.dto';
|
||||||
|
|
||||||
export class ChargeDto {
|
export class ChargeDto {
|
||||||
@ApiProperty({ description: 'User token from authentication' })
|
@ApiProperty({ description: 'User token from authentication' })
|
||||||
@ -77,46 +80,6 @@ export class RefundDto {
|
|||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PaymentQueryDto {
|
|
||||||
@ApiProperty({ required: false, enum: ['PENDING', 'SUCCESS', 'FAILED', 'REFUNDED'] })
|
|
||||||
@IsOptional()
|
|
||||||
@IsEnum(['PENDING', 'SUCCESS', 'FAILED', 'REFUNDED'])
|
|
||||||
status?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
userId?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
|
||||||
@IsOptional()
|
|
||||||
@IsNumber()
|
|
||||||
subscriptionId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
|
||||||
@IsOptional()
|
|
||||||
@IsDateString()
|
|
||||||
startDate?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
|
||||||
@IsOptional()
|
|
||||||
@IsDateString()
|
|
||||||
endDate?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ required: false, default: 1 })
|
|
||||||
@IsOptional()
|
|
||||||
@Type(() => Number)
|
|
||||||
@IsNumber()
|
|
||||||
@Min(1)
|
|
||||||
page?: number = 1;
|
|
||||||
|
|
||||||
@ApiProperty({ required: false, default: 20 })
|
|
||||||
@IsOptional()
|
|
||||||
@Type(() => Number)
|
|
||||||
@IsNumber()
|
|
||||||
@Min(1)
|
|
||||||
limit?: number = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PaymentResponseDto {
|
export class PaymentResponseDto {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@ -168,3 +131,156 @@ export class PaymentListResponseDto {
|
|||||||
totalPages: number;
|
totalPages: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class PaymentQueryDto extends PaginationDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by payment type',
|
||||||
|
enum: PaymentType,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(PaymentType)
|
||||||
|
type?: PaymentType;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by transaction status',
|
||||||
|
enum: TransactionStatus,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(TransactionStatus)
|
||||||
|
status?: TransactionStatus;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by merchant partner ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
merchantPartnerId?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by customer ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
customerId?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by subscription ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
subscriptionId?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Search by external reference',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
externalReference?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Search by payment reference',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
reference?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by currency code (e.g., XOF, XAF, EUR)',
|
||||||
|
example: 'XOF',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
currency?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter payments with amount greater than or equal to this value',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
amountMin?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter payments with amount less than or equal to this value',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
amountMax?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter payments created from this date (ISO format)',
|
||||||
|
example: '2024-01-01T00:00:00Z',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
createdFrom?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter payments created until this date (ISO format)',
|
||||||
|
example: '2024-12-31T23:59:59Z',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
createdTo?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter payments completed from this date (ISO format)',
|
||||||
|
example: '2024-01-01T00:00:00Z',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
completedFrom?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter payments completed until this date (ISO format)',
|
||||||
|
example: '2024-12-31T23:59:59Z',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
completedTo?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter only failed payments (with failure reason)',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Boolean)
|
||||||
|
hasFailureReason?: boolean;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Sort field',
|
||||||
|
enum: ['createdAt', 'completedAt', 'amount'],
|
||||||
|
default: 'createdAt',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
sortBy?: 'createdAt' | 'completedAt' | 'amount' = 'createdAt';
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Sort order',
|
||||||
|
enum: ['asc', 'desc'],
|
||||||
|
default: 'desc',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(['asc', 'desc'])
|
||||||
|
sortOrder?: 'asc' | 'desc' = 'desc';
|
||||||
|
}
|
||||||
@ -125,23 +125,32 @@ export class PaymentsController {
|
|||||||
|
|
||||||
|
|
||||||
@Get('/')
|
@Get('/')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Get payment list with pagination and filters',
|
||||||
|
description: 'Retrieve payments with optional filters on status, type, dates, amounts, etc.'
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Paginated list of payments',
|
||||||
|
})
|
||||||
@ApiOperation({ summary: 'Get payments list' })
|
@ApiOperation({ summary: 'Get payments list' })
|
||||||
async getAll(@Request() req) {
|
async getAll(@Request() req, @Query() paymentQueryDto: PaymentQueryDto) {
|
||||||
return this.paymentsService.findAll();
|
return this.paymentsService.findAll(paymentQueryDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('merchant/:merchantId')
|
@Get('merchant/:merchantId')
|
||||||
@ApiOperation({ summary: 'Get payments list by merchant' })
|
@ApiOperation({ summary: 'Get payments list by merchant' })
|
||||||
async getAllByPaymentByMerchant(@Request() req, @Param('merchantId', ParseIntPipe) merchantId: number) {
|
async getAllByPaymentByMerchant(@Request() req, @Param('merchantId', ParseIntPipe) merchantId: number, @Query() queryDto: Omit<PaymentQueryDto, 'merchantPartnerId'>,) {
|
||||||
return this.paymentsService.findAllByMerchant(merchantId);
|
return this.paymentsService.findAll({ ...queryDto, merchantPartnerId: merchantId });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('merchant/:merchantId/subscription/:subscriptionId')
|
@Get('merchant/:merchantId/subscription/:subscriptionId')
|
||||||
@ApiOperation({ summary: 'Get payments list by merchant' })
|
@ApiOperation({ summary: 'Get payments list by merchant' })
|
||||||
async getAllBySubscription(@Request() req,
|
async getAllBySubscription(@Request() req,
|
||||||
@Param('merchantId', ParseIntPipe) merchantId: number,
|
@Param('merchantId', ParseIntPipe) merchantId: number,
|
||||||
@Param('subscriptionId', ParseIntPipe) subscriptionId: number) {
|
@Param('subscriptionId', ParseIntPipe) subscriptionId: number,
|
||||||
return this.paymentsService.findAllByMerchantSubscription(merchantId,subscriptionId);
|
@Query() queryDto: Omit<PaymentQueryDto, 'merchantPartnerId' | 'subscriptionId'>,) {
|
||||||
|
return this.paymentsService.findAll({ ...queryDto, merchantPartnerId: merchantId, subscriptionId: subscriptionId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import { Injectable, BadRequestException, Logger } from '@nestjs/common';
|
|||||||
import { OperatorsService } from '../operators/operators.service';
|
import { OperatorsService } from '../operators/operators.service';
|
||||||
import { PrismaService } from '../../shared/services/prisma.service';
|
import { PrismaService } from '../../shared/services/prisma.service';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { ChargeDto } from './dto/payment.dto';
|
import { ChargeDto, PaymentQueryDto } from './dto/payment.dto';
|
||||||
import { RefundDto } from './dto/payment.dto';
|
import { RefundDto } from './dto/payment.dto';
|
||||||
import { PaymentType, TransactionStatus } from 'generated/prisma';
|
import { Payment, PaymentType, Prisma, TransactionStatus } from 'generated/prisma';
|
||||||
|
import { PaginatedResponse } from 'src/common/interfaces/paginated-response.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentsService {
|
export class PaymentsService {
|
||||||
@ -64,17 +65,149 @@ export class PaymentsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<any[]> {
|
async findAll(
|
||||||
// Check if merchant exists
|
queryDto: PaymentQueryDto,
|
||||||
return this.prisma.payment.findMany({
|
): Promise<PaginatedResponse<Payment>> {
|
||||||
// where: { merchantPartnerId: merchantId },
|
const {
|
||||||
|
page = 1,
|
||||||
|
limit = 10,
|
||||||
|
sortBy = 'createdAt',
|
||||||
|
sortOrder = 'desc',
|
||||||
|
} = queryDto;
|
||||||
|
|
||||||
orderBy: {
|
const skip = (page - 1) * limit;
|
||||||
createdAt: 'desc',
|
const where = this.buildWhereClause(queryDto);
|
||||||
|
|
||||||
|
// Construction du orderBy dynamique
|
||||||
|
const orderBy: Prisma.PaymentOrderByWithRelationInput = {
|
||||||
|
[sortBy]: sortOrder,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exécuter les requêtes en parallèle
|
||||||
|
const [payments, total] = await Promise.all([
|
||||||
|
this.prisma.payment.findMany({
|
||||||
|
where,
|
||||||
|
skip,
|
||||||
|
take: limit,
|
||||||
|
orderBy,
|
||||||
|
// Optionnel: inclure les relations
|
||||||
|
// include: {
|
||||||
|
// merchantPartner: true,
|
||||||
|
// subscription: true,
|
||||||
|
// reversementRequests: true,
|
||||||
|
// },
|
||||||
|
}),
|
||||||
|
this.prisma.payment.count({ where }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: payments,
|
||||||
|
meta: {
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
totalPages,
|
||||||
|
hasNextPage: page < totalPages,
|
||||||
|
hasPreviousPage: page > 1,
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private buildWhereClause(
|
||||||
|
filters: Partial<PaymentQueryDto>
|
||||||
|
): Prisma.PaymentWhereInput {
|
||||||
|
const {
|
||||||
|
type,
|
||||||
|
status,
|
||||||
|
merchantPartnerId,
|
||||||
|
customerId,
|
||||||
|
subscriptionId,
|
||||||
|
externalReference,
|
||||||
|
reference,
|
||||||
|
currency,
|
||||||
|
amountMin,
|
||||||
|
amountMax,
|
||||||
|
createdFrom,
|
||||||
|
createdTo,
|
||||||
|
completedFrom,
|
||||||
|
completedTo,
|
||||||
|
hasFailureReason,
|
||||||
|
} = filters;
|
||||||
|
|
||||||
|
const where: Prisma.PaymentWhereInput = {};
|
||||||
|
|
||||||
|
// Filtres simples
|
||||||
|
if (type) where.type = type;
|
||||||
|
if (status) where.status = status;
|
||||||
|
if (merchantPartnerId) where.merchantPartnerId = merchantPartnerId;
|
||||||
|
if (customerId) where.customerId = customerId;
|
||||||
|
if (subscriptionId) where.subscriptionId = subscriptionId;
|
||||||
|
if (currency) where.currency = currency;
|
||||||
|
|
||||||
|
// Filtres de recherche par référence
|
||||||
|
if (externalReference) {
|
||||||
|
where.externalReference = {
|
||||||
|
contains: externalReference,
|
||||||
|
mode: 'insensitive',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reference) {
|
||||||
|
where.reference = {
|
||||||
|
contains: reference,
|
||||||
|
mode: 'insensitive',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtre sur les montants
|
||||||
|
if (amountMin !== undefined || amountMax !== undefined) {
|
||||||
|
where.amount = {};
|
||||||
|
if (amountMin !== undefined) {
|
||||||
|
where.amount.gte = amountMin;
|
||||||
|
}
|
||||||
|
if (amountMax !== undefined) {
|
||||||
|
where.amount.lte = amountMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtres sur createdAt
|
||||||
|
if (createdFrom || createdTo) {
|
||||||
|
where.createdAt = {};
|
||||||
|
if (createdFrom) {
|
||||||
|
where.createdAt.gte = new Date(createdFrom);
|
||||||
|
}
|
||||||
|
if (createdTo) {
|
||||||
|
where.createdAt.lte = new Date(createdTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtres sur completedAt
|
||||||
|
if (completedFrom || completedTo) {
|
||||||
|
where.completedAt = {};
|
||||||
|
if (completedFrom) {
|
||||||
|
where.completedAt.gte = new Date(completedFrom);
|
||||||
|
}
|
||||||
|
if (completedTo) {
|
||||||
|
where.completedAt.lte = new Date(completedTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtre sur les paiements échoués
|
||||||
|
if (hasFailureReason !== undefined) {
|
||||||
|
if (hasFailureReason) {
|
||||||
|
where.failureReason = { not: null };
|
||||||
|
} else {
|
||||||
|
where.failureReason = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
refundPayment(paymentId: string, partnerId: any, refundDto: RefundDto) {
|
refundPayment(paymentId: string, partnerId: any, refundDto: RefundDto) {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { IsString, IsOptional, IsNumber, IsEnum, IsBoolean, Min } from 'class-validator';
|
import { IsString, IsOptional, IsNumber, IsEnum,IsDateString, IsBoolean, Min, IsInt } from 'class-validator';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { PaginationDto } from 'src/common/dto/pagination.dto';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import { Periodicity, SubscriptionStatus } from 'generated/prisma';
|
||||||
|
|
||||||
export class CreateSubscriptionDto {
|
export class CreateSubscriptionDto {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@ -45,3 +48,122 @@ export class UpdateSubscriptionDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SubscriptionQueryDto extends PaginationDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by merchant partner ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
merchantPartnerId?: number;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by subscription status',
|
||||||
|
enum: SubscriptionStatus,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(SubscriptionStatus)
|
||||||
|
status?: SubscriptionStatus;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by periodicity',
|
||||||
|
enum: Periodicity,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(Periodicity)
|
||||||
|
periodicity?: Periodicity;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by service ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
serviceId?: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions starting from this date (ISO format)',
|
||||||
|
example: '2024-01-01',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
startDateFrom?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions starting until this date (ISO format)',
|
||||||
|
example: '2024-12-31',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
startDateTo?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions ending from this date (ISO format)',
|
||||||
|
example: '2024-01-01',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
endDateFrom?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions ending until this date (ISO format)',
|
||||||
|
example: '2024-12-31',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
endDateTo?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions created from this date (ISO format)',
|
||||||
|
example: '2024-01-01T00:00:00Z',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
createdFrom?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions created until this date (ISO format)',
|
||||||
|
example: '2024-12-31T23:59:59Z',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
createdTo?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions with next payment from this date',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
nextPaymentFrom?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter subscriptions with next payment until this date',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
nextPaymentTo?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Filter by customer ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
customerId?: number;
|
||||||
|
}
|
||||||
@ -13,10 +13,11 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
ParseIntPipe,
|
ParseIntPipe,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
import { ApiTags, ApiOperation, ApiQuery,ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
|
||||||
import { SubscriptionsService } from './subscriptions.service';
|
import { SubscriptionsService } from './subscriptions.service';
|
||||||
import { CreateSubscriptionDto, UpdateSubscriptionDto } from './dto/subscription.dto';
|
import { CreateSubscriptionDto, SubscriptionQueryDto, UpdateSubscriptionDto } from './dto/subscription.dto';
|
||||||
import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard';
|
||||||
|
import { PaginationDto } from 'src/common/dto/pagination.dto';
|
||||||
|
|
||||||
@ApiTags('subscriptions')
|
@ApiTags('subscriptions')
|
||||||
@Controller('subscriptions')
|
@Controller('subscriptions')
|
||||||
@ -41,15 +42,21 @@ export class SubscriptionsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('/')
|
@Get('/')
|
||||||
@ApiOperation({ summary: 'Get subscription list' })
|
@ApiOperation({ summary: 'Get subscription list with pagination' })
|
||||||
async getAll(@Request() req) {
|
@ApiQuery({ type: PaginationDto })
|
||||||
return this.subscriptionsService.findAll();
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Paginated list of subscriptions',
|
||||||
|
})
|
||||||
|
async getAll(@Request() req, @Query() paginationDto: SubscriptionQueryDto,) {
|
||||||
|
return this.subscriptionsService.findAll(paginationDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('merchant/:merchantId')
|
@Get('merchant/:merchantId')
|
||||||
@ApiOperation({ summary: 'Get subscription list by merchant' })
|
@ApiOperation({ summary: 'Get subscription list by merchant' })
|
||||||
async getAllByMErchant(@Request() req, @Param('merchantId', ParseIntPipe) merchantId: number) {
|
async getAllByMErchant(@Request() req, @Param('merchantId', ParseIntPipe,) merchantId: number, paginationDto: Omit<SubscriptionQueryDto, 'merchantPartnerId'> ,) {
|
||||||
return this.subscriptionsService.findAllByMerchant(merchantId);
|
const page = {...paginationDto, merchantPartnerId: merchantId};
|
||||||
|
return this.subscriptionsService.findAll(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,11 @@ import { InjectQueue } from '@nestjs/bull';
|
|||||||
import bull from 'bull';
|
import bull from 'bull';
|
||||||
import { PrismaService } from '../../shared/services/prisma.service';
|
import { PrismaService } from '../../shared/services/prisma.service';
|
||||||
import { PaymentsService } from '../payments/payments.service';
|
import { PaymentsService } from '../payments/payments.service';
|
||||||
import { CreateSubscriptionDto, UpdateSubscriptionDto } from './dto/subscription.dto';
|
import { CreateSubscriptionDto, SubscriptionQueryDto, UpdateSubscriptionDto } from './dto/subscription.dto';
|
||||||
import { Subscription } from 'generated/prisma';
|
import { Prisma, Subscription } from 'generated/prisma';
|
||||||
import { OperatorsService } from '../operators/operators.service';
|
import { OperatorsService } from '../operators/operators.service';
|
||||||
|
import { PaginationDto } from 'src/common/dto/pagination.dto';
|
||||||
|
import { PaginatedResponse } from 'src/common/interfaces/paginated-response.interface';
|
||||||
//import { SubscriptionStatus } from '@prisma/client';
|
//import { SubscriptionStatus } from '@prisma/client';
|
||||||
//import { SubscriptionStatus, Prisma } from '@prisma/client';
|
//import { SubscriptionStatus, Prisma } from '@prisma/client';
|
||||||
|
|
||||||
@ -46,16 +48,122 @@ export class SubscriptionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<Subscription[]> {
|
async findAll( paginationDto: SubscriptionQueryDto,): Promise<PaginatedResponse<Subscription>> {
|
||||||
|
const { page = 1, limit = 10, status,
|
||||||
|
periodicity,
|
||||||
|
merchantPartnerId,
|
||||||
|
customerId,
|
||||||
|
serviceId,
|
||||||
|
startDateFrom,
|
||||||
|
startDateTo,
|
||||||
|
endDateFrom,
|
||||||
|
endDateTo,
|
||||||
|
createdFrom,
|
||||||
|
createdTo,
|
||||||
|
nextPaymentFrom,
|
||||||
|
nextPaymentTo, } = paginationDto;
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
// Construction du where clause dynamique
|
||||||
|
const where: Prisma.SubscriptionWhereInput = {};
|
||||||
|
|
||||||
|
// Filtre par status
|
||||||
|
if (status) {
|
||||||
|
where.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtre par periodicity
|
||||||
|
if (periodicity) {
|
||||||
|
where.periodicity = periodicity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtre par IDs
|
||||||
|
if (merchantPartnerId) {
|
||||||
|
where.merchantPartnerId = merchantPartnerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customerId) {
|
||||||
|
where.customerId = customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceId) {
|
||||||
|
where.serviceId = serviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtres sur startDate
|
||||||
|
if (startDateFrom || startDateTo) {
|
||||||
|
where.startDate = {};
|
||||||
|
if (startDateFrom) {
|
||||||
|
where.startDate.gte = new Date(startDateFrom);
|
||||||
|
}
|
||||||
|
if (startDateTo) {
|
||||||
|
where.startDate.lte = new Date(startDateTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtres sur endDate
|
||||||
|
if (endDateFrom || endDateTo) {
|
||||||
|
where.endDate = {};
|
||||||
|
if (endDateFrom) {
|
||||||
|
where.endDate.gte = new Date(endDateFrom);
|
||||||
|
}
|
||||||
|
if (endDateTo) {
|
||||||
|
where.endDate.lte = new Date(endDateTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtres sur createdAt
|
||||||
|
if (createdFrom || createdTo) {
|
||||||
|
where.createdAt = {};
|
||||||
|
if (createdFrom) {
|
||||||
|
where.createdAt.gte = new Date(createdFrom);
|
||||||
|
}
|
||||||
|
if (createdTo) {
|
||||||
|
where.createdAt.lte = new Date(createdTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtres sur nextPaymentDate
|
||||||
|
if (nextPaymentFrom || nextPaymentTo) {
|
||||||
|
where.nextPaymentDate = {};
|
||||||
|
if (nextPaymentFrom) {
|
||||||
|
where.nextPaymentDate.gte = new Date(nextPaymentFrom);
|
||||||
|
}
|
||||||
|
if (nextPaymentTo) {
|
||||||
|
where.nextPaymentDate.lte = new Date(nextPaymentTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Check if merchant exists
|
// Check if merchant exists
|
||||||
|
const [subscriptions, total] = await Promise.all([
|
||||||
return this.prisma.subscription.findMany({
|
this.prisma.subscription.findMany({
|
||||||
// where: { merchantPartnerId: merchantId },
|
where,
|
||||||
|
skip,
|
||||||
|
take: limit,
|
||||||
orderBy: {
|
orderBy: {
|
||||||
createdAt: 'desc',
|
createdAt: 'desc',
|
||||||
},
|
},
|
||||||
});
|
// Vous pouvez inclure des relations si nécessaire
|
||||||
|
// include: {
|
||||||
|
// merchantPartner: true,
|
||||||
|
// service: true,
|
||||||
|
// },
|
||||||
|
}),
|
||||||
|
this.prisma.subscription.count(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: subscriptions,
|
||||||
|
meta: {
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
totalPages,
|
||||||
|
hasNextPage: page < totalPages,
|
||||||
|
hasPreviousPage: page > 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getInvoices(id: string, partnerId: any) {
|
getInvoices(id: string, partnerId: any) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user