first
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { Expose } from "class-transformer";
|
||||
import { IsNotEmpty, IsNumber, IsOptional, IsString, Min } from "class-validator";
|
||||
|
||||
import { ApiProperty } from "../../../common/decorator/swggerDocs";
|
||||
import { WithdrawalStatus } from "../models/abstraction/IWithdrawal";
|
||||
|
||||
export class WithdrawDTO {
|
||||
@Expose()
|
||||
@IsNotEmpty({ message: "مقدار برداشت نمیتواند خالی باشد" })
|
||||
@IsNumber({}, { message: "مقدار برداشت باید یک عدد باشد" })
|
||||
@Min(100000, { message: "حداقل مقدار برداشت باید بیشتر از 100000 باشد" })
|
||||
@ApiProperty({ type: "number", description: "amount of the withdrawal", example: 100000 })
|
||||
amount: number;
|
||||
|
||||
// @Expose()
|
||||
// @IsNotEmpty({ message: "مقدار شماره شبا نمیتواند خالی باشد" })
|
||||
// @IsString()
|
||||
// @ApiProperty({ type: "string", description: "shaba for withdrawal request" })
|
||||
// shaba: string;
|
||||
|
||||
// @Expose()
|
||||
// @IsNotEmpty({ message: "مقدار شماره حساب نمیتواند خالی باشد" })
|
||||
// @IsString()
|
||||
// @ApiProperty({ type: "string", description: "bank account number for withdrawal request" })
|
||||
// bankAccountNumber: string;
|
||||
}
|
||||
|
||||
export class RejectWithdrawalDTO {
|
||||
@Expose()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "reject reason for withdrawal request" })
|
||||
rejectReason: string;
|
||||
}
|
||||
|
||||
export class WithdrawalStatusDTO {
|
||||
@Expose()
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "status for withdrawal request", example: "completed | failed" })
|
||||
status: WithdrawalStatus;
|
||||
|
||||
@Expose()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ type: "string", description: "reject reason for withdrawal request" })
|
||||
rejectReason: string;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
export const TransactionReason = Object.freeze({
|
||||
WITHDRAWAL: "برداشت پول",
|
||||
ORDERـPAY: "پرداخت مبلغ سفارش",
|
||||
BY_ADMIN: "شارژ توسط ادمین",
|
||||
});
|
||||
|
||||
export const CSVHeaders = Object.freeze({
|
||||
fullName: "نام و نام خانوادگی",
|
||||
phoneNumber: "شماره تماس",
|
||||
email: "ایمیل",
|
||||
accountStatus: "وضعیت حساب",
|
||||
isRegisterCompleted: "ثبت نام تکمیل شده",
|
||||
businessType: "نوع فعالیت",
|
||||
isWholesaler: "عمده فروش",
|
||||
createdAt: "تاریخ ثبت",
|
||||
updatedAt: "تاریخ بروزرسانی",
|
||||
amount: "مبلغ",
|
||||
status: "وضعیت برداشت",
|
||||
processedAt: "تاریخ پردازش",
|
||||
IBAN: "شماره شبا",
|
||||
});
|
||||
|
||||
export const Translate = Object.freeze({
|
||||
real: "حقیقی",
|
||||
legal: "حقوقی",
|
||||
Pending: "در انتظار",
|
||||
Approved: "تایید شده",
|
||||
Rejected: "رد شده",
|
||||
Completed: "تکمیل شده",
|
||||
Cancelled: "لغو شده",
|
||||
Failed: "ناموفق",
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export enum TransactionType {
|
||||
CREDIT = "CREDIT",
|
||||
DEBIT = "DEBIT",
|
||||
WITHDRAWAL = "WITHDRAWAL",
|
||||
}
|
||||
|
||||
export interface ITransaction {
|
||||
wallet: Types.ObjectId;
|
||||
order: number;
|
||||
amount: number;
|
||||
transactionType: TransactionType;
|
||||
reason: string;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export interface IWallet {
|
||||
seller: Types.ObjectId;
|
||||
balance: number;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Types } from "mongoose";
|
||||
|
||||
export enum WithdrawalStatus {
|
||||
PENDING = "Pending",
|
||||
COMPLETED = "Completed",
|
||||
FAILED = "Failed",
|
||||
}
|
||||
|
||||
export interface IWithdrawal {
|
||||
wallet: Types.ObjectId;
|
||||
amount: number;
|
||||
IBAN: string;
|
||||
// bankAccountNumber: string;
|
||||
status: WithdrawalStatus;
|
||||
rejectReason: string;
|
||||
processedAt: Date | null;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
import { ITransaction, TransactionType } from "./abstraction/ITransaction";
|
||||
|
||||
const TransactionSchema = new Schema<ITransaction>(
|
||||
{
|
||||
wallet: { type: Schema.Types.ObjectId, ref: "Wallet", required: true },
|
||||
order: { type: Number, ref: "Order", default: null },
|
||||
amount: { type: Number, required: true },
|
||||
transactionType: { type: String, enum: TransactionType, required: true },
|
||||
reason: { type: String, required: false },
|
||||
},
|
||||
{ timestamps: true, toJSON: { versionKey: false, virtuals: true }, id: false },
|
||||
);
|
||||
|
||||
const TransactionModel = model<ITransaction>("Transaction", TransactionSchema);
|
||||
|
||||
export { TransactionModel };
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
import { IWallet } from "./abstraction/IWallet";
|
||||
|
||||
const WalletSchema = new Schema<IWallet>(
|
||||
{
|
||||
seller: { type: Schema.Types.ObjectId, ref: "Seller", required: true, unique: true },
|
||||
balance: { type: Number, default: 0 },
|
||||
},
|
||||
{ timestamps: true, toJSON: { versionKey: false, virtuals: true }, id: false },
|
||||
);
|
||||
|
||||
const WalletModel = model<IWallet>("Wallet", WalletSchema);
|
||||
|
||||
export { WalletModel };
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
import { IWithdrawal, WithdrawalStatus } from "./abstraction/IWithdrawal";
|
||||
|
||||
const WithdrawalSchema = new Schema<IWithdrawal>(
|
||||
{
|
||||
wallet: { type: Schema.Types.ObjectId, ref: "Wallet", required: true },
|
||||
amount: { type: Number, required: true },
|
||||
IBAN: { type: String, required: true },
|
||||
// bankAccountNumber: { type: String, default: null },
|
||||
status: { type: String, enum: WithdrawalStatus, default: WithdrawalStatus.PENDING },
|
||||
rejectReason: { type: String, default: null },
|
||||
processedAt: { type: Date, default: null },
|
||||
},
|
||||
{ timestamps: true, toJSON: { versionKey: false, virtuals: true }, id: false },
|
||||
);
|
||||
|
||||
const WithdrawalModel = model<IWithdrawal>("Withdrawal", WithdrawalSchema);
|
||||
|
||||
export { IWithdrawal, WithdrawalModel };
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BaseRepository } from "../../../common/base/repository";
|
||||
import { ITransaction } from "../models/abstraction/ITransaction";
|
||||
import { TransactionModel } from "../models/transaction.model";
|
||||
|
||||
class TransactionRepo extends BaseRepository<ITransaction> {
|
||||
constructor() {
|
||||
super(TransactionModel);
|
||||
}
|
||||
}
|
||||
|
||||
function createTransactionRepo(): TransactionRepo {
|
||||
return new TransactionRepo();
|
||||
}
|
||||
|
||||
export { TransactionRepo, createTransactionRepo };
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BaseRepository } from "../../../common/base/repository";
|
||||
import { IWallet } from "../models/abstraction/IWallet";
|
||||
import { WalletModel } from "../models/wallet.model";
|
||||
|
||||
class WalletRepo extends BaseRepository<IWallet> {
|
||||
constructor() {
|
||||
super(WalletModel);
|
||||
}
|
||||
}
|
||||
|
||||
function createWalletRepo(): WalletRepo {
|
||||
return new WalletRepo();
|
||||
}
|
||||
|
||||
export { WalletRepo, createWalletRepo };
|
||||
@@ -0,0 +1,19 @@
|
||||
import { BaseRepository } from "../../../common/base/repository";
|
||||
import { WithdrawalStatus } from "../models/abstraction/IWithdrawal";
|
||||
import { IWithdrawal, WithdrawalModel } from "../models/withdrawal.model";
|
||||
|
||||
class WithdrawalRepo extends BaseRepository<IWithdrawal> {
|
||||
constructor() {
|
||||
super(WithdrawalModel);
|
||||
}
|
||||
|
||||
async getWithdrawalRequestsReport() {
|
||||
return this.model.countDocuments({ status: WithdrawalStatus.PENDING });
|
||||
}
|
||||
}
|
||||
|
||||
function createWithdrawalRepo(): WithdrawalRepo {
|
||||
return new WithdrawalRepo();
|
||||
}
|
||||
|
||||
export { WithdrawalRepo, createWithdrawalRepo };
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Request } from "express";
|
||||
import { inject } from "inversify";
|
||||
import { controller, httpGet, httpPost, request, requestBody } from "inversify-express-utils";
|
||||
|
||||
import { WalletService } from "./wallet.service";
|
||||
import { HttpStatus } from "../../common";
|
||||
import { WithdrawDTO } from "./DTO/withdraw.dto";
|
||||
import { BaseController } from "../../common/base/controller";
|
||||
import { ApiAuth, ApiModel, ApiOperation, ApiResponse, ApiTags } from "../../common/decorator/swggerDocs";
|
||||
import { Guard } from "../../core/middlewares/guard.middleware";
|
||||
import { ValidationMiddleware } from "../../core/middlewares/validator.middleware";
|
||||
import { IOCTYPES } from "../../IOC/ioc.types";
|
||||
import { ISeller } from "../seller/models/Abstraction/ISeller";
|
||||
|
||||
@controller("/wallet")
|
||||
@ApiTags("Wallet")
|
||||
class WalletController extends BaseController {
|
||||
@inject(IOCTYPES.WalletService) walletService: WalletService;
|
||||
|
||||
@ApiOperation("get all transaction of seller wallet ==> login as seller")
|
||||
@ApiResponse("successful", HttpStatus.Ok)
|
||||
@ApiAuth()
|
||||
@httpGet("/transaction", Guard.authSeller())
|
||||
public async getTransactions(@request() req: Request) {
|
||||
const seller = req.user as ISeller;
|
||||
const data = await this.walletService.getTransactions(seller._id.toString());
|
||||
return this.response(data);
|
||||
}
|
||||
|
||||
@ApiOperation("Get wallet balance ==> login as seller")
|
||||
@ApiResponse("Successful", HttpStatus.Ok)
|
||||
@ApiAuth()
|
||||
@httpGet("/balance", Guard.authSeller())
|
||||
public async getWalletBalance(@request() req: Request) {
|
||||
const seller = req.user as ISeller;
|
||||
const balance = await this.walletService.getWalletBalance(seller._id.toString());
|
||||
return this.response(balance);
|
||||
}
|
||||
|
||||
@ApiOperation("Get all seller withdrawal requests ==> login as seller")
|
||||
@ApiResponse("Successful", HttpStatus.Ok)
|
||||
@ApiAuth()
|
||||
@httpGet("/withdraw", Guard.authSeller())
|
||||
public async getWithdrawalRequests(@request() req: Request) {
|
||||
const seller = req.user as ISeller;
|
||||
const data = await this.walletService.getWithdrawalRequests(seller._id.toString());
|
||||
return this.response(data);
|
||||
}
|
||||
|
||||
@ApiOperation("withdraw request for seller wallet ==> login as seller")
|
||||
@ApiResponse("Successful", HttpStatus.Created)
|
||||
@ApiResponse("Insufficient balance or withdrawal failed", HttpStatus.BadRequest)
|
||||
@ApiModel(WithdrawDTO)
|
||||
@ApiAuth()
|
||||
@httpPost("/withdraw", Guard.authSeller(), ValidationMiddleware.validateInput(WithdrawDTO))
|
||||
public async withdrawRequest(@request() req: Request, @requestBody() withdrawDto: WithdrawDTO) {
|
||||
const seller = req.user as ISeller;
|
||||
const data = await this.walletService.withdrawRequest(seller, withdrawDto.amount);
|
||||
return this.response(data, HttpStatus.Created);
|
||||
}
|
||||
}
|
||||
|
||||
export { WalletController };
|
||||
@@ -0,0 +1,431 @@
|
||||
import { stringify } from "csv";
|
||||
import { Response } from "express";
|
||||
import { inject, injectable } from "inversify";
|
||||
import { ClientSession, startSession } from "mongoose";
|
||||
|
||||
import { CSVHeaders, TransactionReason } from "./constant";
|
||||
import { TransactionType } from "./models/abstraction/ITransaction";
|
||||
import { WithdrawalStatus } from "./models/abstraction/IWithdrawal";
|
||||
import { TransactionRepo } from "./repository/transaction.repo";
|
||||
import { WalletRepo } from "./repository/wallet.repo";
|
||||
import { WithdrawalRepo } from "./repository/withdrawal.repo";
|
||||
import { SellerMessage, WalletMessage } from "../../common/enums/message.enum";
|
||||
import { BadRequestError, InternalError } from "../../core/app/app.errors";
|
||||
import { Logger } from "../../core/logging/logger";
|
||||
import { IOCTYPES } from "../../IOC/ioc.types";
|
||||
import { TimeService } from "../../utils/time.service";
|
||||
import { translate } from "../../utils/translate.utils";
|
||||
import { NotificationService } from "../notification/notification.service";
|
||||
import { WithdrawalStatusDTO } from "./DTO/withdraw.dto";
|
||||
import { BusinessTypeEnum } from "../seller/enum/seller.enum";
|
||||
import { ISeller } from "../seller/models/Abstraction/ISeller";
|
||||
import { LegalSellerRepo } from "../seller/repository/legalSeller.repository";
|
||||
import { RealSellerRepo } from "../seller/repository/realSeller.repository";
|
||||
|
||||
@injectable()
|
||||
class WalletService {
|
||||
private logger = new Logger(WalletService.name);
|
||||
|
||||
@inject(IOCTYPES.WalletRepo) private walletRepo: WalletRepo;
|
||||
@inject(IOCTYPES.NotificationService) notificationService: NotificationService;
|
||||
@inject(IOCTYPES.TransactionRepo) private transactionRepo: TransactionRepo;
|
||||
@inject(IOCTYPES.WithdrawalRepo) private withdrawalRepo: WithdrawalRepo;
|
||||
@inject(IOCTYPES.RealSellerRepo) private realSellerRepo: RealSellerRepo;
|
||||
@inject(IOCTYPES.LegalSellerRepo) private legalSellerRepo: LegalSellerRepo;
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async createSellerWallet(sellerId: string, session: ClientSession) {
|
||||
const wallet = await this.walletRepo.model.create([{ seller: sellerId }], { session });
|
||||
return { wallet: wallet[0] };
|
||||
}
|
||||
//#######################################
|
||||
//#######################################
|
||||
async creditSellerWallet(sellerId: string, amount: number) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
const wallet = await this.walletRepo.model.findOne({ seller: sellerId });
|
||||
|
||||
if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
|
||||
//create a transaction for credit the wallet balance
|
||||
const transaction = await this.transactionRepo.model.create(
|
||||
[
|
||||
{
|
||||
wallet: wallet._id,
|
||||
amount,
|
||||
transactionType: TransactionType.CREDIT,
|
||||
reason: TransactionReason.BY_ADMIN,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
// update wallet balance
|
||||
wallet.balance += amount;
|
||||
await wallet.save({ session });
|
||||
await this.notificationService.notifyWalletCredit(sellerId, amount, session);
|
||||
|
||||
await session.commitTransaction();
|
||||
|
||||
return { message: WalletMessage.CreditedSuccessfully, newBalance: wallet.balance, transaction };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await session.endSession();
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async creditSellerWalletForOrder(sellerId: string, orderId: number, amount: number, session: ClientSession) {
|
||||
const wallet = await this.walletRepo.model.findOneAndUpdate(
|
||||
{ seller: sellerId },
|
||||
{ $inc: { balance: amount } },
|
||||
{ session, new: true },
|
||||
);
|
||||
|
||||
if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
|
||||
// create a transaction for credit the wallet balance
|
||||
const transaction = await this.transactionRepo.model.create(
|
||||
[
|
||||
{
|
||||
wallet: wallet._id,
|
||||
order: orderId,
|
||||
amount: amount,
|
||||
transactionType: TransactionType.CREDIT,
|
||||
reason: TransactionReason.ORDERـPAY,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
await this.notificationService.notifyWalletCredit(sellerId, amount, session);
|
||||
|
||||
return { wallet, transaction };
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async debitSellerWallet(sellerId: string, orderId: number, debitAmount: number, reason: string) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
const wallet = await this.walletRepo.model.findOne({ seller: sellerId });
|
||||
|
||||
if (!wallet || wallet.balance < debitAmount) throw new BadRequestError(WalletMessage.InsufficientBalance);
|
||||
|
||||
// create a new transaction for the debit
|
||||
const transaction = await this.transactionRepo.model.create(
|
||||
[
|
||||
{
|
||||
wallet: wallet._id,
|
||||
orderId,
|
||||
amount: debitAmount,
|
||||
transactionType: TransactionType.DEBIT,
|
||||
reason,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
// update wallet balance
|
||||
wallet.balance -= debitAmount;
|
||||
|
||||
await wallet.save({ session });
|
||||
await this.notificationService.notifyWalletDebit(sellerId, debitAmount, session);
|
||||
|
||||
await session.commitTransaction();
|
||||
|
||||
return { message: WalletMessage.DebitedSuccessfully, newBalance: wallet.balance, transaction };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await session.endSession();
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async withdrawRequest(seller: ISeller, withdrawalAmount: number) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
let IBAN: string = "";
|
||||
if (seller.businessType === BusinessTypeEnum.REAL) {
|
||||
//
|
||||
const realSeller = await this.realSellerRepo.model.findOne({ seller: seller._id }).lean();
|
||||
if (!realSeller) throw new BadRequestError(SellerMessage.SellerNotFound);
|
||||
IBAN = realSeller.shebaNumber;
|
||||
} else if (seller.businessType === BusinessTypeEnum.LEGAL) {
|
||||
//
|
||||
const legalSeller = await this.legalSellerRepo.model.findOne({ seller: seller._id }).lean();
|
||||
if (!legalSeller) throw new BadRequestError(SellerMessage.SellerNotFound);
|
||||
IBAN = legalSeller.IBan;
|
||||
}
|
||||
|
||||
if (!IBAN) throw new BadRequestError(SellerMessage.InvalidBusinessType);
|
||||
|
||||
const wallet = await this.walletRepo.model.findOne({ seller: seller._id.toString() });
|
||||
|
||||
if (!wallet || wallet.balance < withdrawalAmount) throw new BadRequestError(WalletMessage.InsufficientBalanceForWithdrawal);
|
||||
|
||||
// create a new withdrawal request
|
||||
const withdrawal = await this.withdrawalRepo.model.create(
|
||||
[
|
||||
{
|
||||
wallet: wallet._id,
|
||||
amount: withdrawalAmount,
|
||||
IBAN,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
await this.notificationService.notifyWithdrawalRequest(seller._id.toString(), withdrawalAmount, session);
|
||||
|
||||
await session.commitTransaction();
|
||||
|
||||
return { message: WalletMessage.WithdrawalSubmitted, withdrawal: withdrawal[0] };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await session.endSession();
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async processWithdrawal(withdrawalId: string, withdrawalStatusDto: WithdrawalStatusDTO) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
const withdrawal = await this.withdrawalRepo.model.findOne({ _id: withdrawalId });
|
||||
if (!withdrawal) throw new BadRequestError(WalletMessage.WithdrawalNotFound);
|
||||
|
||||
const wallet = await this.walletRepo.model.findOne({ _id: withdrawal.wallet });
|
||||
if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
|
||||
if (withdrawalStatusDto.status === WithdrawalStatus.COMPLETED) {
|
||||
// check if the wallet has enough balance for the withdrawal
|
||||
if (wallet.balance < withdrawal.amount) throw new BadRequestError(WalletMessage.InsufficientBalanceForWithdrawal);
|
||||
|
||||
// update balance and withdrawal status
|
||||
wallet.balance -= withdrawal.amount;
|
||||
withdrawal.status = WithdrawalStatus.COMPLETED;
|
||||
withdrawal.processedAt = new Date();
|
||||
// save the transaction as a debit
|
||||
await this.transactionRepo.model.create(
|
||||
[
|
||||
{
|
||||
wallet: wallet._id,
|
||||
orderId: null,
|
||||
amount: withdrawal.amount,
|
||||
transactionType: TransactionType.WITHDRAWAL,
|
||||
reason: TransactionReason.WITHDRAWAL,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
await this.notificationService.notifyWithdrawalCompleted(wallet.seller.toString(), withdrawal.amount, session);
|
||||
//
|
||||
} else if (withdrawalStatusDto.status === WithdrawalStatus.FAILED) {
|
||||
// update withdrawal status when its failed
|
||||
withdrawal.status = WithdrawalStatus.FAILED;
|
||||
withdrawal.processedAt = new Date();
|
||||
withdrawal.rejectReason = withdrawalStatusDto.rejectReason;
|
||||
await this.notificationService.notifyWithdrawalFailed(wallet.seller.toString(), withdrawal.amount, session);
|
||||
}
|
||||
|
||||
await withdrawal.save({ session });
|
||||
await wallet.save({ session });
|
||||
await session.commitTransaction();
|
||||
return { message: `Withdrawal ${withdrawalStatusDto.status}`, newBalance: wallet.balance };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await session.endSession();
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async getWalletBalance(sellerId: string) {
|
||||
const wallet = await this.walletRepo.model.findOne({ seller: sellerId });
|
||||
if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
|
||||
return { balance: wallet.balance };
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async getTransactions(sellerId: string) {
|
||||
const wallet = await this.walletRepo.model.findOne({ seller: sellerId });
|
||||
if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
const transaction = await this.transactionRepo.model.find({ wallet: wallet._id });
|
||||
|
||||
return { wallet, transaction };
|
||||
}
|
||||
|
||||
//#######################################
|
||||
//#######################################
|
||||
async getWithdrawalRequests(sellerId: string) {
|
||||
const wallet = await this.walletRepo.model.findOne({ seller: sellerId });
|
||||
if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
|
||||
const withdrawals = await this.withdrawalRepo.model.find({ wallet: wallet._id });
|
||||
|
||||
return { wallet, withdrawals, lastPaidAmount: withdrawals[0]?.amount ?? null, lastPaidDate: withdrawals[0]?.processedAt || null };
|
||||
}
|
||||
|
||||
async getWithdrawalRequestsReport() {
|
||||
return await this.withdrawalRepo.getWithdrawalRequestsReport();
|
||||
}
|
||||
|
||||
//*********** */
|
||||
|
||||
async getWithdrawalRequestsForAdminPanel() {
|
||||
const wallets = await this.walletRepo.model.find().populate("seller");
|
||||
|
||||
const walletData = await Promise.all(
|
||||
wallets.map(async (wallet) => {
|
||||
const withdrawals = await this.withdrawalRepo.model.find({ wallet: wallet._id }).sort({ createdAt: -1 });
|
||||
|
||||
return {
|
||||
wallet,
|
||||
withdrawals,
|
||||
lastPaidAmount: withdrawals[0]?.amount ?? null,
|
||||
lastPaidDate: withdrawals[0]?.processedAt || null,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
walletData,
|
||||
};
|
||||
}
|
||||
//*********** */
|
||||
async getWithdrawalRequestsCsv(res: Response): Promise<void> {
|
||||
const withdrawals = await this.withdrawalRepo.model
|
||||
.find({ status: WithdrawalStatus.PENDING })
|
||||
.populate({ path: "wallet", populate: { path: "seller" } })
|
||||
.sort({ createdAt: -1 });
|
||||
|
||||
const columns = [
|
||||
CSVHeaders.fullName,
|
||||
CSVHeaders.phoneNumber,
|
||||
CSVHeaders.email,
|
||||
CSVHeaders.accountStatus,
|
||||
CSVHeaders.businessType,
|
||||
CSVHeaders.amount,
|
||||
CSVHeaders.status,
|
||||
// CSVHeaders.processedAt,
|
||||
CSVHeaders.createdAt,
|
||||
CSVHeaders.IBAN,
|
||||
];
|
||||
|
||||
// Create a CSV stringifier
|
||||
const stringifier = stringify({
|
||||
header: true,
|
||||
columns: columns,
|
||||
});
|
||||
|
||||
// Write CSV data row by row
|
||||
withdrawals.forEach((withdrawal: any) => {
|
||||
stringifier.write({
|
||||
[CSVHeaders.fullName]: withdrawal.wallet.seller.fullName,
|
||||
[CSVHeaders.phoneNumber]: withdrawal.wallet.seller.phoneNumber,
|
||||
[CSVHeaders.email]: withdrawal.wallet.seller.email,
|
||||
[CSVHeaders.accountStatus]: translate(withdrawal.wallet.seller.accountStatus),
|
||||
[CSVHeaders.businessType]: translate(withdrawal.wallet.seller.businessType),
|
||||
[CSVHeaders.IBAN]: withdrawal.IBAN,
|
||||
[CSVHeaders.amount]: Intl.NumberFormat("en").format(withdrawal.amount * 10),
|
||||
[CSVHeaders.status]: translate(withdrawal.status),
|
||||
// [CSVHeaders.processedAt]: TimeService.convertGregorianToPersian(withdrawal.processedAt),
|
||||
[CSVHeaders.createdAt]: TimeService.convertGregorianToPersian(withdrawal.createdAt),
|
||||
});
|
||||
});
|
||||
|
||||
stringifier
|
||||
.pipe(res)
|
||||
.on("finish", () => {
|
||||
this.logger.info("Withdrawal requests CSV generated successfully");
|
||||
})
|
||||
.on("error", (err) => {
|
||||
this.logger.error("Error while generating withdrawal requests CSV", err);
|
||||
throw new InternalError("something went wrong");
|
||||
});
|
||||
|
||||
stringifier.end();
|
||||
}
|
||||
|
||||
//*********** */
|
||||
|
||||
// async approveWithdrawalRequest(withdrawalRequestId: string) {
|
||||
// const session = await startSession();
|
||||
// session.startTransaction();
|
||||
// try {
|
||||
// const withdrawRequest = await this.withdrawalRepo.model.findOne({ _id: withdrawalRequestId });
|
||||
// if (!withdrawRequest) throw new BadRequestError(WalletMessage.WithdrawalNotFound);
|
||||
|
||||
// const wallet = await this.walletRepo.findById(withdrawRequest.wallet.toString());
|
||||
// if (!wallet) throw new BadRequestError(WalletMessage.WalletNotFound);
|
||||
|
||||
// await this.withdrawalRepo.model.findByIdAndUpdate(withdrawRequest._id, {
|
||||
// status: WithdrawalStatus.COMPLETED,
|
||||
// processedAt: new Date(),
|
||||
// });
|
||||
|
||||
// //TODO:Check this line
|
||||
// wallet.balance -= withdrawRequest.amount;
|
||||
|
||||
// const transaction = await this.transactionRepo.model.create(
|
||||
// [
|
||||
// {
|
||||
// wallet: wallet._id,
|
||||
// amount: withdrawRequest.amount,
|
||||
// transactionType: TransactionType.DEBIT,
|
||||
// reason: TransactionReason.WITHDRAWAL,
|
||||
// },
|
||||
// ],
|
||||
// { session },
|
||||
// );
|
||||
// await wallet.save({ session });
|
||||
// await session.commitTransaction();
|
||||
|
||||
// return {
|
||||
// message: WalletMessage.WithdrawalSubmitted,
|
||||
// transaction: transaction[0],
|
||||
// };
|
||||
// } catch (error) {
|
||||
// await session.abortTransaction();
|
||||
// throw error;
|
||||
// } finally {
|
||||
// await session.endSession();
|
||||
// }
|
||||
// }
|
||||
|
||||
async rejectWithdrawalRequest(withdrawalRequestId: string, rejectReason: string) {
|
||||
const withdrawRequest = await this.withdrawalRepo.model.findOne({ _id: withdrawalRequestId });
|
||||
if (!withdrawRequest) throw new BadRequestError(WalletMessage.WithdrawalNotFound);
|
||||
|
||||
await this.withdrawalRepo.model.findByIdAndUpdate(withdrawRequest._id, {
|
||||
status: WithdrawalStatus.FAILED,
|
||||
processedAt: new Date(),
|
||||
rejectReason,
|
||||
});
|
||||
|
||||
return {
|
||||
message: WalletMessage.RejectWithdrawalRequest,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { WalletService };
|
||||
Reference in New Issue
Block a user