list of card to card

This commit is contained in:
2026-07-11 20:56:38 +03:30
parent 8b2d1b0a47
commit bf4e4c8d65
4 changed files with 126 additions and 0 deletions
@@ -27,6 +27,8 @@ import { SalonSubscriptionsService } from '../subscriptions/salon-subscriptions.
import { UploaderService } from '../uploader/uploader.service'; import { UploaderService } from '../uploader/uploader.service';
import { UserRole } from '../users/enums/user-role.enum'; import { UserRole } from '../users/enums/user-role.enum';
import { ShortLinksService } from '../short-links/short-links.service'; import { ShortLinksService } from '../short-links/short-links.service';
import { DepositListItemDto } from './dto/deposit-list-item.dto';
import { FindDepositsQueryDto } from './dto/find-deposits-query.dto';
import { PublicDepositPaymentDto } from './dto/public-deposit-payment.dto'; import { PublicDepositPaymentDto } from './dto/public-deposit-payment.dto';
import { ReviewDepositDto } from './dto/review-deposit.dto'; import { ReviewDepositDto } from './dto/review-deposit.dto';
import { Payment } from './entities/payment.entity'; import { Payment } from './entities/payment.entity';
@@ -253,6 +255,43 @@ export class CardToCardDepositService {
return this.getPublicPaymentByToken(token); return this.getPublicPaymentByToken(token);
} }
async findForSalon(
user: AuthUser,
query: FindDepositsQueryDto,
): Promise<DepositListItemDto[]> {
if (user.role !== UserRole.SALON && user.role !== UserRole.ADMIN) {
throw new ForbiddenException();
}
const qb = this.paymentsRepository
.createQueryBuilder('payment')
.innerJoinAndSelect('payment.reserve', 'reserve')
.innerJoinAndSelect('reserve.salon', 'salon')
.leftJoinAndSelect('reserve.customer', 'customer')
.leftJoinAndSelect('customer.user', 'customerUser')
.where('payment.type = :type', { type: PaymentType.DEPOSIT })
.andWhere('payment.method = :method', {
method: PaymentMethod.CARD_TO_CARD,
})
.orderBy('payment.createdAt', 'DESC');
if (query.status) {
qb.andWhere('payment.status = :status', { status: query.status });
}
if (!this.authorizationService.isAdmin(user)) {
qb.andWhere('salon.ownerId = :ownerId', { ownerId: user.id });
}
const limit = query.limit ?? 50;
const offset = query.offset ?? 0;
qb.take(limit).skip(offset);
const payments = await qb.getMany();
return payments.map((payment) => this.toDepositListItem(payment));
}
async findForReview(paymentId: string, user: AuthUser): Promise<Payment> { async findForReview(paymentId: string, user: AuthUser): Promise<Payment> {
const payment = await this.loadPaymentWithRelations(paymentId); const payment = await this.loadPaymentWithRelations(paymentId);
this.assertCanReview(user, payment); this.assertCanReview(user, payment);
@@ -391,4 +430,23 @@ export class CardToCardDepositService {
private rialToToman(amountRial: number): number { private rialToToman(amountRial: number): number {
return Math.round(amountRial / 10); return Math.round(amountRial / 10);
} }
private toDepositListItem(payment: Payment): DepositListItemDto {
const user = payment.reserve?.customer?.user;
const customerName = user
? `${user.firstName} ${user.lastName}`.trim()
: 'مشتری';
return {
id: payment.id,
status: payment.status,
amountToman: this.rialToToman(payment.amountRial),
receiptUrl: payment.receiptUrl,
createdAt: payment.createdAt,
customerName,
customerMobile: user?.mobile ?? null,
appointmentDate: payment.reserve?.appointmentDate ?? null,
startTime: payment.reserve?.startTime ?? null,
};
}
} }
+15
View File
@@ -5,6 +5,7 @@ import {
Param, Param,
ParseUUIDPipe, ParseUUIDPipe,
Post, Post,
Query,
UploadedFile, UploadedFile,
UseGuards, UseGuards,
UseInterceptors, UseInterceptors,
@@ -25,6 +26,8 @@ import { ApiPublicEndpoint } from '../swagger/decorators/swagger-auth.decorator'
import { SWAGGER_JWT_AUTH } from '../swagger/swagger.setup'; import { SWAGGER_JWT_AUTH } from '../swagger/swagger.setup';
import { UserRole } from '../users/enums/user-role.enum'; import { UserRole } from '../users/enums/user-role.enum';
import { CardToCardDepositService } from './card-to-card-deposit.service'; import { CardToCardDepositService } from './card-to-card-deposit.service';
import { DepositListItemDto } from './dto/deposit-list-item.dto';
import { FindDepositsQueryDto } from './dto/find-deposits-query.dto';
import { PublicDepositPaymentDto } from './dto/public-deposit-payment.dto'; import { PublicDepositPaymentDto } from './dto/public-deposit-payment.dto';
import { ReviewDepositDto } from './dto/review-deposit.dto'; import { ReviewDepositDto } from './dto/review-deposit.dto';
import { Payment } from './entities/payment.entity'; import { Payment } from './entities/payment.entity';
@@ -84,6 +87,18 @@ export class DepositsController {
return this.cardToCardDepositService.submitReceipt(token, file); return this.cardToCardDepositService.submitReceipt(token, file);
} }
@ApiBearerAuth(SWAGGER_JWT_AUTH)
@UseGuards(RolesGuard)
@Roles(UserRole.ADMIN, UserRole.SALON)
@ApiStandardOkResponse(DepositListItemDto, { isArray: true })
@Get()
findAll(
@Query() query: FindDepositsQueryDto,
@CurrentUser() user: AuthUser,
) {
return this.cardToCardDepositService.findForSalon(user, query);
}
@ApiBearerAuth(SWAGGER_JWT_AUTH) @ApiBearerAuth(SWAGGER_JWT_AUTH)
@UseGuards(RolesGuard) @UseGuards(RolesGuard)
@Roles(UserRole.ADMIN, UserRole.SALON) @Roles(UserRole.ADMIN, UserRole.SALON)
+31
View File
@@ -0,0 +1,31 @@
import { ApiProperty } from '@nestjs/swagger';
import { PaymentStatus } from '../enums/payment-status.enum';
export class DepositListItemDto {
@ApiProperty()
id: string;
@ApiProperty({ enum: PaymentStatus })
status: PaymentStatus;
@ApiProperty({ example: 500000 })
amountToman: number;
@ApiProperty({ required: false, nullable: true })
receiptUrl: string | null;
@ApiProperty()
createdAt: Date;
@ApiProperty({ example: 'مریم احمدی' })
customerName: string;
@ApiProperty({ required: false, nullable: true, example: '09121234567' })
customerMobile: string | null;
@ApiProperty({ required: false, nullable: true })
appointmentDate: string | null;
@ApiProperty({ required: false, nullable: true })
startTime: string | null;
}
@@ -0,0 +1,22 @@
import { Type } from 'class-transformer';
import { IsEnum, IsInt, IsOptional, Max, Min } from 'class-validator';
import { PaymentStatus } from '../enums/payment-status.enum';
export class FindDepositsQueryDto {
@IsOptional()
@IsEnum(PaymentStatus)
status?: PaymentStatus;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
limit?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(0)
offset?: number;
}