module creation

This commit is contained in:
2025-12-15 21:44:06 +01:00
parent b834f88bbd
commit 7c7267da72
88 changed files with 12119 additions and 4241 deletions

View File

@@ -0,0 +1,346 @@
import { Injectable } from '@nestjs/common';
import { CreatePaymentInput, CreatePaymentOutput, UpdatePaymentStatusInput, UpdatePaymentStatusOutput, PaymentDto, GetPaymentsQuery, GetPaymentsOutput, PaymentStatus, MembershipFeeDto, MemberPaymentDto, GetMembershipFeesQuery, GetMembershipFeesOutput, UpsertMembershipFeeInput, UpsertMembershipFeeOutput, UpdateMemberPaymentInput, UpdateMemberPaymentOutput, MembershipFeeType, MemberPaymentStatus, PrizeDto, GetPrizesQuery, GetPrizesOutput, CreatePrizeInput, CreatePrizeOutput, AwardPrizeInput, AwardPrizeOutput, DeletePrizeInput, DeletePrizeOutput, PrizeType, WalletDto, TransactionDto, GetWalletQuery, GetWalletOutput, ProcessWalletTransactionInput, ProcessWalletTransactionOutput, TransactionType, ReferenceType } from './dto/PaymentsDto';
import { LeagueSettingsDto, LeagueConfigFormModelStructureDto } from '../league/dto/LeagueDto'; // For the mock data definitions
const payments: Map<string, PaymentDto> = new Map();
const membershipFees: Map<string, MembershipFeeDto> = new Map();
const memberPayments: Map<string, MemberPaymentDto> = new Map();
const prizes: Map<string, PrizeDto> = new Map();
const wallets: Map<string, WalletDto> = new Map();
const transactions: Map<string, TransactionDto> = new Map();
const PLATFORM_FEE_RATE = 0.10;
@Injectable()
export class PaymentsService {
async getPayments(query: GetPaymentsQuery): Promise<GetPaymentsOutput> {
let results = Array.from(payments.values());
if (query.leagueId) {
results = results.filter(p => p.leagueId === query.leagueId);
}
if (query.payerId) {
results = results.filter(p => p.payerId === query.payerId);
}
if (query.type) {
results = results.filter(p => p.type === query.type);
}
return { payments: results };
}
async createPayment(input: CreatePaymentInput): Promise<CreatePaymentOutput> {
const { type, amount, payerId, payerType, leagueId, seasonId } = input;
const platformFee = amount * PLATFORM_FEE_RATE;
const netAmount = amount - platformFee;
const id = `payment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const payment: PaymentDto = {
id,
type,
amount,
platformFee,
netAmount,
payerId,
payerType,
leagueId,
seasonId: seasonId || undefined,
status: PaymentStatus.PENDING,
createdAt: new Date(),
};
payments.set(id, payment);
return { payment };
}
async updatePaymentStatus(input: UpdatePaymentStatusInput): Promise<UpdatePaymentStatusOutput> {
const { paymentId, status } = input;
const payment = payments.get(paymentId);
if (!payment) {
throw new Error('Payment not found');
}
payment.status = status;
if (status === PaymentStatus.COMPLETED) {
payment.completedAt = new Date();
}
payments.set(paymentId, payment);
return { payment };
}
async getMembershipFees(query: GetMembershipFeesQuery): Promise<GetMembershipFeesOutput> {
const { leagueId, driverId } = query;
if (!leagueId) {
throw new Error('leagueId is required');
}
const fee = Array.from(membershipFees.values()).find(f => f.leagueId === leagueId) || null;
let payments: MemberPaymentDto[] = [];
if (driverId) {
payments = Array.from(memberPayments.values()).filter(
p => membershipFees.get(p.feeId)?.leagueId === leagueId && p.driverId === driverId
);
}
return { fee, payments };
}
async upsertMembershipFee(input: UpsertMembershipFeeInput): Promise<UpsertMembershipFeeOutput> {
const { leagueId, seasonId, type, amount } = input;
// Check for existing fee config
let existingFee = Array.from(membershipFees.values()).find(f => f.leagueId === leagueId);
if (existingFee) {
// Update existing fee
existingFee.type = type;
existingFee.amount = amount;
existingFee.seasonId = seasonId || existingFee.seasonId;
existingFee.enabled = amount > 0;
existingFee.updatedAt = new Date();
membershipFees.set(existingFee.id, existingFee);
return { fee: existingFee };
}
const id = `fee-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const fee: MembershipFeeDto = {
id,
leagueId,
seasonId: seasonId || undefined,
type,
amount,
enabled: amount > 0,
createdAt: new Date(),
updatedAt: new Date(),
};
membershipFees.set(id, fee);
return { fee };
}
async updateMemberPayment(input: UpdateMemberPaymentInput): Promise<UpdateMemberPaymentOutput> {
const { feeId, driverId, status, paidAt } = input;
const fee = membershipFees.get(feeId);
if (!fee) {
throw new Error('Membership fee configuration not found');
}
// Find or create payment record
let payment = Array.from(memberPayments.values()).find(
p => p.feeId === feeId && p.driverId === driverId
);
if (!payment) {
const platformFee = fee.amount * PLATFORM_FEE_RATE;
const netAmount = fee.amount - platformFee;
const paymentId = `mp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
payment = {
id: paymentId,
feeId,
driverId,
amount: fee.amount,
platformFee,
netAmount,
status: MemberPaymentStatus.PENDING,
dueDate: new Date(),
};
memberPayments.set(paymentId, payment);
}
if (status) {
payment.status = status;
}
if (paidAt || status === MemberPaymentStatus.PAID) {
payment.paidAt = paidAt ? new Date(paidAt) : new Date();
}
memberPayments.set(payment.id, payment);
return { payment };
}
async getPrizes(query: GetPrizesQuery): Promise<GetPrizesOutput> {
const { leagueId, seasonId } = query;
let results = Array.from(prizes.values()).filter(p => p.leagueId === leagueId);
if (seasonId) {
results = results.filter(p => p.seasonId === seasonId);
}
results.sort((a, b) => a.position - b.position);
return { prizes: results };
}
async createPrize(input: CreatePrizeInput): Promise<CreatePrizeOutput> {
const { leagueId, seasonId, position, name, amount, type, description } = input;
// Check for duplicate position
const existingPrize = Array.from(prizes.values()).find(
p => p.leagueId === leagueId && p.seasonId === seasonId && p.position === position
);
if (existingPrize) {
throw new Error(`Prize for position ${position} already exists`);
}
const id = `prize-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const prize: PrizeDto = {
id,
leagueId,
seasonId,
position,
name,
amount,
type,
description: description || undefined,
awarded: false,
createdAt: new Date(),
};
prizes.set(id, prize);
return { prize };
}
async awardPrize(input: AwardPrizeInput): Promise<AwardPrizeOutput> {
const { prizeId, driverId } = input;
const prize = prizes.get(prizeId);
if (!prize) {
throw new Error('Prize not found');
}
if (prize.awarded) {
throw new Error('Prize has already been awarded');
}
prize.awarded = true;
prize.awardedTo = driverId;
prize.awardedAt = new Date();
prizes.set(prizeId, prize);
return { prize };
}
async deletePrize(input: DeletePrizeInput): Promise<DeletePrizeOutput> {
const { prizeId } = input;
const prize = prizes.get(prizeId);
if (!prize) {
throw new Error('Prize not found');
}
if (prize.awarded) {
throw new Error('Cannot delete an awarded prize');
}
prizes.delete(prizeId);
return { success: true };
}
async getWallet(query: GetWalletQuery): Promise<GetWalletOutput> {
const { leagueId } = query;
if (!leagueId) {
throw new Error('LeagueId is required');
}
let wallet = Array.from(wallets.values()).find(w => w.leagueId === leagueId);
if (!wallet) {
const id = `wallet-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
wallet = {
id,
leagueId,
balance: 0,
totalRevenue: 0,
totalPlatformFees: 0,
totalWithdrawn: 0,
createdAt: new Date(),
currency: 'USD', // Assuming default currency (mock)
};
wallets.set(id, wallet);
}
const walletTransactions = Array.from(transactions.values())
.filter(t => t.walletId === wallet!.id)
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
return { wallet, transactions: walletTransactions };
}
async processWalletTransaction(input: ProcessWalletTransactionInput): Promise<ProcessWalletTransactionOutput> {
const { leagueId, type, amount, description, referenceId, referenceType } = input;
if (!leagueId || !type || amount === undefined || !description) {
throw new Error('Missing required fields: leagueId, type, amount, description');
}
if (type !== TransactionType.DEPOSIT && type !== TransactionType.WITHDRAWAL) {
throw new Error('Type must be "deposit" or "withdrawal"');
}
let wallet = Array.from(wallets.values()).find(w => w.leagueId === leagueId);
if (!wallet) {
const id = `wallet-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
wallet = {
id,
leagueId,
balance: 0,
totalRevenue: 0,
totalPlatformFees: 0,
totalWithdrawn: 0,
createdAt: new Date(),
currency: 'USD', // Assuming default currency (mock)
};
wallets.set(id, wallet);
}
if (type === TransactionType.WITHDRAWAL) {
if (amount > wallet.balance) {
throw new Error('Insufficient balance');
}
}
const transactionId = `txn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const transaction: TransactionDto = {
id: transactionId,
walletId: wallet.id,
type,
amount,
description,
referenceId: referenceId || undefined,
referenceType: referenceType || undefined,
createdAt: new Date(),
};
transactions.set(transactionId, transaction);
if (type === TransactionType.DEPOSIT) {
wallet.balance += amount;
wallet.totalRevenue += amount;
} else {
wallet.balance -= amount;
wallet.totalWithdrawn += amount;
}
wallets.set(wallet.id, wallet);
return { wallet, transaction };
}
}