mirror of
https://github.com/acedanger/finance.git
synced 2025-12-05 22:50:12 -08:00
transitioned from astro to react
This commit is contained in:
283
server/data/db.service.ts
Normal file
283
server/data/db.service.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
import type { Account, Transaction } from '../types.js';
|
||||
import { prisma } from './prisma';
|
||||
|
||||
// Define the enums ourselves since Prisma isn't exporting them
|
||||
export enum AccountType {
|
||||
CHECKING = 'CHECKING',
|
||||
SAVINGS = 'SAVINGS',
|
||||
CREDIT_CARD = 'CREDIT_CARD',
|
||||
INVESTMENT = 'INVESTMENT',
|
||||
OTHER = 'OTHER',
|
||||
}
|
||||
|
||||
export enum AccountStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
CLOSED = 'CLOSED',
|
||||
}
|
||||
|
||||
export enum TransactionStatus {
|
||||
PENDING = 'PENDING',
|
||||
CLEARED = 'CLEARED',
|
||||
}
|
||||
|
||||
export enum TransactionType {
|
||||
DEPOSIT = 'DEPOSIT',
|
||||
WITHDRAWAL = 'WITHDRAWAL',
|
||||
TRANSFER = 'TRANSFER',
|
||||
UNSPECIFIED = 'UNSPECIFIED',
|
||||
}
|
||||
|
||||
// Account services
|
||||
export const accountService = {
|
||||
/**
|
||||
* Get all accounts
|
||||
*/
|
||||
async getAll(): Promise<Account[]> {
|
||||
return prisma.account.findMany({
|
||||
orderBy: { name: 'asc' },
|
||||
}) as Promise<Account[]>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get account by ID
|
||||
*/
|
||||
async getById(id: string): Promise<Account | null> {
|
||||
return prisma.account.findUnique({
|
||||
where: { id },
|
||||
}) as Promise<Account | null>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new account
|
||||
*/
|
||||
async create(data: {
|
||||
bankName: string;
|
||||
accountNumber: string;
|
||||
name: string;
|
||||
type?: AccountType;
|
||||
status?: AccountStatus;
|
||||
currency?: string;
|
||||
balance?: number;
|
||||
notes?: string;
|
||||
}): Promise<Account> {
|
||||
return prisma.account.create({
|
||||
data,
|
||||
}) as Promise<Account>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update an account
|
||||
*/
|
||||
async update(
|
||||
id: string,
|
||||
data: {
|
||||
bankName?: string;
|
||||
accountNumber?: string;
|
||||
name?: string;
|
||||
type?: AccountType;
|
||||
status?: AccountStatus;
|
||||
currency?: string;
|
||||
balance?: number;
|
||||
notes?: string;
|
||||
},
|
||||
): Promise<Account | null> {
|
||||
return prisma.account.update({
|
||||
where: { id },
|
||||
data,
|
||||
}) as Promise<Account>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete an account
|
||||
*/
|
||||
async delete(id: string): Promise<Account | null> {
|
||||
return prisma.account.delete({
|
||||
where: { id },
|
||||
}) as Promise<Account>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update account balance
|
||||
*/
|
||||
async updateBalance(id: string, amount: number): Promise<Account | null> {
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return prisma.account.update({
|
||||
where: { id },
|
||||
data: {
|
||||
balance: {
|
||||
increment: amount,
|
||||
},
|
||||
},
|
||||
}) as Promise<Account>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get transactions for an account
|
||||
*/
|
||||
async getTransactions(accountId: string): Promise<Transaction[]> {
|
||||
return prisma.transaction.findMany({
|
||||
where: { accountId },
|
||||
orderBy: { date: 'desc' },
|
||||
}) as Promise<Transaction[]>;
|
||||
},
|
||||
};
|
||||
|
||||
// Transaction services
|
||||
export const transactionService = {
|
||||
/**
|
||||
* Get all transactions
|
||||
*/
|
||||
async getAll(): Promise<Transaction[]> {
|
||||
return prisma.transaction.findMany({
|
||||
orderBy: { date: 'desc' },
|
||||
}) as Promise<Transaction[]>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get transactions by account ID
|
||||
*/
|
||||
async getByAccountId(accountId: string): Promise<Transaction[]> {
|
||||
return prisma.transaction.findMany({
|
||||
where: { accountId },
|
||||
orderBy: { date: 'desc' },
|
||||
}) as Promise<Transaction[]>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get transaction by ID
|
||||
*/
|
||||
async getById(id: string): Promise<Transaction | null> {
|
||||
return prisma.transaction.findUnique({
|
||||
where: { id },
|
||||
}) as Promise<Transaction | null>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new transaction and update account balance
|
||||
*/
|
||||
async create(data: {
|
||||
accountId: string;
|
||||
date: Date;
|
||||
description: string;
|
||||
amount: number;
|
||||
category?: string;
|
||||
status?: TransactionStatus;
|
||||
type?: TransactionType;
|
||||
notes?: string;
|
||||
tags?: string;
|
||||
}): Promise<Transaction> {
|
||||
// Use a transaction to ensure data consistency
|
||||
return prisma.$transaction<Transaction>(async (tx) => {
|
||||
// Create the transaction
|
||||
const transaction = await tx.transaction.create({
|
||||
data,
|
||||
});
|
||||
|
||||
// Update the account balance
|
||||
await tx.account.update({
|
||||
where: { id: data.accountId },
|
||||
data: {
|
||||
balance: {
|
||||
increment: data.amount,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return transaction as Transaction;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a transaction and adjust account balance
|
||||
*/
|
||||
async update(
|
||||
id: string,
|
||||
data: {
|
||||
accountId?: string;
|
||||
date?: Date;
|
||||
description?: string;
|
||||
amount?: number;
|
||||
category?: string;
|
||||
status?: TransactionStatus;
|
||||
type?: TransactionType;
|
||||
notes?: string;
|
||||
tags?: string;
|
||||
},
|
||||
): Promise<Transaction | null> {
|
||||
// If amount is changing, we need to adjust the account balance
|
||||
if (typeof data.amount !== 'undefined') {
|
||||
return prisma.$transaction<Transaction | null>(async (tx) => {
|
||||
// Get the current transaction to calculate difference
|
||||
const currentTxn = await tx.transaction.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!currentTxn) return null;
|
||||
|
||||
// Amount is guaranteed to be defined at this point since we checked above
|
||||
const amount = data.amount as number; // Use type assertion instead of non-null assertion
|
||||
const amountDifference = amount - Number(currentTxn.amount);
|
||||
|
||||
// Update transaction
|
||||
const updatedTxn = await tx.transaction.update({
|
||||
where: { id },
|
||||
data,
|
||||
});
|
||||
|
||||
// Update account balance
|
||||
await tx.account.update({
|
||||
where: { id: data.accountId || currentTxn.accountId },
|
||||
data: {
|
||||
balance: {
|
||||
increment: amountDifference,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return updatedTxn as Transaction;
|
||||
});
|
||||
}
|
||||
|
||||
// If amount isn't changing, just update the transaction
|
||||
return prisma.transaction.update({
|
||||
where: { id },
|
||||
data,
|
||||
}) as Promise<Transaction>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a transaction and adjust account balance
|
||||
*/
|
||||
async delete(id: string): Promise<Transaction | null> {
|
||||
return prisma.$transaction<Transaction | null>(async (tx) => {
|
||||
// Get transaction before deleting
|
||||
const transaction = await tx.transaction.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!transaction) return null;
|
||||
|
||||
// Delete the transaction
|
||||
const deletedTxn = await tx.transaction.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
// Adjust the account balance (reverse the transaction amount)
|
||||
await tx.account.update({
|
||||
where: { id: transaction.accountId },
|
||||
data: {
|
||||
balance: {
|
||||
decrement: Number(transaction.amount),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return deletedTxn as Transaction;
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user