bank account

This commit is contained in:
2026-04-15 10:42:16 +03:30
parent 6f1217f063
commit 2b44b3cb2f
9 changed files with 147 additions and 14 deletions
@@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddUSerBAnkAccount1776232939010 implements MigrationInterface {
name = 'AddUSerBAnkAccount1776232939010'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "user_bank_account" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "cardNumber" character varying(100), "IBan" character varying(100), "bankName" character varying(100) NOT NULL, "accountOwnerName" character varying(150) NOT NULL, "userId" uuid, CONSTRAINT "UQ_731c0d4523b22914c22236d471d" UNIQUE ("cardNumber"), CONSTRAINT "UQ_5fc918bd3726009307a7c5f07a3" UNIQUE ("IBan"), CONSTRAINT "REL_17640f868ef97b2f53b7f7ab0c" UNIQUE ("userId"), CONSTRAINT "PK_080d426b06188408bc1cc508c84" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "user_bank_account" ADD CONSTRAINT "FK_17640f868ef97b2f53b7f7ab0cb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user_bank_account" DROP CONSTRAINT "FK_17640f868ef97b2f53b7f7ab0cb"`);
await queryRunner.query(`DROP TABLE "user_bank_account"`);
}
}
@@ -10,16 +10,14 @@ import { SearchRewardsRequestQueryDto } from "../../reseller/dto/search-reward-r
export class ReferralRewardsRepository extends Repository<ReferralReward> { export class ReferralRewardsRepository extends Repository<ReferralReward> {
constructor(@InjectRepository(ReferralReward) repository: Repository<ReferralReward>) { constructor(@InjectRepository(ReferralReward) repository: Repository<ReferralReward>) {
super(repository.target, repository.manager, repository.queryRunner); super(repository.target, repository.manager, repository.queryRunner);
} }
async findPaginatedList(userIds: string[], queryDto: SearchRewardsRequestQueryDto) { async findPaginatedList(userIds: string[], queryDto: SearchRewardsRequestQueryDto) {
const { limit, skip } = PaginationUtils(queryDto); const { limit, skip } = PaginationUtils(queryDto);
const qb = this.createQueryBuilder("rr")
const qb = this.createQueryBuilder("referral") .where("rr.userId IN (:...userIds)", { userIds })
.where("referral.referrerId IN (:...userIds)", { userIds }) .leftJoinAndSelect("rr.user", "user")
.leftJoinAndSelect("referral.referrer", "referrer") .orderBy("rr.createdAt", "DESC")
.leftJoinAndSelect("referral.referredUser", "referred")
.orderBy("referral.createdAt", "DESC")
.skip(skip) .skip(skip)
.take(limit); .take(limit);
+32
View File
@@ -0,0 +1,32 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsNotEmpty, IsNumberString, IsString, Length } from "class-validator";
import { PaymentMessage } from "../../../common/enums/message.enum";
export class CreateUserBankAccountDto {
@IsNotEmpty({ message: PaymentMessage.CARD_NUMBER_REQUIRED })
@IsNumberString({ no_symbols: true }, { message: PaymentMessage.CARD_NUMBER_NUMBER_STRING })
@Length(16, 16, { message: PaymentMessage.CARD_NUMBER_LENGTH })
@ApiProperty({ description: "card number", example: "1234567812345678" })
cardNumber: string;
@IsNotEmpty({ message: PaymentMessage.IBAN_REQUIRED })
@IsNumberString({ no_symbols: true }, { message: PaymentMessage.IBAN_NUMBER_STRING })
// @IsIBAN({ message: PaymentMessage.IBAN_INVALID })
@Length(24, 24, { message: PaymentMessage.IBAN_INVALID })
@ApiProperty({ description: "IBAN number", example: "123456789012345678901234" })
IBan: string;
@IsNotEmpty({ message: PaymentMessage.BANK_NAME_REQUIRED })
@IsString({ message: PaymentMessage.BANK_NAME_STRING })
@Length(2, 100, { message: PaymentMessage.BANK_NAME_LENGTH })
@ApiProperty({ description: "bank name", example: "Example Bank" })
bankName: string;
@IsNotEmpty({ message: PaymentMessage.ACCOUNT_OWNER_NAME_REQUIRED })
@IsString({ message: PaymentMessage.ACCOUNT_OWNER_NAME_STRING })
@Length(3, 150, { message: PaymentMessage.ACCOUNT_OWNER_NAME_LENGTH })
@ApiProperty({ description: "account owner name", example: "John Doe" })
accountOwnerName: string;
}
+25
View File
@@ -0,0 +1,25 @@
import { Column, Entity, JoinColumn, OneToOne } from "typeorm";
import { BaseEntity } from "../../../common/entities/base.entity";
import { User } from "../../users/entities/user.entity";
@Entity()
export class UserBankAccount extends BaseEntity {
@Column({ type: "varchar", length: 100, unique: true, nullable: true })
cardNumber?: string;
@Column({ type: "varchar", length: 100, unique: true, nullable: true })
IBan?: string;
@Column({ type: "varchar", length: 100, nullable: false })
bankName: string;
@Column({ type: "varchar", length: 150, nullable: false })
accountOwnerName: string;
//----------------------
@OneToOne(() => User, (user) => user.bankAckount)
@JoinColumn()
user?: User
}
@@ -0,0 +1,12 @@
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { UserBankAccount } from "../entities/user-bank-account.entity";
@Injectable()
export class UserBankAccountRepository extends Repository<UserBankAccount> {
constructor(@InjectRepository(UserBankAccount) repository: Repository<UserBankAccount>) {
super(repository.target, repository.manager, repository.queryRunner);
}
}
+19 -2
View File
@@ -15,6 +15,7 @@ import { SearchResellerInvoicesQueryDto } from './dto/search-reseller-invoices.d
import { PaginationDto } from '../../common/DTO/pagination.dto'; import { PaginationDto } from '../../common/DTO/pagination.dto';
import { WalletsService } from '../wallets/providers/wallets.service'; import { WalletsService } from '../wallets/providers/wallets.service';
import { CreateWithdrawRequestDto } from './dto/create-withdraw-request.dto'; import { CreateWithdrawRequestDto } from './dto/create-withdraw-request.dto';
import { CreateUserBankAccountDto } from './dto/create-user-bank-account.dto';
@Controller('reseller') @Controller('reseller')
@AuthGuards() @AuthGuards()
@@ -127,8 +128,24 @@ export class ResellerController {
@ApiOperation({ summary: "remove Reseller/Agent withdraw request ==> reseller route" }) @ApiOperation({ summary: "remove Reseller/Agent withdraw request ==> reseller route" })
@ResellerRoute() @ResellerRoute()
@Delete('wallet/withdraw/:id') @Delete('wallet/withdraw/:id')
removeWithdrawRequest(@UserDec("id") userId: string) { removeWithdrawRequest(@UserDec("id") userId: string, @Param('id') id: string) {
return this.walletsService.getBalance(userId) return this.resellerService.removeWithdrawRequest(userId, id)
}
// ============ Bank Account =============
@ApiOperation({ summary: "Create Bank Account ==> reseller route" })
@ResellerRoute()
@Post('bank-account')
createBankAccount(@UserDec("id") userId: string, @Body() dto: CreateUserBankAccountDto) {
return this.resellerService.createUserBankAccount(userId, dto)
}
// ============ Remove Bank Account =============
@ApiOperation({ summary: "remove User Bank account ==> reseller route" })
@ResellerRoute()
@Delete('bank-account/:id')
removeBankAccount(@UserDec("id") userId: string, @Param('id') id: string) {
return this.resellerService.deleteUserBankAccount(userId, id)
} }
} }
+4 -2
View File
@@ -10,15 +10,17 @@ import { InvoicesModule } from '../invoices/invoices.module';
import { RewardWithdrawRequest } from "../reseller/entities/rewards-withdraw-request.entity"; import { RewardWithdrawRequest } from "../reseller/entities/rewards-withdraw-request.entity";
import { WithdrawRequestRepository } from './repositories/withdraw-request.repository'; import { WithdrawRequestRepository } from './repositories/withdraw-request.repository';
import { WalletsModule } from '../wallets/wallets.module'; import { WalletsModule } from '../wallets/wallets.module';
import { UserBankAccount } from './entities/user-bank-account.entity';
import { UserBankAccountRepository } from './repositories/user-bank-account.repository';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([Reseller, RewardWithdrawRequest]), imports: [TypeOrmModule.forFeature([Reseller, RewardWithdrawRequest, UserBankAccount]),
UsersModule, UsersModule,
ReferralsModule, ReferralsModule,
InvoicesModule, InvoicesModule,
WalletsModule WalletsModule
], ],
controllers: [ResellerController], controllers: [ResellerController],
providers: [ResellerService, ResellerRepository, WithdrawRequestRepository], providers: [ResellerService, ResellerRepository, WithdrawRequestRepository, UserBankAccountRepository],
}) })
export class ResellerModule { } export class ResellerModule { }
+28 -3
View File
@@ -9,6 +9,8 @@ import { SearchRewardsRequestQueryDto } from './dto/search-reward-requests.dto';
import { WithdrawRequestRepository } from './repositories/withdraw-request.repository'; import { WithdrawRequestRepository } from './repositories/withdraw-request.repository';
import { SearchCustomersQueryDto } from './dto/search-customers.dto'; import { SearchCustomersQueryDto } from './dto/search-customers.dto';
import { SearchResellerInvoicesQueryDto } from './dto/search-reseller-invoices.dto'; import { SearchResellerInvoicesQueryDto } from './dto/search-reseller-invoices.dto';
import { CreateUserBankAccountDto } from './dto/create-user-bank-account.dto';
import { UserBankAccountRepository } from './repositories/user-bank-account.repository';
@Injectable() @Injectable()
export class ResellerService { export class ResellerService {
@@ -18,6 +20,7 @@ export class ResellerService {
private readonly resellerRepository: ResellerRepository, private readonly resellerRepository: ResellerRepository,
private readonly invoicesService: InvoicesService, private readonly invoicesService: InvoicesService,
private readonly withdrawRequestRepository: WithdrawRequestRepository, private readonly withdrawRequestRepository: WithdrawRequestRepository,
private readonly userBankAccountRepository: UserBankAccountRepository,
) { } ) { }
async create(dto: CreateResellerDto) { async create(dto: CreateResellerDto) {
@@ -148,20 +151,42 @@ export class ResellerService {
async removeWithdrawRequest(userId: string, withdrawRequestId: string) { async removeWithdrawRequest(userId: string, withdrawRequestId: string) {
const request =await this.withdrawRequestRepository.findOne({ const request = await this.withdrawRequestRepository.findOne({
where: { where: {
id: withdrawRequestId, id: withdrawRequestId,
user: { id: userId } user: { id: userId }
} }
}) })
if(!request){ if (!request) {
throw new NotFoundException() throw new NotFoundException()
} }
if(request.paidAt){ if (request.paidAt) {
throw new BadRequestException("درخواست پرداخت شده قابل حذف نیست") throw new BadRequestException("درخواست پرداخت شده قابل حذف نیست")
} }
return this.withdrawRequestRepository.remove(request) return this.withdrawRequestRepository.remove(request)
} }
//********* Bank Account ***********/
async createUserBankAccount(userId: string, dto: CreateUserBankAccountDto) {
const { user } = await this.usersService.findOneById(userId)
const account = this.userBankAccountRepository.create({
user,
...dto
})
return this.userBankAccountRepository.save(account)
}
//********* Remove Bank Account ***********/
async deleteUserBankAccount(userId: string, bankAccountId: string) {
return this.userBankAccountRepository.delete({
user: { id: userId },
id: bankAccountId
})
}
} }
@@ -31,6 +31,7 @@ import { Ticket } from "../../tickets/entities/ticket.entity";
import { Wallet } from "../../wallets/entities/wallet.entity"; import { Wallet } from "../../wallets/entities/wallet.entity";
import { FinancialType } from "../enums/financial-type.enum"; import { FinancialType } from "../enums/financial-type.enum";
import { Reseller } from "../../reseller/entities/reseller.entity"; import { Reseller } from "../../reseller/entities/reseller.entity";
import { UserBankAccount } from "../../reseller/entities/user-bank-account.entity";
@Entity() @Entity()
export class User extends BaseEntity { export class User extends BaseEntity {
@@ -171,4 +172,9 @@ export class User extends BaseEntity {
@OneToOne(() => Reseller, reseller => reseller.owner, { nullable: true }) @OneToOne(() => Reseller, reseller => reseller.owner, { nullable: true })
ownedReseller?: Reseller ownedReseller?: Reseller
//------------------------
@OneToOne(() => UserBankAccount, (account) => account.user, { nullable: true })
bankAckount: UserBankAccount
} }