mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 1633-Display-QR-Code-on-link-in-transaction-list
This commit is contained in:
commit
98ae90c05e
19
CHANGELOG.md
19
CHANGELOG.md
@ -4,8 +4,27 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [1.7.1](https://github.com/gradido/gradido/compare/1.7.0...1.7.1)
|
||||
|
||||
- fix: Localize Dates on Redeem Transaction Link Page [`#1720`](https://github.com/gradido/gradido/pull/1720)
|
||||
- fix: Round Virtual Transaction Link Transaction [`#1718`](https://github.com/gradido/gradido/pull/1718)
|
||||
- larger icon and deacy information if center [`#1719`](https://github.com/gradido/gradido/pull/1719)
|
||||
- Fix: restore script load correct .env [`#1717`](https://github.com/gradido/gradido/pull/1717)
|
||||
- fix-disbled-button-if-totalBalance [`#1716`](https://github.com/gradido/gradido/pull/1716)
|
||||
- icon droplet-halflarger and correctly positioned [`#1713`](https://github.com/gradido/gradido/pull/1713)
|
||||
- fix: Clean up Registration Flow [`#1709`](https://github.com/gradido/gradido/pull/1709)
|
||||
- 1703 submit button disabled when total amount to submit is minus [`#1705`](https://github.com/gradido/gradido/pull/1705)
|
||||
- add extra disabled variable for send emit, disabled send by emit [`#1704`](https://github.com/gradido/gradido/pull/1704)
|
||||
- Fix: Correct calculation of decay [`#1699`](https://github.com/gradido/gradido/pull/1699)
|
||||
- Fix: Allow sending of more then half of my wealth via link [`#1700`](https://github.com/gradido/gradido/pull/1700)
|
||||
- feat: Seed Creations Months Ago From Now [`#1702`](https://github.com/gradido/gradido/pull/1702)
|
||||
- Fix: Frontend show proper error message on failed send [`#1701`](https://github.com/gradido/gradido/pull/1701)
|
||||
|
||||
#### [1.7.0](https://github.com/gradido/gradido/compare/1.6.6...1.7.0)
|
||||
|
||||
> 30 March 2022
|
||||
|
||||
- v1.7.0 [`#1698`](https://github.com/gradido/gradido/pull/1698)
|
||||
- folder for new style images [`#1694`](https://github.com/gradido/gradido/pull/1694)
|
||||
- fix: No Email Exposed on Forgot Password [`#1696`](https://github.com/gradido/gradido/pull/1696)
|
||||
- fix: No Decay Calculation in Frontend [`#1692`](https://github.com/gradido/gradido/pull/1692)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"description": "Administraion Interface for Gradido",
|
||||
"main": "index.js",
|
||||
"author": "Moriz Wahl",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.1",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-backend",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.1",
|
||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/backend",
|
||||
|
||||
@ -12,15 +12,22 @@ export class Transaction {
|
||||
this.user = user
|
||||
this.previous = transaction.previous
|
||||
this.typeId = transaction.typeId
|
||||
this.amount = transaction.amount
|
||||
this.balance = transaction.balance
|
||||
this.amount = transaction.amount.toDecimalPlaces(2, Decimal.ROUND_DOWN)
|
||||
this.balance = transaction.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN)
|
||||
this.balanceDate = transaction.balanceDate
|
||||
if (!transaction.decayStart) {
|
||||
this.decay = new Decay(transaction.balance, new Decimal(0), null, null, null)
|
||||
// TODO: hot fix, we should separate decay calculation from decay graphql model
|
||||
this.decay = new Decay(
|
||||
transaction.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN),
|
||||
new Decimal(0),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
} else {
|
||||
this.decay = new Decay(
|
||||
transaction.balance,
|
||||
transaction.decay,
|
||||
transaction.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN),
|
||||
transaction.decay.toDecimalPlaces(2, Decimal.ROUND_FLOOR),
|
||||
transaction.decayStart,
|
||||
transaction.balanceDate,
|
||||
Math.round((transaction.balanceDate.getTime() - transaction.decayStart.getTime()) / 1000),
|
||||
|
||||
@ -54,19 +54,23 @@ export class BalanceResolver {
|
||||
},
|
||||
})
|
||||
|
||||
// The decay is always calculated on the last booked transaction
|
||||
const calculatedDecay = calculateDecay(
|
||||
lastTransaction.balance,
|
||||
lastTransaction.balanceDate,
|
||||
now,
|
||||
)
|
||||
|
||||
// The final balance is reduced by the link amount withheld
|
||||
const transactionLinkRepository = getCustomRepository(TransactionLinkRepository)
|
||||
const { sumHoldAvailableAmount } = context.sumHoldAvailableAmount
|
||||
? { sumHoldAvailableAmount: context.sumHoldAvailableAmount }
|
||||
: await transactionLinkRepository.summary(user.id, now)
|
||||
|
||||
const calculatedDecay = calculateDecay(
|
||||
lastTransaction.balance.minus(sumHoldAvailableAmount.toString()),
|
||||
lastTransaction.balanceDate,
|
||||
now,
|
||||
)
|
||||
|
||||
return new Balance({
|
||||
balance: calculatedDecay.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN), // round towards zero
|
||||
balance: calculatedDecay.balance
|
||||
.minus(sumHoldAvailableAmount.toString())
|
||||
.toDecimalPlaces(2, Decimal.ROUND_DOWN), // round towards zero
|
||||
decay: calculatedDecay.decay.toDecimalPlaces(2, Decimal.ROUND_FLOOR), // round towards - infinity
|
||||
lastBookedBalance: lastTransaction.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN),
|
||||
balanceGDT,
|
||||
|
||||
@ -57,7 +57,12 @@ export const executeTransaction = async (
|
||||
|
||||
// validate amount
|
||||
const receivedCallDate = new Date()
|
||||
const sendBalance = await calculateBalance(sender.id, amount.mul(-1), receivedCallDate)
|
||||
const sendBalance = await calculateBalance(
|
||||
sender.id,
|
||||
amount.mul(-1),
|
||||
receivedCallDate,
|
||||
transactionLink,
|
||||
)
|
||||
if (!sendBalance) {
|
||||
throw new Error("user hasn't enough GDD or amount is < 0")
|
||||
}
|
||||
@ -198,12 +203,15 @@ export class TransactionResolver {
|
||||
|
||||
// decay & link transactions
|
||||
if (currentPage === 1 && order === Order.DESC) {
|
||||
// The virtual decay is always on the booked amount, not including the generated, not yet booked links,
|
||||
// since the decay is substantially different when the amount is less
|
||||
transactions.push(
|
||||
virtualDecayTransaction(
|
||||
lastTransaction.balance.minus(sumHoldAvailableAmount.toString()),
|
||||
lastTransaction.balance,
|
||||
lastTransaction.balanceDate,
|
||||
now,
|
||||
self,
|
||||
sumHoldAvailableAmount,
|
||||
),
|
||||
)
|
||||
// virtual transaction for pending transaction-links sum
|
||||
|
||||
@ -4,4 +4,6 @@ export interface CreationInterface {
|
||||
memo: string
|
||||
creationDate: string
|
||||
confirmed?: boolean
|
||||
// number of months to move the confirmed creation to the past
|
||||
moveCreationDate?: number
|
||||
}
|
||||
|
||||
@ -1,29 +1,28 @@
|
||||
import { CreationInterface } from './CreationInterface'
|
||||
|
||||
const lastMonth = (date: Date): string => {
|
||||
return new Date(date.getFullYear(), date.getMonth() - 1, 1).toISOString()
|
||||
}
|
||||
import { nMonthsBefore } from '../factory/creation'
|
||||
|
||||
export const creations: CreationInterface[] = [
|
||||
{
|
||||
email: 'bibi@bloxberg.de',
|
||||
amount: 1000,
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
creationDate: lastMonth(new Date()),
|
||||
creationDate: nMonthsBefore(new Date()),
|
||||
confirmed: true,
|
||||
moveCreationDate: 12,
|
||||
},
|
||||
{
|
||||
email: 'bob@baumeister.de',
|
||||
amount: 1000,
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
creationDate: lastMonth(new Date()),
|
||||
creationDate: nMonthsBefore(new Date()),
|
||||
confirmed: true,
|
||||
moveCreationDate: 8,
|
||||
},
|
||||
{
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
amount: 1000,
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
creationDate: lastMonth(new Date()),
|
||||
creationDate: nMonthsBefore(new Date()),
|
||||
confirmed: true,
|
||||
},
|
||||
]
|
||||
|
||||
@ -6,9 +6,14 @@ import { login } from '@/seeds/graphql/queries'
|
||||
import { CreationInterface } from '@/seeds/creation/CreationInterface'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { User } from '@entity/User'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
||||
// import CONFIG from '@/config/index'
|
||||
|
||||
export const nMonthsBefore = (date: Date, months = 1): string => {
|
||||
return new Date(date.getFullYear(), date.getMonth() - months, 1).toISOString()
|
||||
}
|
||||
|
||||
export const creationFactory = async (
|
||||
client: ApolloServerTestClient,
|
||||
creation: CreationInterface,
|
||||
@ -34,5 +39,21 @@ export const creationFactory = async (
|
||||
})
|
||||
|
||||
await mutate({ mutation: confirmPendingCreation, variables: { id: pendingCreation.id } })
|
||||
|
||||
if (creation.moveCreationDate) {
|
||||
const transaction = await Transaction.findOneOrFail({
|
||||
where: { userId: user.id, creationDate: new Date(creation.creationDate) },
|
||||
order: { balanceDate: 'DESC' },
|
||||
})
|
||||
if (transaction.decay.equals(0) && transaction.creationDate) {
|
||||
transaction.creationDate = new Date(
|
||||
nMonthsBefore(transaction.creationDate, creation.moveCreationDate),
|
||||
)
|
||||
transaction.balanceDate = new Date(
|
||||
nMonthsBefore(transaction.balanceDate, creation.moveCreationDate),
|
||||
)
|
||||
await transaction.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { Transaction } from '@entity/Transaction'
|
||||
import { Decay } from '@model/Decay'
|
||||
import { getCustomRepository } from '@dbTools/typeorm'
|
||||
import { TransactionLinkRepository } from '@repository/TransactionLink'
|
||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||
|
||||
function isStringBoolean(value: string): boolean {
|
||||
const lowerValue = value.toLowerCase()
|
||||
@ -21,6 +22,7 @@ async function calculateBalance(
|
||||
userId: number,
|
||||
amount: Decimal,
|
||||
time: Date,
|
||||
transactionLink?: dbTransactionLink | null,
|
||||
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> {
|
||||
const lastTransaction = await Transaction.findOne({ userId }, { order: { balanceDate: 'DESC' } })
|
||||
if (!lastTransaction) return null
|
||||
@ -32,7 +34,13 @@ async function calculateBalance(
|
||||
const transactionLinkRepository = getCustomRepository(TransactionLinkRepository)
|
||||
const { sumHoldAvailableAmount } = await transactionLinkRepository.summary(userId, time)
|
||||
|
||||
if (balance.minus(sumHoldAvailableAmount.toString()).lessThan(0)) {
|
||||
// If we want to redeem a link we need to make sure that the link amount is not considered as blocked
|
||||
// else we cannot redeem links which are more or equal to half of what an account actually owns
|
||||
const releasedLinkAmount = transactionLink ? transactionLink.holdAvailableAmount : new Decimal(0)
|
||||
|
||||
if (
|
||||
balance.minus(sumHoldAvailableAmount.toString()).plus(releasedLinkAmount.toString()).lessThan(0)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
return { balance, lastTransactionId: lastTransaction.id, decay }
|
||||
|
||||
@ -42,11 +42,11 @@ const virtualLinkTransaction = (
|
||||
userId: -1,
|
||||
previous: -1,
|
||||
typeId: TransactionTypeId.LINK_SUMMARY,
|
||||
amount: amount,
|
||||
balance: balance,
|
||||
amount: amount.toDecimalPlaces(2, Decimal.ROUND_FLOOR),
|
||||
balance: balance.toDecimalPlaces(2, Decimal.ROUND_DOWN),
|
||||
balanceDate: validUntil,
|
||||
decayStart: createdAt,
|
||||
decay: decay,
|
||||
decay: decay.toDecimalPlaces(2, Decimal.ROUND_FLOOR),
|
||||
memo: '',
|
||||
creationDate: null,
|
||||
...defaultModelFunctions,
|
||||
@ -59,6 +59,7 @@ const virtualDecayTransaction = (
|
||||
balanceDate: Date,
|
||||
time: Date = new Date(),
|
||||
user: User,
|
||||
holdAvailabeAmount: Decimal,
|
||||
): Transaction => {
|
||||
const decay = calculateDecay(balance, balanceDate, time)
|
||||
// const balance = decay.balance.minus(lastTransaction.balance)
|
||||
@ -67,10 +68,12 @@ const virtualDecayTransaction = (
|
||||
userId: -1,
|
||||
previous: -1,
|
||||
typeId: TransactionTypeId.DECAY,
|
||||
amount: decay.decay ? decay.decay : new Decimal(0), // new Decimal(0), // this kinda is wrong, but helps with the frontend query
|
||||
balance: decay.balance,
|
||||
amount: decay.decay ? decay.decay.toDecimalPlaces(2, Decimal.ROUND_FLOOR) : new Decimal(0), // new Decimal(0), // this kinda is wrong, but helps with the frontend query
|
||||
balance: decay.balance
|
||||
.minus(holdAvailabeAmount.toString())
|
||||
.toDecimalPlaces(2, Decimal.ROUND_DOWN),
|
||||
balanceDate: time,
|
||||
decay: decay.decay ? decay.decay : new Decimal(0),
|
||||
decay: decay.decay ? decay.decay.toDecimalPlaces(2, Decimal.ROUND_FLOOR) : new Decimal(0),
|
||||
decayStart: decay.start,
|
||||
memo: '',
|
||||
creationDate: null,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-database",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.1",
|
||||
"description": "Gradido Database Tool to execute database migrations",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/database",
|
||||
|
||||
@ -15,13 +15,13 @@ if [ ! -f "$BACKUP_FILE" ]; then
|
||||
return "File '$BACKUP_FILE' does not exist" 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
# Load backend .env for DB_USERNAME, DB_PASSWORD & DB_DATABASE
|
||||
# Load database .env for DB_USERNAME, DB_PASSWORD & DB_DATABASE
|
||||
# NOTE: all config values will be in process.env when starting
|
||||
# the services and will therefore take precedence over the .env
|
||||
if [ -f "$PROJECT_ROOT/backend/.env" ]; then
|
||||
export $(cat $PROJECT_ROOT/backend/.env | sed 's/#.*//g' | xargs)
|
||||
if [ -f "$PROJECT_ROOT/database/.env" ]; then
|
||||
export $(cat $PROJECT_ROOT/database/.env | sed 's/#.*//g' | xargs)
|
||||
else
|
||||
export $(cat $PROJECT_ROOT/backend/.env.dist | sed 's/#.*//g' | xargs)
|
||||
export $(cat $PROJECT_ROOT/database/.env.dist | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
# Stop gradido-backend service
|
||||
@ -30,6 +30,11 @@ pm2 stop gradido-backend
|
||||
# Backup data
|
||||
mysqldump --databases --single-transaction --quick --hex-blob --lock-tables=false > ${SCRIPT_DIR}/backup/mariadb-restore-backup-$(date +%d-%m-%Y_%H-%M-%S).sql -u ${DB_USER} -p${DB_PASSWORD} ${DB_DATABASE}
|
||||
|
||||
# Drop Database
|
||||
mysql -u ${DB_USER} -p${DB_PASSWORD} <<EOFMYSQL
|
||||
DROP DATABASE $DB_DATABASE
|
||||
EOFMYSQL
|
||||
|
||||
# Restore Data
|
||||
mysql -u ${DB_USER} -p${DB_PASSWORD} <<EOFMYSQL
|
||||
source $BACKUP_FILE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bootstrap-vue-gradido-wallet",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node run/server.js",
|
||||
|
||||
@ -1,22 +1,27 @@
|
||||
<template>
|
||||
<div class="decayinformation-decay">
|
||||
<div class="d-flex">
|
||||
<div class="text-center pb-3 gradido-max-width">
|
||||
<b-icon icon="droplet-half" height="12" class="mb-2" />
|
||||
<b>{{ $t('decay.calculation_decay') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.decay') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div>
|
||||
{{ previousBookedBalance | GDD }}
|
||||
{{ decay | GDD }} {{ $t('math.equal') }}
|
||||
<b>{{ balance | GDD }}</b>
|
||||
<b-col cols="1"></b-col>
|
||||
<b-col cols="11">
|
||||
<div class="d-flex">
|
||||
<div class="text-center pb-3 gradido-max-width">
|
||||
<b-icon icon="droplet-half" class="mr-2" />
|
||||
<b>{{ $t('decay.calculation_decay') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-row>
|
||||
<b-col cols="5" class="text-right">
|
||||
<div>{{ $t('decay.decay') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="7">
|
||||
<div>
|
||||
{{ previousBookedBalance | GDD }}
|
||||
{{ decay | GDD }} {{ $t('math.equal') }}
|
||||
<b>{{ balance | GDD }}</b>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
@ -1,64 +1,69 @@
|
||||
<template>
|
||||
<div class="decayinformation-long">
|
||||
<div class="d-flex">
|
||||
<div class="text-center pb-3 gradido-max-width">
|
||||
<b-icon icon="droplet-half" height="12" class="mb-2" />
|
||||
<b>{{ $t('decay.calculation_decay') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.last_transaction') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div>
|
||||
<span>
|
||||
{{ $d(new Date(decay.start), 'long') }}
|
||||
</span>
|
||||
<b-col cols="1"></b-col>
|
||||
<b-col cols="11">
|
||||
<div class="d-flex">
|
||||
<div class="text-center pb-3 gradido-max-width">
|
||||
<b-icon icon="droplet-half" class="mr-2" />
|
||||
<b>{{ $t('decay.calculation_decay') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.past_time') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<span v-if="duration">{{ durationText }}</span>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<!-- Decay-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.decay') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">{{ decay.decay | GDD }}</b-col>
|
||||
</b-row>
|
||||
<hr class="mt-2 mb-2" />
|
||||
<b-row>
|
||||
<b-col class="text-center pt-3 pb-2">
|
||||
<b>{{ $t('decay.calculation_total') }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<!-- Type-->
|
||||
<b-row>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
|
||||
<b-col cols="6" class="text-right">{{ $t(`decay.types.${typeId.toLowerCase()}`) }}</b-col>
|
||||
<b-col cols="6">{{ amount | GDD }}</b-col>
|
||||
</b-row>
|
||||
<!-- Decay-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">{{ $t('decay.decay') }}</b-col>
|
||||
<b-col cols="6">{{ decay.decay | GDD }}</b-col>
|
||||
</b-row>
|
||||
<!-- Total-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.total') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<b>{{ (Number(amount) + Number(decay.decay)) | GDD }}</b>
|
||||
<b-row>
|
||||
<b-col cols="5" class="text-right">
|
||||
<div>{{ $t('decay.last_transaction') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="7">
|
||||
<div>
|
||||
<span>
|
||||
{{ $d(new Date(decay.start), 'long') }}
|
||||
</span>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="5" class="text-right">
|
||||
<div>{{ $t('decay.past_time') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="7">
|
||||
<span v-if="duration">{{ durationText }}</span>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<!-- Decay-->
|
||||
<b-row>
|
||||
<b-col cols="5" class="text-right">
|
||||
<div>{{ $t('decay.decay') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="7">{{ decay.decay | GDD }}</b-col>
|
||||
</b-row>
|
||||
<hr class="mt-2 mb-2" />
|
||||
<b-row>
|
||||
<b-col class="text-center pt-3 pb-2">
|
||||
<b>{{ $t('decay.calculation_total') }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<!-- Type-->
|
||||
<b-row>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
|
||||
<b-col cols="5" class="text-right">{{ $t(`decay.types.${typeId.toLowerCase()}`) }}</b-col>
|
||||
<b-col cols="7">{{ amount | GDD }}</b-col>
|
||||
</b-row>
|
||||
<!-- Decay-->
|
||||
<b-row>
|
||||
<b-col cols="5" class="text-right">{{ $t('decay.decay') }}</b-col>
|
||||
<b-col cols="7">{{ decay.decay | GDD }}</b-col>
|
||||
</b-row>
|
||||
<!-- Total-->
|
||||
<b-row>
|
||||
<b-col cols="5" class="text-right">
|
||||
<div>{{ $t('decay.total') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="7">
|
||||
<b>{{ (Number(amount) + Number(decay.decay)) | GDD }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
@ -28,14 +28,12 @@
|
||||
<strong>{{ $t('gdd_per_link.decay-14-day') }}</strong>
|
||||
</b-col>
|
||||
<b-col class="text-right borderbottom">
|
||||
<strong>{{ $t('math.aprox') }} {{ (amount * 0.028 * -1) | GDD }}</strong>
|
||||
<strong>{{ $t('math.aprox') }} {{ (amount * -0.028) | GDD }}</strong>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="pr-3">
|
||||
<b-col class="text-right">{{ $t('form.new_balance') }}</b-col>
|
||||
<b-col class="text-right">
|
||||
{{ $t('math.aprox') }} {{ (balance - amount - amount * 0.028) | GDD }}
|
||||
</b-col>
|
||||
<b-col class="text-right">{{ $t('math.aprox') }} {{ totalBalance | GDD }}</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
|
||||
@ -44,7 +42,7 @@
|
||||
<b-button @click="$emit('on-reset')">{{ $t('form.cancel') }}</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-button variant="success" :disabled="loading" @click="$emit('send-transaction')">
|
||||
<b-button variant="success" :disabled="disabled" @click="$emit('send-transaction')">
|
||||
{{ $t('form.generate_now') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
@ -60,7 +58,17 @@ export default {
|
||||
amount: { type: Number, required: true },
|
||||
memo: { type: String, required: true },
|
||||
loading: { type: Boolean, required: true },
|
||||
selected: { type: String, required: true },
|
||||
},
|
||||
computed: {
|
||||
totalBalance() {
|
||||
return this.balance - this.amount * 1.028
|
||||
},
|
||||
disabled() {
|
||||
if (this.totalBalance < 0) {
|
||||
return true
|
||||
}
|
||||
return this.loading
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -45,6 +45,33 @@ describe('GddSend confirm', () => {
|
||||
it('renders the component div.confirm-box-send', () => {
|
||||
expect(wrapper.find('div.confirm-box-send').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('send now button', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('single click', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('button.btn-success').trigger('click')
|
||||
})
|
||||
|
||||
it('emits send transaction one time', () => {
|
||||
expect(wrapper.emitted('send-transaction')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('double click', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('button.btn-success').trigger('click')
|
||||
await wrapper.find('button.btn-success').trigger('click')
|
||||
})
|
||||
|
||||
it('emits send transaction one time', () => {
|
||||
expect(wrapper.emitted('send-transaction')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -58,7 +58,11 @@
|
||||
<b-button @click="$emit('on-reset')">{{ $t('form.cancel') }}</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-button variant="success" :disabled="loading" @click="$emit('send-transaction')">
|
||||
<b-button
|
||||
variant="success"
|
||||
:disabled="disabled"
|
||||
@click="$emit('send-transaction'), (disabled = true)"
|
||||
>
|
||||
{{ $t('form.send_now') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
@ -73,8 +77,11 @@ export default {
|
||||
email: { type: String, required: false, default: '' },
|
||||
amount: { type: Number, required: true },
|
||||
memo: { type: String, required: true },
|
||||
loading: { type: Boolean, required: true },
|
||||
selected: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
disabled: false,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
<div class="mb-3 text-center">
|
||||
<div class="mt-3">
|
||||
{{ $t('gdd_per_link.no-redeem') }}
|
||||
<a to="/transactions" href="#!">
|
||||
<b-link to="/transactions">
|
||||
<b>{{ $t('gdd_per_link.link-overview') }}</b>
|
||||
</a>
|
||||
</b-link>
|
||||
</div>
|
||||
</div>
|
||||
</b-jumbotron>
|
||||
|
||||
@ -4,6 +4,7 @@ import { toastErrorSpy, toastSuccessSpy } from '@test/testSetup'
|
||||
import { TRANSACTION_STEPS } from '@/components/GddSend.vue'
|
||||
import { sendCoins, createTransactionLink } from '@/graphql/mutations.js'
|
||||
import DashboardLayout from '@/layouts/DashboardLayout_gdd.vue'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
const apolloMutationMock = jest.fn()
|
||||
apolloMutationMock.mockResolvedValue('success')
|
||||
@ -57,12 +58,13 @@ describe('Send', () => {
|
||||
|
||||
describe('fill transaction form for send coins', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
selected: SEND_TYPES.send,
|
||||
})
|
||||
const transactionForm = wrapper.findComponent({ name: 'TransactionForm' })
|
||||
await transactionForm.findAll('input[type="radio"]').at(0).setChecked()
|
||||
await transactionForm.find('input[type="email"]').setValue('user@example.org')
|
||||
await transactionForm.find('input[type="text"]').setValue('23.45')
|
||||
await transactionForm.find('textarea').setValue('Make the best of it!')
|
||||
await transactionForm.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('steps forward in the dialog', () => {
|
||||
@ -74,7 +76,8 @@ describe('Send', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmationSend' })
|
||||
.vm.$emit('on-reset')
|
||||
.find('button.btn-secondary')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
it('shows the transaction formular again', () => {
|
||||
@ -98,7 +101,8 @@ describe('Send', () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmationSend' })
|
||||
.vm.$emit('send-transaction')
|
||||
.find('button.btn-success')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
it('calls the API when send-transaction is emitted', async () => {
|
||||
@ -131,7 +135,8 @@ describe('Send', () => {
|
||||
apolloMutationMock.mockRejectedValue({ message: 'recipient not known' })
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmationSend' })
|
||||
.vm.$emit('send-transaction')
|
||||
.find('button.btn-success')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
it('has a component TransactionResultSendError', () => {
|
||||
@ -155,18 +160,17 @@ describe('Send', () => {
|
||||
})
|
||||
})
|
||||
|
||||
/* LINK */
|
||||
|
||||
describe('transaction form link', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutationMock.mockResolvedValue({
|
||||
data: { createTransactionLink: { code: '0123456789' } },
|
||||
})
|
||||
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
|
||||
amount: 56.78,
|
||||
memo: 'Make the best of the link!',
|
||||
selected: SEND_TYPES.link,
|
||||
})
|
||||
const transactionForm = wrapper.findComponent({ name: 'TransactionForm' })
|
||||
await transactionForm.findAll('input[type="radio"]').at(1).setChecked()
|
||||
await transactionForm.find('input[type="text"]').setValue('56.78')
|
||||
await transactionForm.find('textarea').setValue('Make the best of the link!')
|
||||
await transactionForm.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('steps forward in the dialog', () => {
|
||||
@ -178,7 +182,8 @@ describe('Send', () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmationLink' })
|
||||
.vm.$emit('send-transaction')
|
||||
.find('button.btn-success')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
it('calls the API when send-transaction is emitted', async () => {
|
||||
@ -254,14 +259,17 @@ describe('Send', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('send apollo if transaction link with error', () => {
|
||||
beforeEach(() => {
|
||||
describe('apollo call returns error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutationMock.mockRejectedValue({ message: 'OUCH!' })
|
||||
wrapper.find('button.btn-success').trigger('click')
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmationLink' })
|
||||
.find('button.btn-success')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith({ message: 'OUCH!' })
|
||||
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -12,11 +12,9 @@
|
||||
<template #transactionConfirmationSend>
|
||||
<transaction-confirmation-send
|
||||
:balance="balance"
|
||||
:selected="transactionData.selected"
|
||||
:email="transactionData.email"
|
||||
:amount="transactionData.amount"
|
||||
:memo="transactionData.memo"
|
||||
:loading="loading"
|
||||
@send-transaction="sendTransaction"
|
||||
@on-reset="onReset"
|
||||
></transaction-confirmation-send>
|
||||
@ -24,7 +22,6 @@
|
||||
<template #transactionConfirmationLink>
|
||||
<transaction-confirmation-link
|
||||
:balance="balance"
|
||||
:selected="transactionData.selected"
|
||||
:email="transactionData.email"
|
||||
:amount="transactionData.amount"
|
||||
:memo="transactionData.memo"
|
||||
@ -133,8 +130,8 @@ export default {
|
||||
this.transactionData = { ...EMPTY_TRANSACTION_DATA }
|
||||
this.currentTransactionStep = TRANSACTION_STEPS.transactionResultSendSuccess
|
||||
})
|
||||
.catch((err) => {
|
||||
this.errorResult = err.message
|
||||
.catch((error) => {
|
||||
this.errorResult = error.message
|
||||
this.error = true
|
||||
this.currentTransactionStep = TRANSACTION_STEPS.transactionResultSendError
|
||||
})
|
||||
@ -153,7 +150,7 @@ export default {
|
||||
this.updateTransactions({})
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error)
|
||||
this.toastError(error.message)
|
||||
})
|
||||
break
|
||||
default:
|
||||
|
||||
@ -12,6 +12,10 @@ const routerPushMock = jest.fn()
|
||||
|
||||
const now = new Date().toISOString()
|
||||
|
||||
const stubs = {
|
||||
RouterLink: true,
|
||||
}
|
||||
|
||||
const transactionLinkValidExpireDate = () => {
|
||||
const validUntil = new Date()
|
||||
return new Date(validUntil.setDate(new Date().getDate() + 14)).toISOString()
|
||||
@ -34,6 +38,7 @@ apolloQueryMock.mockResolvedValue({
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t, obj = null) => (obj ? [t, obj.date].join('; ') : t)),
|
||||
$d: jest.fn((d) => d.toISOString()),
|
||||
$store: {
|
||||
state: {
|
||||
token: null,
|
||||
@ -58,7 +63,7 @@ describe('TransactionLink', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(TransactionLink, { localVue, mocks })
|
||||
return mount(TransactionLink, { localVue, mocks, stubs })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
@ -232,8 +237,8 @@ describe('TransactionLink', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('has a link to transaction page', () => {
|
||||
expect(wrapper.find('a[to="/transactions"]').exists()).toBe(true)
|
||||
it.skip('has a link to transaction page', () => {
|
||||
expect(wrapper.find('a[target="/transactions"]').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@ export default {
|
||||
if (this.linkData.deletedAt) {
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
this.redeemedBoxText = this.$t('gdd_per_link.link-deleted', {
|
||||
date: this.linkData.deletedAt,
|
||||
date: this.$d(new Date(this.linkData.deletedAt), 'long'),
|
||||
})
|
||||
return `TEXT`
|
||||
}
|
||||
@ -108,7 +108,7 @@ export default {
|
||||
if (new Date(this.linkData.validUntil) < new Date()) {
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
this.redeemedBoxText = this.$t('gdd_per_link.link-expired', {
|
||||
date: this.linkData.validUntil,
|
||||
date: this.$d(new Date(this.linkData.validUntil), 'long'),
|
||||
})
|
||||
return `TEXT`
|
||||
}
|
||||
@ -117,7 +117,7 @@ export default {
|
||||
if (this.linkData.redeemedAt) {
|
||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||
this.redeemedBoxText = this.$t('gdd_per_link.redeemed-at', {
|
||||
date: this.linkData.redeemedAt,
|
||||
date: this.$d(new Date(this.linkData.redeemedAt), 'long'),
|
||||
})
|
||||
return `TEXT`
|
||||
}
|
||||
|
||||
@ -79,14 +79,6 @@ describe('Thx', () => {
|
||||
it('renders the thanks text', () => {
|
||||
expect(wrapper.find('p.h4').text()).toBe('site.thx.register')
|
||||
})
|
||||
|
||||
it('renders the thanks redirect button', () => {
|
||||
expect(wrapper.find('a.btn').text()).toBe('site.login.signin')
|
||||
})
|
||||
|
||||
it('links the redirect button to /login', () => {
|
||||
expect(wrapper.find('a.btn').attributes('href')).toBe('/login')
|
||||
})
|
||||
})
|
||||
|
||||
describe('coming from /login', () => {
|
||||
|
||||
@ -9,11 +9,11 @@
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
|
||||
<p class="h4">{{ $t(displaySetup.subtitle) }}</p>
|
||||
<hr />
|
||||
<b-button v-if="$route.params.code" :to="`/redeem/${$route.params.code}`">
|
||||
<b-button v-if="$route.params.code" :to="`/login/${$route.params.code}`">
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
|
||||
{{ $t(displaySetup.button) }}
|
||||
</b-button>
|
||||
<b-button v-else :to="displaySetup.linkTo">
|
||||
<b-button v-else-if="displaySetup.linkTo" :to="displaySetup.linkTo">
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
|
||||
{{ $t(displaySetup.button) }}
|
||||
</b-button>
|
||||
@ -41,7 +41,7 @@ const textFields = {
|
||||
headline: 'site.thx.title',
|
||||
subtitle: 'site.thx.register',
|
||||
button: 'site.login.signin',
|
||||
linkTo: '/login',
|
||||
// linkTo: '/login',
|
||||
},
|
||||
checkEmail: {
|
||||
headline: 'site.thx.title',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido",
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.1",
|
||||
"description": "Gradido",
|
||||
"main": "index.js",
|
||||
"repository": "git@github.com:gradido/gradido.git",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user