mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into test-transaction-page
This commit is contained in:
commit
610fbcc8fb
@ -4,12 +4,14 @@ export const communityStatistics = gql`
|
|||||||
query {
|
query {
|
||||||
communityStatistics {
|
communityStatistics {
|
||||||
totalUsers
|
totalUsers
|
||||||
activeUsers
|
|
||||||
deletedUsers
|
deletedUsers
|
||||||
totalGradidoCreated
|
totalGradidoCreated
|
||||||
totalGradidoDecayed
|
totalGradidoDecayed
|
||||||
totalGradidoAvailable
|
dynamicStatisticsFields {
|
||||||
totalGradidoUnbookedDecayed
|
activeUsers
|
||||||
|
totalGradidoAvailable
|
||||||
|
totalGradidoUnbookedDecayed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -17,12 +17,14 @@ const defaultData = () => {
|
|||||||
return {
|
return {
|
||||||
communityStatistics: {
|
communityStatistics: {
|
||||||
totalUsers: 3113,
|
totalUsers: 3113,
|
||||||
activeUsers: 1057,
|
|
||||||
deletedUsers: 35,
|
deletedUsers: 35,
|
||||||
totalGradidoCreated: '4083774.05000000000000000000',
|
totalGradidoCreated: '4083774.05000000000000000000',
|
||||||
totalGradidoDecayed: '-1062639.13634129622923372197',
|
totalGradidoDecayed: '-1062639.13634129622923372197',
|
||||||
totalGradidoAvailable: '2513565.869444365732411569',
|
dynamicStatisticsFields: {
|
||||||
totalGradidoUnbookedDecayed: '-500474.6738366222166261272',
|
activeUsers: 1057,
|
||||||
|
totalGradidoAvailable: '2513565.869444365732411569',
|
||||||
|
totalGradidoUnbookedDecayed: '-500474.6738366222166261272',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,9 @@ export default {
|
|||||||
return communityStatistics
|
return communityStatistics
|
||||||
},
|
},
|
||||||
update({ communityStatistics }) {
|
update({ communityStatistics }) {
|
||||||
this.statistics = communityStatistics
|
const totals = { ...communityStatistics.dynamicStatisticsFields }
|
||||||
|
this.statistics = { ...communityStatistics, ...totals }
|
||||||
|
delete this.statistics.dynamicStatisticsFields
|
||||||
},
|
},
|
||||||
error({ message }) {
|
error({ message }) {
|
||||||
this.toastError(message)
|
this.toastError(message)
|
||||||
|
|||||||
@ -2,13 +2,25 @@ import { ObjectType, Field } from 'type-graphql'
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class CommunityStatistics {
|
export class DynamicStatisticsFields {
|
||||||
@Field(() => Number)
|
|
||||||
totalUsers: number
|
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
activeUsers: number
|
activeUsers: number
|
||||||
|
|
||||||
|
@Field(() => Decimal)
|
||||||
|
totalGradidoAvailable: Decimal
|
||||||
|
|
||||||
|
@Field(() => Decimal)
|
||||||
|
totalGradidoUnbookedDecayed: Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class CommunityStatistics {
|
||||||
|
@Field(() => Number)
|
||||||
|
allUsers: number
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
totalUsers: number
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
deletedUsers: number
|
deletedUsers: number
|
||||||
|
|
||||||
@ -18,9 +30,7 @@ export class CommunityStatistics {
|
|||||||
@Field(() => Decimal)
|
@Field(() => Decimal)
|
||||||
totalGradidoDecayed: Decimal
|
totalGradidoDecayed: Decimal
|
||||||
|
|
||||||
@Field(() => Decimal)
|
// be carefull querying this, takes longer than 2 secs.
|
||||||
totalGradidoAvailable: Decimal
|
@Field(() => DynamicStatisticsFields)
|
||||||
|
dynamicStatisticsFields: DynamicStatisticsFields
|
||||||
@Field(() => Decimal)
|
|
||||||
totalGradidoUnbookedDecayed: Decimal
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,81 +1,113 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { Resolver, Query, Authorized } from 'type-graphql'
|
import { Resolver, Query, Authorized, FieldResolver } from 'type-graphql'
|
||||||
import { getConnection } from '@dbTools/typeorm'
|
import { getConnection } from '@dbTools/typeorm'
|
||||||
|
|
||||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
import { User as DbUser } from '@entity/User'
|
import { User as DbUser } from '@entity/User'
|
||||||
|
|
||||||
import { CommunityStatistics } from '@model/CommunityStatistics'
|
import { CommunityStatistics, DynamicStatisticsFields } from '@model/CommunityStatistics'
|
||||||
|
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
@Resolver((of) => CommunityStatistics)
|
||||||
|
|
||||||
@Resolver()
|
|
||||||
export class StatisticsResolver {
|
export class StatisticsResolver {
|
||||||
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
||||||
@Query(() => CommunityStatistics)
|
@Query(() => CommunityStatistics)
|
||||||
async communityStatistics(): Promise<CommunityStatistics> {
|
async communityStatistics(): Promise<CommunityStatistics> {
|
||||||
const allUsers = await DbUser.count({ withDeleted: true })
|
return new CommunityStatistics()
|
||||||
const totalUsers = await DbUser.count()
|
}
|
||||||
const deletedUsers = allUsers - totalUsers
|
|
||||||
|
|
||||||
|
@FieldResolver(() => Decimal)
|
||||||
|
async allUsers(): Promise<number> {
|
||||||
|
return await DbUser.count({ withDeleted: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async totalUsers(): Promise<number> {
|
||||||
|
return await DbUser.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async deletedUsers(): Promise<number> {
|
||||||
|
return (await this.allUsers()) - (await this.totalUsers())
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async totalGradidoCreated(): Promise<Decimal> {
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
try {
|
||||||
|
await queryRunner.connect()
|
||||||
|
const { totalGradidoCreated } = await queryRunner.manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
||||||
|
.from(DbTransaction, 'transaction')
|
||||||
|
.where('transaction.typeId = 1')
|
||||||
|
.getRawOne()
|
||||||
|
return totalGradidoCreated
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async totalGradidoDecayed(): Promise<Decimal> {
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
try {
|
||||||
|
await queryRunner.connect()
|
||||||
|
const { totalGradidoDecayed } = await queryRunner.manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.select('SUM(transaction.decay) AS totalGradidoDecayed')
|
||||||
|
.from(DbTransaction, 'transaction')
|
||||||
|
.where('transaction.decay IS NOT NULL')
|
||||||
|
.getRawOne()
|
||||||
|
return totalGradidoDecayed
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FieldResolver()
|
||||||
|
async dynamicStatisticsFields(): Promise<DynamicStatisticsFields> {
|
||||||
let totalGradidoAvailable: Decimal = new Decimal(0)
|
let totalGradidoAvailable: Decimal = new Decimal(0)
|
||||||
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
||||||
|
|
||||||
const receivedCallDate = new Date()
|
const receivedCallDate = new Date()
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
await queryRunner.connect()
|
try {
|
||||||
|
await queryRunner.connect()
|
||||||
|
|
||||||
const lastUserTransactions = await queryRunner.manager
|
const lastUserTransactions = await queryRunner.manager
|
||||||
.createQueryBuilder(DbUser, 'user')
|
.createQueryBuilder(DbUser, 'user')
|
||||||
.select('transaction.balance', 'balance')
|
.select('transaction.balance', 'balance')
|
||||||
.addSelect('transaction.balance_date', 'balanceDate')
|
.addSelect('transaction.balance_date', 'balanceDate')
|
||||||
.innerJoin(DbTransaction, 'transaction', 'user.id = transaction.user_id')
|
.innerJoin(DbTransaction, 'transaction', 'user.id = transaction.user_id')
|
||||||
.where(
|
.where(
|
||||||
`transaction.balance_date = (SELECT MAX(t.balance_date) FROM transactions AS t WHERE t.user_id = user.id)`,
|
`transaction.balance_date = (SELECT MAX(t.balance_date) FROM transactions AS t WHERE t.user_id = user.id)`,
|
||||||
)
|
)
|
||||||
.orderBy('transaction.balance_date', 'DESC')
|
.orderBy('transaction.balance_date', 'DESC')
|
||||||
.addOrderBy('transaction.id', 'DESC')
|
.addOrderBy('transaction.id', 'DESC')
|
||||||
.getRawMany()
|
.getRawMany()
|
||||||
|
|
||||||
const activeUsers = lastUserTransactions.length
|
const activeUsers = lastUserTransactions.length
|
||||||
|
|
||||||
lastUserTransactions.forEach(({ balance, balanceDate }) => {
|
lastUserTransactions.forEach(({ balance, balanceDate }) => {
|
||||||
const decay = calculateDecay(new Decimal(balance), new Date(balanceDate), receivedCallDate)
|
const decay = calculateDecay(new Decimal(balance), new Date(balanceDate), receivedCallDate)
|
||||||
if (decay) {
|
if (decay) {
|
||||||
totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString())
|
totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString())
|
||||||
totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString())
|
totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeUsers,
|
||||||
|
totalGradidoAvailable,
|
||||||
|
totalGradidoUnbookedDecayed,
|
||||||
}
|
}
|
||||||
})
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
const { totalGradidoCreated } = await queryRunner.manager
|
|
||||||
.createQueryBuilder()
|
|
||||||
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
|
||||||
.from(DbTransaction, 'transaction')
|
|
||||||
.where('transaction.typeId = 1')
|
|
||||||
.getRawOne()
|
|
||||||
|
|
||||||
const { totalGradidoDecayed } = await queryRunner.manager
|
|
||||||
.createQueryBuilder()
|
|
||||||
.select('SUM(transaction.decay) AS totalGradidoDecayed')
|
|
||||||
.from(DbTransaction, 'transaction')
|
|
||||||
.where('transaction.decay IS NOT NULL')
|
|
||||||
.getRawOne()
|
|
||||||
|
|
||||||
await queryRunner.release()
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalUsers,
|
|
||||||
activeUsers,
|
|
||||||
deletedUsers,
|
|
||||||
totalGradidoCreated,
|
|
||||||
totalGradidoDecayed,
|
|
||||||
totalGradidoAvailable,
|
|
||||||
totalGradidoUnbookedDecayed,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ describe('ContributionForm', () => {
|
|||||||
const mocks = {
|
const mocks = {
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => d),
|
||||||
|
$n: jest.fn((n) => n),
|
||||||
$store: {
|
$store: {
|
||||||
state: {
|
state: {
|
||||||
creation: ['1000', '1000', '1000'],
|
creation: ['1000', '1000', '1000'],
|
||||||
|
|||||||
@ -9,10 +9,10 @@ describe('InputAmount', () => {
|
|||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
|
$n: jest.fn((n) => n),
|
||||||
$i18n: {
|
$i18n: {
|
||||||
locale: jest.fn(() => 'en'),
|
locale: jest.fn(() => 'en'),
|
||||||
},
|
},
|
||||||
$n: jest.fn((n) => String(n)),
|
|
||||||
$route: {
|
$route: {
|
||||||
params: {},
|
params: {},
|
||||||
},
|
},
|
||||||
@ -46,13 +46,14 @@ describe('InputAmount', () => {
|
|||||||
|
|
||||||
describe('amount normalization', () => {
|
describe('amount normalization', () => {
|
||||||
describe('if invalid', () => {
|
describe('if invalid', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
|
await wrapper.setProps({ value: '12m34' })
|
||||||
valid = false
|
valid = false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('is not normalized', () => {
|
it('is not normalized', () => {
|
||||||
wrapper.vm.normalizeAmount(valid)
|
wrapper.vm.normalizeAmount(false)
|
||||||
expect(wrapper.vm.amountValue).toBe(0.0)
|
expect(wrapper.vm.currentValue).toBe('12m34')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -97,13 +98,14 @@ describe('InputAmount', () => {
|
|||||||
|
|
||||||
describe('amount normalization', () => {
|
describe('amount normalization', () => {
|
||||||
describe('if invalid', () => {
|
describe('if invalid', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
|
await wrapper.setProps({ value: '12m34' })
|
||||||
valid = false
|
valid = false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('is not normalized', () => {
|
it('is not normalized', () => {
|
||||||
wrapper.vm.normalizeAmount(valid)
|
wrapper.vm.normalizeAmount(valid)
|
||||||
expect(wrapper.vm.amountValue).toBe(0.0)
|
expect(wrapper.vm.currentValue).toBe('12m34')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
trim
|
trim
|
||||||
v-focus="amountFocused"
|
v-focus="amountFocused"
|
||||||
@focus="amountFocused = true"
|
@focus="amountFocused = true"
|
||||||
@blur="normalizeAmount(true)"
|
@blur="normalizeAmount(valid)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
></b-form-input>
|
></b-form-input>
|
||||||
@ -90,5 +90,8 @@ export default {
|
|||||||
this.currentValue = this.$n(this.amountValue, 'ungroupedDecimal')
|
this.currentValue = this.$n(this.amountValue, 'ungroupedDecimal')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.value !== '') this.normalizeAmount(true)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -13,7 +13,6 @@ export const verifyLogin = gql`
|
|||||||
hasElopage
|
hasElopage
|
||||||
publisherId
|
publisherId
|
||||||
isAdmin
|
isAdmin
|
||||||
creation
|
|
||||||
hideAmountGDD
|
hideAmountGDD
|
||||||
hideAmountGDT
|
hideAmountGDT
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user