diff --git a/backend/src/graphql/enum/TransactionTypeId.ts b/backend/src/graphql/enum/TransactionTypeId.ts index 497ad5055..38ee99cc6 100644 --- a/backend/src/graphql/enum/TransactionTypeId.ts +++ b/backend/src/graphql/enum/TransactionTypeId.ts @@ -6,6 +6,7 @@ export enum TransactionTypeId { RECEIVE = 3, // This is a virtual property, never occurring on the database DECAY = 4, + TRANSACTION_LINK, } registerEnumType(TransactionTypeId, { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1d1a00c39..33aa94211 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' -import { getCustomRepository, getConnection } from '@dbTools/typeorm' +import { getCustomRepository, getConnection, MoreThan } from '@dbTools/typeorm' import CONFIG from '@/config' import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' @@ -23,6 +23,7 @@ import { TransactionLinkRepository } from '@repository/TransactionLink' import { User as dbUser } from '@entity/User' import { Transaction as dbTransaction } from '@entity/Transaction' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { apiPost } from '@/apis/HttpRequest' import { TransactionTypeId } from '@enum/TransactionTypeId' @@ -30,6 +31,7 @@ import { calculateBalance, isHexPublicKey } from '@/util/validate' import { RIGHTS } from '@/auth/RIGHTS' import { User } from '@model/User' import { communityUser } from '@/util/communityUser' +import { virtualLinkTransaction } from '@/util/virtualLinkTransaction' import { virtualDecayTransaction } from '@/util/virtualDecayTransaction' import Decimal from 'decimal.js-light' import { calculateDecay } from '@/util/decay' @@ -112,11 +114,41 @@ export class TransactionResolver { const self = new User(user) const transactions: Transaction[] = [] + const transactionLinkRepository = getCustomRepository(TransactionLinkRepository) + const { sumHoldAvailableAmount, sumAmount } = await transactionLinkRepository.sumAmounts( + user.id, + now, + ) + // decay transaction if (!onlyCreations && currentPage === 1 && order === Order.DESC) { transactions.push( virtualDecayTransaction(lastTransaction.balance, lastTransaction.balanceDate, now, self), ) + // open transaction link sum transaction + if (sumHoldAvailableAmount.greaterThan(0)) { + const lastTransactionLink = await dbTransactionLink.find({ + where: { userId: self.id, redeemedBy: null, validUntil: MoreThan(now) }, + order: { createdAt: 'DESC' }, + take: 1, + }) + const firstTransactionLink = await dbTransactionLink.find({ + where: { userId: self.id, redeemedBy: null, validUntil: MoreThan(now) }, + order: { createdAt: 'ASC' }, + take: 1, + }) + transactions.push( + virtualLinkTransaction( + lastTransaction.balance, + sumAmount, + sumHoldAvailableAmount, + sumHoldAvailableAmount.minus(sumAmount.toString()), + firstTransactionLink[0].createdAt, + lastTransactionLink[0].validUntil, + self, + ), + ) + } } // transactions @@ -128,13 +160,10 @@ export class TransactionResolver { transactions.push(new Transaction(userTransaction, self, linkedUser)) }) - const transactionLinkRepository = getCustomRepository(TransactionLinkRepository) - const toHoldAvailable = await transactionLinkRepository.sumAmountToHoldAvailable(user.id, now) - // Construct Result return new TransactionList( calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now).balance.minus( - toHoldAvailable.toString(), + sumHoldAvailableAmount.toString(), ), transactions, userTransactionsCount, diff --git a/backend/src/typeorm/repository/TransactionLink.ts b/backend/src/typeorm/repository/TransactionLink.ts index 5f7531bc8..13a06ad0d 100644 --- a/backend/src/typeorm/repository/TransactionLink.ts +++ b/backend/src/typeorm/repository/TransactionLink.ts @@ -4,13 +4,23 @@ import Decimal from 'decimal.js-light' @EntityRepository(dbTransactionLink) export class TransactionLinkRepository extends Repository { - async sumAmountToHoldAvailable(userId: number, date: Date): Promise { - const { sum } = await this.createQueryBuilder('transactionLinks') - .select('SUM(transactionLinks.holdAvailableAmount)', 'sum') + async sumAmounts( + userId: number, + date: Date, + ): Promise<{ sumHoldAvailableAmount: Decimal; sumAmount: Decimal }> { + const { sumHoldAvailableAmount, sumAmount } = await this.createQueryBuilder('transactionLinks') + .select('SUM(transactionLinks.holdAvailableAmount)', 'sumHoldAvailableAmount') + .addSelect('SUM(transactionLinks.amount)', 'sumAmount') .where('transactionLinks.userId = :userId', { userId }) .andWhere('transactionLinks.redeemedAt is NULL') .andWhere('transactionLinks.validUntil > :date', { date }) + .orderBy('transactionLinks.createdAt', 'DESC') .getRawOne() - return sum ? new Decimal(sum) : new Decimal(0) + return { + sumHoldAvailableAmount: sumHoldAvailableAmount + ? new Decimal(sumHoldAvailableAmount) + : new Decimal(0), + sumAmount: sumAmount ? new Decimal(sumAmount) : new Decimal(0), + } } } diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index 0858ea50f..64524b12a 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -30,9 +30,9 @@ async function calculateBalance( // TODO why we have to use toString() here? const balance = decay.balance.add(amount.toString()) const transactionLinkRepository = getCustomRepository(TransactionLinkRepository) - const toHoldAvailable = await transactionLinkRepository.sumAmountToHoldAvailable(userId, time) + const { sumHoldAvailableAmount } = await transactionLinkRepository.sumAmounts(userId, time) - if (balance.minus(toHoldAvailable.toString()).lessThan(0)) { + if (balance.minus(sumHoldAvailableAmount.toString()).lessThan(0)) { return null } return { balance, lastTransactionId: lastTransaction.id, decay }