Skip to content

Commit

Permalink
feat(api): add check transaction category for amount (#121)
Browse files Browse the repository at this point in the history
Fixes bug transaction always be negative
  • Loading branch information
duongdev committed Jul 15, 2024
1 parent cc9a26d commit 56f0f6e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 8 deletions.
81 changes: 75 additions & 6 deletions apps/api/v1/routes/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { zCreateTransaction, zUpdateTransaction } from '@6pm/validation'
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { z } from 'zod'
import { getLogger } from '../../lib/log'
import { getAuthUserStrict } from '../middlewares/auth'
import { generateTransactionDataFromFile } from '../services/ai.service'
import { canUserReadBudget, findBudget } from '../services/budget.service'
import { canUserReadCategory, findCategory } from '../services/category.service'
import {
canUserCreateTransaction,
canUserDeleteTransaction,
Expand All @@ -16,7 +18,7 @@ import {
listTransactions,
updateTransaction,
} from '../services/transaction.service'
import { findUserWallet } from '../services/wallet.service'
import { canUserReadWallet, findUserWallet } from '../services/wallet.service'

const router = new Hono()

Expand Down Expand Up @@ -84,29 +86,72 @@ const router = new Hono()
)

.post('/', zValidator('json', zCreateTransaction), async (c) => {
const logger = getLogger(`${c.req.method} ${c.req.path}`)
const user = getAuthUserStrict(c)
const data = c.req.valid('json')
const { budgetId, walletAccountId: walletId } = data
const { budgetId, walletAccountId: walletId, categoryId } = data

logger.debug('Creating transaction %o', data)

const budget = budgetId ? await findBudget({ budgetId }) : null
if (budgetId && (!budget || !(await canUserReadBudget({ user, budget })))) {
logger.error(`Budget not found or user doesn't have read access %o`, {
budgetId,
budget,
})
return c.json({ message: 'budget not found' }, 404)
}
logger.debug('Budget found %o', { budgetId, budget })

const wallet = await findUserWallet({ user, walletId })
if (!wallet) {
if (!wallet || !(await canUserReadWallet({ user, walletId }))) {
logger.error(`Wallet not found or user doesn't have read access %o`, {
walletId,
wallet,
})
return c.json({ message: 'wallet not found' }, 404)
}
logger.debug('Wallet found %o', { walletId, wallet })

const category =
(categoryId && (await findCategory({ id: categoryId }))) || null
if (
categoryId &&
(!category || !(await canUserReadCategory({ user, category })))
) {
logger.error(`Category not found or user doesn't have read access %o`, {
categoryId,
category,
})
return c.json({ message: 'category not found' }, 404)
}
logger.debug('Category found %o', { categoryId, category })

if (
!(await canUserCreateTransaction({ user, budget, walletAccount: wallet }))
) {
logger.error(`User doesn't have permission to create transaction %o`, {
user,
budget,
wallet,
})
return c.json({ message: 'user cannot create transaction' }, 403)
}

const createTransactionData = {
...data,
amount:
(category &&
(category?.type === 'INCOME'
? Math.abs(data.amount)
: -Math.abs(data.amount))) ||
data.amount,
}
logger.debug('Creating transaction with data %o', createTransactionData)

const transaction = await createTransaction({
user,
data,
data: createTransactionData,
})

return c.json(transaction, 201)
Expand Down Expand Up @@ -136,7 +181,10 @@ const router = new Hono()
}

const wallet = walletId ? await findUserWallet({ user, walletId }) : null
if (walletId && !wallet) {
if (
walletId &&
(!wallet || !(await canUserReadWallet({ user, walletId })))
) {
return c.json({ message: 'wallet not found' }, 404)
}

Expand All @@ -158,9 +206,30 @@ const router = new Hono()
return c.json({ message: 'budget not found' }, 404)
}

const categoryId = data.categoryId || transaction.categoryId

const category =
(categoryId && (await findCategory({ id: categoryId }))) || null
if (
categoryId &&
(!category || !(await canUserReadCategory({ user, category })))
) {
return c.json({ message: 'category not found' }, 404)
}

const transactionAmount =
data.amount && category
? category?.type === 'INCOME'
? Math.abs(data.amount)
: -Math.abs(data.amount)
: transaction.amount

const updatedTransaction = await updateTransaction({
transactionId,
data,
data: {
...data,
amount: transactionAmount as number,
},
})

return c.json(updatedTransaction)
Expand Down
7 changes: 5 additions & 2 deletions apps/api/v1/services/category.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,12 @@ export async function deleteCategory({ categoryId }: { categoryId: string }) {
})
}

export async function findCategory({ id }: { id: string }) {
export async function findCategory({
id,
userId,
}: { id: string; userId?: string }) {
return prisma.category.findUnique({
where: { id },
where: { id, userId },
})
}

Expand Down
10 changes: 10 additions & 0 deletions apps/api/v1/services/wallet.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ export async function canUserDeleteWallet({
return await canUserUpdateWallet({ user, walletId })
}

export async function canUserReadWallet({
user,
walletId,
}: {
user: User
walletId: string
}) {
return await canUserUpdateWallet({ user, walletId })
}

export async function walletWithBalance(wallet: UserWalletAccount) {
const balance = await getWalletBalance({ wallet })
return { ...wallet, balance }
Expand Down

0 comments on commit 56f0f6e

Please sign in to comment.