diff --git a/src/modules/banking/bank-cards/application/bank-operations.service.ts b/src/modules/banking/bank-cards/application/bank-operations.service.ts index 870748b..e562780 100644 --- a/src/modules/banking/bank-cards/application/bank-operations.service.ts +++ b/src/modules/banking/bank-cards/application/bank-operations.service.ts @@ -91,6 +91,101 @@ export class BankOperationsService { }; } + private async pageViews(tx: Tx, rows: BankCardOperation[]) { + const userId = rows[0]?.userId; + if (!userId) return []; + const cardIds = new Set( + rows.flatMap((row) => + [row.cardId, row.targetCardId].filter( + (id): id is string => id !== null, + ), + ), + ); + const sourceIds = new Set( + rows.flatMap((row) => (row.originalExpenseId ? [row.originalExpenseId] : [])), + ); + const [cards, sources] = await Promise.all([ + tx.bankCard.findMany({ where: { userId, id: { in: [...cardIds] } } }), + tx.bankCardOperation.findMany({ + where: { userId, id: { in: [...sourceIds] } }, + }), + ]); + const categoryIds = new Set( + rows + .map( + (row) => + sources.find((source) => source.id === row.originalExpenseId) + ?.categoryId ?? row.categoryId, + ) + .filter((id): id is string => id !== null), + ); + const expenseIds = new Set( + rows.flatMap((row) => { + const source = sources.find((item) => item.id === row.originalExpenseId); + return row.kind === BankOperationKind.Expense || source + ? [source?.id ?? row.id] + : []; + }), + ); + const [categories, refunds] = await Promise.all([ + tx.expenseCategory.findMany({ + where: { userId, id: { in: [...categoryIds] } }, + }), + tx.bankCardOperation.findMany({ + where: { + userId, + originalExpenseId: { in: [...expenseIds] }, + deletedAt: null, + }, + }), + ]); + const cardsById = new Map(cards.map((card) => [card.id, card])); + const sourcesById = new Map(sources.map((source) => [source.id, source])); + const categoriesById = new Map(categories.map((category) => [category.id, category])); + return rows.map((row) => { + const card = cardsById.get(row.cardId) as { name: string }; + const target = row.targetCardId ? cardsById.get(row.targetCardId) : null; + const source = row.originalExpenseId + ? sourcesById.get(row.originalExpenseId) + : null; + const categoryId = source?.categoryId ?? row.categoryId; + const relatedRefunds = + row.kind === BankOperationKind.Expense || source + ? refunds.filter( + (refund) => + refund.originalExpenseId === (source?.id ?? row.id) && + (!source || refund.id !== row.id), + ) + : []; + return { + id: row.id, + kind: row.kind as BankOperationDto['kind'], + cardId: row.cardId, + cardName: card.name, + targetCardId: row.targetCardId, + targetCardName: target?.name ?? null, + categoryId, + categoryName: categoriesById.get(categoryId ?? '')?.name ?? null, + originalExpenseId: row.originalExpenseId, + amount: rubMoney(row.amountKopecks), + availableRefund: + row.kind === BankOperationKind.Expense || source + ? rubMoney( + (source?.amountKopecks ?? row.amountKopecks) - + relatedRefunds.reduce( + (sum, refund) => sum + refund.amountKopecks, + ZERO_KOPECKS, + ), + ) + : null, + date: row.date, + description: row.description, + version: row.version, + createdAt: row.createdAt.toISOString(), + }; + }); + } + private async validateOperation( tx: Tx, userId: string, @@ -359,9 +454,7 @@ export class BankOperationsService { take: limit, }); return { - items: await Promise.all( - rows.map((row) => this.operationView(tx, row)), - ), + items: await this.pageViews(tx, rows), total, page, hasNext: page * limit < total,