mirror of
https://github.com/acedanger/finance.git
synced 2025-12-05 14:40:13 -08:00
190 lines
6.1 KiB
TypeScript
190 lines
6.1 KiB
TypeScript
import { Router } from 'express';
|
|
import { accountService, transactionService } from '../data/db.service.js';
|
|
import type { Transaction, TransactionStatus, TransactionType } from '../types.js';
|
|
|
|
const router = Router();
|
|
|
|
// Helper function to transform update data
|
|
function transformUpdateData(updateData: Partial<Omit<Transaction, 'id'>>) {
|
|
const typedUpdateData: {
|
|
accountId?: string;
|
|
date?: Date;
|
|
description?: string;
|
|
amount?: number;
|
|
category?: string;
|
|
status?: TransactionStatus;
|
|
type?: TransactionType;
|
|
notes?: string;
|
|
tags?: string;
|
|
} = {};
|
|
|
|
// Convert string date to Date object if provided
|
|
if (updateData.date) {
|
|
typedUpdateData.date =
|
|
typeof updateData.date === 'string' ? new Date(updateData.date) : updateData.date;
|
|
}
|
|
|
|
// Convert amount to number if provided
|
|
if (updateData.amount !== undefined) {
|
|
typedUpdateData.amount = Number(updateData.amount);
|
|
}
|
|
|
|
// Copy other fields
|
|
if (updateData.accountId) typedUpdateData.accountId = updateData.accountId;
|
|
if (updateData.description) typedUpdateData.description = updateData.description;
|
|
if (updateData.category !== undefined)
|
|
typedUpdateData.category = updateData.category || undefined;
|
|
if (updateData.status) typedUpdateData.status = updateData.status as TransactionStatus;
|
|
if (updateData.type) typedUpdateData.type = updateData.type as TransactionType;
|
|
if (updateData.notes !== undefined) typedUpdateData.notes = updateData.notes || undefined;
|
|
if (updateData.tags !== undefined) typedUpdateData.tags = updateData.tags || undefined;
|
|
|
|
return typedUpdateData;
|
|
}
|
|
|
|
// POST /api/transactions - Create new transaction
|
|
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
|
|
router.post('/', async (req: any, res: any) => {
|
|
try {
|
|
const transaction = req.body as Omit<Transaction, 'id'>;
|
|
|
|
// Validate required fields
|
|
if (
|
|
!transaction.accountId ||
|
|
!transaction.date ||
|
|
!transaction.description ||
|
|
transaction.amount === undefined
|
|
) {
|
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
}
|
|
|
|
// Validate account exists
|
|
const account = await accountService.getById(transaction.accountId);
|
|
if (!account) {
|
|
return res.status(404).json({ error: 'Account not found' });
|
|
}
|
|
|
|
// Convert string date to Date object if needed
|
|
const transactionDate =
|
|
typeof transaction.date === 'string' ? new Date(transaction.date) : transaction.date;
|
|
|
|
// Create new transaction with database service
|
|
const newTransaction = await transactionService.create({
|
|
accountId: transaction.accountId,
|
|
date: transactionDate,
|
|
description: transaction.description,
|
|
amount: Number(transaction.amount),
|
|
category: transaction.category || undefined,
|
|
status: transaction.status as TransactionStatus,
|
|
type: transaction.type as TransactionType,
|
|
notes: transaction.notes || undefined,
|
|
tags: transaction.tags || undefined,
|
|
});
|
|
|
|
// Convert Decimal to number for response
|
|
const response = {
|
|
...newTransaction,
|
|
amount: Number(newTransaction.amount),
|
|
};
|
|
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
console.error('Error creating transaction:', error);
|
|
res.status(500).json({ error: 'Failed to create transaction' });
|
|
}
|
|
});
|
|
|
|
// GET /api/transactions/:id - Get single transaction
|
|
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
|
|
router.get('/:id', async (req: any, res: any) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
if (!id) {
|
|
return res.status(400).json({ error: 'Transaction ID is required' });
|
|
}
|
|
|
|
const transaction = await transactionService.getById(id);
|
|
|
|
if (!transaction) {
|
|
return res.status(404).json({ error: 'Transaction not found' });
|
|
}
|
|
|
|
// Convert Decimal to number for response
|
|
const response = {
|
|
...transaction,
|
|
amount: Number(transaction.amount),
|
|
};
|
|
|
|
res.json(response);
|
|
} catch (error) {
|
|
console.error('Error fetching transaction:', error);
|
|
res.status(500).json({ error: 'Failed to fetch transaction' });
|
|
}
|
|
});
|
|
|
|
// PUT /api/transactions/:id - Update transaction
|
|
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
|
|
router.put('/:id', async (req: any, res: any) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
if (!id) {
|
|
return res.status(400).json({ error: 'Transaction ID is required' });
|
|
}
|
|
|
|
// Check if transaction exists
|
|
const existingTransaction = await transactionService.getById(id);
|
|
if (!existingTransaction) {
|
|
return res.status(404).json({ error: 'Transaction not found' });
|
|
}
|
|
|
|
const updateData = req.body as Partial<Omit<Transaction, 'id'>>;
|
|
const typedUpdateData = transformUpdateData(updateData);
|
|
|
|
const updatedTransaction = await transactionService.update(id, typedUpdateData);
|
|
|
|
if (!updatedTransaction) {
|
|
return res.status(404).json({ error: 'Transaction not found or could not be updated' });
|
|
}
|
|
|
|
// Convert Decimal to number for response
|
|
const response = {
|
|
...updatedTransaction,
|
|
amount: Number(updatedTransaction.amount),
|
|
};
|
|
|
|
res.json(response);
|
|
} catch (error) {
|
|
console.error('Error updating transaction:', error);
|
|
res.status(500).json({ error: 'Failed to update transaction' });
|
|
}
|
|
});
|
|
|
|
// DELETE /api/transactions/:id - Delete transaction
|
|
// biome-ignore lint/suspicious/noExplicitAny: Express handler types require any for req/res
|
|
router.delete('/:id', async (req: any, res: any) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
if (!id) {
|
|
return res.status(400).json({ error: 'Transaction ID is required' });
|
|
}
|
|
|
|
// Check if transaction exists
|
|
const existingTransaction = await transactionService.getById(id);
|
|
if (!existingTransaction) {
|
|
return res.status(404).json({ error: 'Transaction not found' });
|
|
}
|
|
|
|
await transactionService.delete(id);
|
|
|
|
res.status(204).send();
|
|
} catch (error) {
|
|
console.error('Error deleting transaction:', error);
|
|
res.status(500).json({ error: 'Failed to delete transaction' });
|
|
}
|
|
});
|
|
|
|
export default router;
|