- Added createWallet, updateWallet, deleteWallet, depositWallet, transferWallet, and listMovements handlers. - Created corresponding routes for wallet operations. - Developed frontend components for wallet management including dialogs for creating, editing, depositing, adjusting balance, transferring, and viewing movements. - Integrated wallet management into the authenticated routes.
165 lines
4.9 KiB
TypeScript
165 lines
4.9 KiB
TypeScript
import { z } from "zod";
|
|
import { Prisma } from "../../generated/prisma/client";
|
|
import { prisma } from "../../lib/prisma";
|
|
|
|
export const createWalletSchema = z.object({
|
|
name: z.string().min(1).max(100),
|
|
currency: z.string().min(1).max(10),
|
|
initialBalance: z.number().optional().default(0),
|
|
});
|
|
|
|
export const updateWalletSchema = z.object({
|
|
name: z.string().min(1).max(100),
|
|
currency: z.string().min(1).max(10),
|
|
});
|
|
|
|
export const depositSchema = z.object({
|
|
amount: z.number().positive(),
|
|
description: z.string().max(255).optional(),
|
|
});
|
|
|
|
export const adjustBalanceSchema = z.object({
|
|
newBalance: z.number().min(0),
|
|
description: z.string().max(255).optional(),
|
|
});
|
|
|
|
export const transferSchema = z.object({
|
|
fromWalletId: z.number().int().positive(),
|
|
toWalletId: z.number().int().positive(),
|
|
outgoingAmount: z.number().positive(),
|
|
incomingAmount: z.number().positive(),
|
|
description: z.string().max(255).optional(),
|
|
});
|
|
|
|
export type CreateWalletInput = z.infer<typeof createWalletSchema>;
|
|
export type UpdateWalletInput = z.infer<typeof updateWalletSchema>;
|
|
export type DepositInput = z.infer<typeof depositSchema>;
|
|
export type AdjustBalanceInput = z.infer<typeof adjustBalanceSchema>;
|
|
export type TransferInput = z.infer<typeof transferSchema>;
|
|
|
|
export async function listWallets() {
|
|
const wallets = await prisma.wallet.findMany({ orderBy: { createdAt: "desc" } });
|
|
return wallets.map((w) => ({ ...w, balance: Number(w.balance) }));
|
|
}
|
|
|
|
export async function createWallet(input: CreateWalletInput) {
|
|
const wallet = await prisma.wallet.create({
|
|
data: {
|
|
name: input.name,
|
|
currency: input.currency,
|
|
balance: input.initialBalance ?? 0,
|
|
},
|
|
});
|
|
return { ...wallet, balance: Number(wallet.balance) };
|
|
}
|
|
|
|
export async function updateWallet(id: number, input: UpdateWalletInput) {
|
|
const wallet = await prisma.wallet.update({
|
|
where: { id },
|
|
data: { name: input.name, currency: input.currency },
|
|
});
|
|
return { ...wallet, balance: Number(wallet.balance) };
|
|
}
|
|
|
|
export async function deleteWallet(id: number) {
|
|
const wallet = await prisma.wallet.findUniqueOrThrow({ where: { id } });
|
|
if (Number(wallet.balance) !== 0) {
|
|
throw new Error("No se puede eliminar una billetera con saldo distinto de cero");
|
|
}
|
|
await prisma.walletMovement.deleteMany({ where: { walletId: id } });
|
|
await prisma.wallet.delete({ where: { id } });
|
|
}
|
|
|
|
export async function depositWallet(id: number, input: DepositInput) {
|
|
const [wallet] = await prisma.$transaction([
|
|
prisma.wallet.update({
|
|
where: { id },
|
|
data: { balance: { increment: input.amount } },
|
|
}),
|
|
prisma.walletMovement.create({
|
|
data: {
|
|
walletId: id,
|
|
type: "DEPOSIT",
|
|
amount: input.amount,
|
|
description: input.description ?? null,
|
|
},
|
|
}),
|
|
]);
|
|
return { ...wallet, balance: Number(wallet.balance) };
|
|
}
|
|
|
|
export async function adjustBalance(id: number, input: AdjustBalanceInput) {
|
|
const wallet = await prisma.wallet.findUniqueOrThrow({ where: { id } });
|
|
const currentBalance = Number(wallet.balance);
|
|
const difference = input.newBalance - currentBalance;
|
|
|
|
const [updated] = await prisma.$transaction([
|
|
prisma.wallet.update({
|
|
where: { id },
|
|
data: { balance: input.newBalance },
|
|
}),
|
|
prisma.walletMovement.create({
|
|
data: {
|
|
walletId: id,
|
|
type: "ADJUSTMENT",
|
|
amount: difference,
|
|
description: input.description ?? null,
|
|
},
|
|
}),
|
|
]);
|
|
return { ...updated, balance: Number(updated.balance) };
|
|
}
|
|
|
|
export async function transferWallet(input: TransferInput) {
|
|
const groupId = crypto.randomUUID();
|
|
|
|
const [fromWallet, toWallet] = await prisma.$transaction(async (tx) => {
|
|
const from = await tx.wallet.update({
|
|
where: { id: input.fromWalletId },
|
|
data: { balance: { decrement: input.outgoingAmount } },
|
|
});
|
|
|
|
const to = await tx.wallet.update({
|
|
where: { id: input.toWalletId },
|
|
data: { balance: { increment: input.incomingAmount } },
|
|
});
|
|
|
|
await tx.walletMovement.create({
|
|
data: {
|
|
walletId: input.fromWalletId,
|
|
type: "TRANSFER_OUT",
|
|
amount: -input.outgoingAmount,
|
|
description: input.description ?? null,
|
|
referenceWalletId: input.toWalletId,
|
|
transferGroupId: groupId,
|
|
},
|
|
});
|
|
|
|
await tx.walletMovement.create({
|
|
data: {
|
|
walletId: input.toWalletId,
|
|
type: "TRANSFER_IN",
|
|
amount: input.incomingAmount,
|
|
description: input.description ?? null,
|
|
referenceWalletId: input.fromWalletId,
|
|
transferGroupId: groupId,
|
|
},
|
|
});
|
|
|
|
return [from, to];
|
|
});
|
|
|
|
return {
|
|
from: { ...fromWallet, balance: Number(fromWallet.balance) },
|
|
to: { ...toWallet, balance: Number(toWallet.balance) },
|
|
};
|
|
}
|
|
|
|
export async function listMovements(walletId: number) {
|
|
const movements = await prisma.walletMovement.findMany({
|
|
where: { walletId },
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
return movements.map((m) => ({ ...m, amount: Number(m.amount) }));
|
|
}
|