Merge branch 'master' into 2140-add-updated-at-to-contributions

This commit is contained in:
Hannes Heine 2022-10-28 09:57:33 +02:00 committed by GitHub
commit a0bf498f40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 72 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mt-2"> <div class="mt-2">
<span v-for="({ type, text }, index) in linkifiedMessage" :key="index"> <span v-for="({ type, text }, index) in linkifiedMessage" :key="index">
<b-link v-if="type === 'link'" :to="text">{{ text }}</b-link> <b-link v-if="type === 'link'" :href="text">{{ text }}</b-link>
<span v-else>{{ text }}</span> <span v-else>{{ text }}</span>
</span> </span>
</div> </div>

View File

@ -6,8 +6,15 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { peterLustig } from '@/seeds/users/peter-lustig' import { peterLustig } from '@/seeds/users/peter-lustig'
import { cleanDB, testEnvironment } from '@test/helpers' import { cleanDB, testEnvironment } from '@test/helpers'
import { userFactory } from '@/seeds/factory/user' import { userFactory } from '@/seeds/factory/user'
import { login, createContributionLink, redeemTransactionLink } from '@/seeds/graphql/mutations' import {
login,
createContributionLink,
redeemTransactionLink,
createContribution,
updateContribution,
} from '@/seeds/graphql/mutations'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
import Decimal from 'decimal.js-light' import Decimal from 'decimal.js-light'
import { GraphQLError } from 'graphql' import { GraphQLError } from 'graphql'
@ -32,6 +39,7 @@ describe('TransactionLinkResolver', () => {
describe('redeem daily Contribution Link', () => { describe('redeem daily Contribution Link', () => {
const now = new Date() const now = new Date()
let contributionLink: DbContributionLink | undefined let contributionLink: DbContributionLink | undefined
let contribution: UnconfirmedContribution | undefined
beforeAll(async () => { beforeAll(async () => {
await mutate({ await mutate({
@ -79,6 +87,58 @@ describe('TransactionLinkResolver', () => {
) )
}) })
describe('user has pending contribution of 1000 GDD', () => {
beforeAll(async () => {
await mutate({
mutation: login,
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
})
const result = await mutate({
mutation: createContribution,
variables: {
amount: new Decimal(1000),
memo: 'I was brewing potions for the community the whole month',
creationDate: now.toISOString(),
},
})
contribution = result.data.createContribution
})
it('does not allow the user to redeem the contribution link', async () => {
await expect(
mutate({
mutation: redeemTransactionLink,
variables: {
code: 'CL-' + (contributionLink ? contributionLink.code : ''),
},
}),
).resolves.toMatchObject({
errors: [
new GraphQLError(
'Creation from contribution link was not successful. Error: The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.',
),
],
})
})
})
describe('user has no pending contributions that would not allow to redeem the link', () => {
beforeAll(async () => {
await mutate({
mutation: login,
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
})
await mutate({
mutation: updateContribution,
variables: {
contributionId: contribution ? contribution.id : -1,
amount: new Decimal(800),
memo: 'I was brewing potions for the community the whole month',
creationDate: now.toISOString(),
},
})
})
it('allows the user to redeem the contribution link', async () => { it('allows the user to redeem the contribution link', async () => {
await expect( await expect(
mutate({ mutate({
@ -120,7 +180,7 @@ describe('TransactionLinkResolver', () => {
jest.runAllTimers() jest.runAllTimers()
await mutate({ await mutate({
mutation: login, mutation: login,
variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
}) })
}) })
@ -163,6 +223,7 @@ describe('TransactionLinkResolver', () => {
}) })
}) })
}) })
})
describe('transactionLinkCode', () => { describe('transactionLinkCode', () => {
const date = new Date() const date = new Date()

View File

@ -258,7 +258,7 @@ export class TransactionLinkResolver {
} }
} }
const creations = await getUserCreation(user.id, false) const creations = await getUserCreation(user.id)
logger.info('open creations', creations) logger.info('open creations', creations)
validateContribution(creations, contributionLink.amount, now) validateContribution(creations, contributionLink.amount, now)
const contribution = new DbContribution() const contribution = new DbContribution()

View File

@ -1,4 +1,3 @@
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
import { getConnection } from '@dbTools/typeorm' import { getConnection } from '@dbTools/typeorm'
import { Contribution } from '@entity/Contribution' import { Contribution } from '@entity/Contribution'
@ -50,27 +49,27 @@ export const getUserCreations = async (
const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day' const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day'
logger.trace('getUserCreations dateFilter=', dateFilter) logger.trace('getUserCreations dateFilter=', dateFilter)
const unionString = includePending const sumAmountContributionPerUserAndLast3MonthQuery = queryRunner.manager
? ` .createQueryBuilder(Contribution, 'c')
UNION .select('month(contribution_date)', 'month')
SELECT contribution_date AS date, amount AS amount, user_id AS userId FROM contributions .addSelect('user_id', 'userId')
WHERE user_id IN (${ids.toString()}) .addSelect('sum(amount)', 'sum')
AND contribution_date >= ${dateFilter} .where(`user_id in (${ids.toString()})`)
AND confirmed_at IS NULL AND deleted_at IS NULL` .andWhere(`contribution_date >= ${dateFilter}`)
: '' .andWhere('deleted_at IS NULL')
logger.trace('getUserCreations unionString=', unionString) .andWhere('denied_at IS NULL')
.groupBy('month')
.addGroupBy('userId')
.orderBy('month', 'DESC')
const unionQuery = await queryRunner.manager.query(` if (!includePending) {
SELECT MONTH(date) AS month, sum(amount) AS sum, userId AS id FROM sumAmountContributionPerUserAndLast3MonthQuery.andWhere('confirmed_at IS NOT NULL')
(SELECT creation_date AS date, amount AS amount, user_id AS userId FROM transactions }
WHERE user_id IN (${ids.toString()})
AND type_id = ${TransactionTypeId.CREATION} const sumAmountContributionPerUserAndLast3Month =
AND creation_date >= ${dateFilter} await sumAmountContributionPerUserAndLast3MonthQuery.getRawMany()
${unionString}) AS result
GROUP BY month, userId logger.trace(sumAmountContributionPerUserAndLast3Month)
ORDER BY date DESC
`)
logger.trace('getUserCreations unionQuery=', unionQuery)
await queryRunner.release() await queryRunner.release()
@ -78,9 +77,9 @@ export const getUserCreations = async (
return { return {
id, id,
creations: months.map((month) => { creations: months.map((month) => {
const creation = unionQuery.find( const creation = sumAmountContributionPerUserAndLast3Month.find(
(raw: { month: string; id: string; creation: number[] }) => (raw: { month: string; userId: string; creation: number[] }) =>
parseInt(raw.month) === month && parseInt(raw.id) === id, parseInt(raw.month) === month && parseInt(raw.userId) === id,
) )
return MAX_CREATION_AMOUNT.minus(creation ? creation.sum : 0) return MAX_CREATION_AMOUNT.minus(creation ? creation.sum : 0)
}), }),

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mt-2"> <div class="mt-2">
<span v-for="({ type, text }, index) in linkifiedMessage" :key="index"> <span v-for="({ type, text }, index) in linkifiedMessage" :key="index">
<b-link v-if="type === 'link'" :to="text">{{ text }}</b-link> <b-link v-if="type === 'link'" :href="text">{{ text }}</b-link>
<span v-else>{{ text }}</span> <span v-else>{{ text }}</span>
</span> </span>
</div> </div>