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; export type UpdateWalletInput = z.infer; export type DepositInput = z.infer; export type AdjustBalanceInput = z.infer; export type TransferInput = z.infer; 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) })); }