feat(bank-accounts): add transaction retrieval and update service logic

- Implemented a new endpoint to fetch transactions for a specific bank account.
- Refactored the bank account service to streamline transaction handling and balance calculations.
- Removed the bank account balance table and adjusted related logic in workflows and services.
- Enhanced transaction mapping to include references to sales and purchase records.
This commit is contained in:
2026-01-05 18:35:08 +03:30
parent fda190f902
commit b05048e62f
14 changed files with 147 additions and 1525 deletions
@@ -31,4 +31,9 @@ export class BankAccountsController {
remove(@Param('id') id: string) {
return this.bankAccountsService.remove(Number(id))
}
@Get(':id/transactions')
getTransactions(@Param('id') id: string) {
return this.bankAccountsService.getTransactions(Number(id))
}
}
@@ -1,49 +1,61 @@
import { Injectable } from '@nestjs/common'
import { withTransaction } from '../../common/database/transaction.helper'
import { ResponseMapper } from '../../common/response/response-mapper'
import { BankTransactionRefType } from '../../generated/prisma/enums'
import { PrismaService } from '../../prisma/prisma.service'
@Injectable()
export class BankAccountsService {
constructor(private prisma: PrismaService) {}
async create(data: any) {
return withTransaction(this.prisma, async tx => {
const item = await tx.bankAccount.create({ data })
await tx.bankAccountBalance.create({
data: {
bankAccountId: item.id,
balance: 0,
},
})
return ResponseMapper.create(item)
})
}
async findAll() {
const items = await this.prisma.bankAccount.findMany({
include: {
branch: {
select: {
id: true,
name: true,
code: true,
bank: {
select: { id: true, name: true, shortName: true },
},
private readonly bankAccountQuery = {
include: {
branch: {
select: {
id: true,
name: true,
code: true,
bank: {
select: { id: true, name: true, shortName: true },
},
},
},
omit: {
branchId: true,
bankAccountTransactions: {
take: 1,
select: { id: true, createdAt: true, balanceAfter: true },
orderBy: { createdAt: 'desc' as const },
},
})
return ResponseMapper.list(items)
},
omit: {
branchId: true,
},
}
async create(data: any) {
const item = await this.prisma.bankAccount.create({ data })
return ResponseMapper.create(item)
}
async findAll() {
const items = await this.prisma.bankAccount.findMany(this.bankAccountQuery)
const mappedData = items.map(({ bankAccountTransactions, ...rest }) => ({
...rest,
currentBalance: Number(bankAccountTransactions[0]?.balanceAfter || 0),
}))
return ResponseMapper.list(mappedData)
}
async findOne(id: number) {
const item = await this.prisma.bankAccount.findUnique({ where: { id } })
if (!item) return null
return ResponseMapper.single(item)
const item = await this.prisma.bankAccount.findUniqueOrThrow({
...this.bankAccountQuery,
where: { id },
})
const { bankAccountTransactions, ...rest } = item
return ResponseMapper.single({
...rest,
currentBalance: Number(bankAccountTransactions[0]?.balanceAfter || 0),
})
}
async update(id: number, data: any) {
@@ -55,4 +67,87 @@ export class BankAccountsService {
const item = await this.prisma.bankAccount.delete({ where: { id } })
return ResponseMapper.single(item)
}
async getTransactions(bankAccountId: number, page = 1, pageSize = 50) {
const query = {
where: { bankAccountId },
orderBy: { createdAt: 'desc' as const },
skip: (page - 1) * pageSize,
take: pageSize,
}
const [items, count] = await this.prisma.$transaction([
this.prisma.bankAccountTransaction.findMany({
...query,
omit: { bankAccountId: true },
}),
this.prisma.bankAccountTransaction.count({ where: { bankAccountId } }),
])
const PosReferenceIds = [] as number[]
const purchaseReferenceIds = [] as number[]
items.forEach(({ referenceType, referenceId }) => {
if (referenceId && referenceType) {
if (
referenceType === BankTransactionRefType.POS_SALE ||
referenceType === BankTransactionRefType.POS_REFUND
) {
PosReferenceIds.push(referenceId)
} else if (
referenceType === BankTransactionRefType.PURCHASE_PAYMENT ||
referenceType === BankTransactionRefType.PURCHASE_REFUND
) {
purchaseReferenceIds.push(referenceId)
}
}
})
const posRecords = await this.prisma.salesInvoice.findMany({
where: { id: { in: PosReferenceIds } },
select: {
id: true,
code: true,
totalAmount: true,
posAccount: {
select: { id: true, name: true },
},
customer: {
select: { id: true, firstName: true, lastName: true },
},
},
})
const purchaseRecords = await this.prisma.purchaseReceipt.findMany({
where: { id: { in: purchaseReferenceIds } },
select: {
id: true,
code: true,
totalAmount: true,
supplier: {
select: { id: true, firstName: true, lastName: true },
},
},
})
const mappedItems = items.map(tx => {
let reference = null as any
if (tx.referenceId && tx.referenceType) {
if (
tx.referenceType === BankTransactionRefType.POS_SALE ||
tx.referenceType === BankTransactionRefType.POS_REFUND
) {
reference = posRecords.find(r => r.id === tx.referenceId) || null
} else if (
tx.referenceType === BankTransactionRefType.PURCHASE_PAYMENT ||
tx.referenceType === BankTransactionRefType.PURCHASE_REFUND
) {
reference = purchaseRecords.find(r => r.id === tx.referenceId) || null
}
}
return { ...tx, reference }
})
return ResponseMapper.paginate(mappedItems, count, page, pageSize)
}
}
@@ -8,13 +8,13 @@ export class BankAccountsWorkflow {
tx: Prisma.TransactionClient,
payload: AddTransactionToBankAccountDto,
) {
const item = await tx.bankAccountBalance.findUnique({
const item = await tx.bankAccountTransaction.findFirst({
where: {
bankAccountId: payload.bankAccountId,
},
})
const balance = item ? Number(item.balance) : 0
const balance = item ? Number(item?.balanceAfter) : 0
const newBalance = item
? payload.type === 'DEPOSIT'
@@ -35,31 +35,5 @@ export class BankAccountsWorkflow {
referenceType: payload.referenceType,
},
})
console.log('first')
const bankAccountBalanceItem = await tx.bankAccountBalance.findUnique({
where: {
bankAccountId: payload.bankAccountId,
},
})
if (!bankAccountBalanceItem) {
return await tx.bankAccountBalance.create({
data: {
bankAccount: { connect: { id: payload.bankAccountId } },
balance: newBalance,
},
})
}
return await tx.bankAccountBalance.update({
where: {
bankAccountId: payload.bankAccountId,
},
data: {
balance: newBalance,
},
})
}
}
@@ -89,7 +89,6 @@ export class InventoriesService {
},
})
let info = null as any
console.log(movements)
const mapped = movements.map(movement => {
const { id, quantity, unitPrice, totalCost, avgCost, product } = movement