diff --git a/README.md b/README.md index 289a39109..3d086018e 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,19 @@ After generating a new version you should commit the changes. This will be the C Note: The Changelog will be regenerated with all tags on release on the external builder tool, but will not be checked in there. The Changelog on the github release will therefore always be correct, on the repo it might be incorrect due to missing tags when executing the `yarn release` command. +## How the different .env work on deploy + +Each component (frontend, admin, backend and database) has its own `.env` file. When running in development with docker and nginx you usually do not have to care about the `.env`. The defaults are set by the respective config file, found in the `src/config/` folder of each component. But if you have a local `.env`, the defaults set in the config are overwritten by the `.env`. If you do not use docker, you need the `.env` in the frontend and admin interface because nginx is not running in order to find the backend. + +Each component has a `.env.dist` file. This file contains all environment variables used by the component and can be used as pattern. If you want to use a local `.env`, copy the `.env.dist` and adjust the variables accordingly. + +Each component has a `.env.template` file. These files are very important on deploy. + +There is one `.env.dist` in the `deployment/bare_metal/` folder. This `.env.dist` contains all variables used by the components, e.g. unites all `.env.dist` from the components. On deploy, we copy this `.env.dist` to `.env` and set all variables in this new file. The deploy script loads this variables and provides them by the `.env.templates` of each component, creating an `.env` for each component (see in `deployment/bare_metal/start.sh` the `envsubst`). + +To avoid forgetting to update an existing `.env` in the `deployment/bare_metal/` folder when deploying, we have an environment version variable inside the codebase of each component. You should update this version, when environment variables must be changed or added on deploy. The code checks, that the environement version provided by the `.env` is the one expected by the codebase. + + ## Troubleshooting | Problem | Issue | Solution | Description | diff --git a/backend/.env.dist b/backend/.env.dist index 780e60e5a..ae89985e3 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -1,57 +1,60 @@ -CONFIG_VERSION=v8.2022-06-20 - -# Server -PORT=4000 -JWT_SECRET=secret123 -JWT_EXPIRES_IN=10m -GRAPHIQL=false -GDT_API_URL=https://gdt.gradido.net - -# Database -DB_HOST=localhost -DB_PORT=3306 -DB_USER=root -DB_PASSWORD= -DB_DATABASE=gradido_community -TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log - -# Klicktipp -KLICKTIPP=false -KLICKTTIPP_API_URL=https://api.klicktipp.com -KLICKTIPP_USER=gradido_test -KLICKTIPP_PASSWORD=secret321 -KLICKTIPP_APIKEY_DE=SomeFakeKeyDE -KLICKTIPP_APIKEY_EN=SomeFakeKeyEN - -# Community -COMMUNITY_NAME=Gradido Entwicklung -COMMUNITY_URL=http://localhost/ -COMMUNITY_REGISTER_URL=http://localhost/register -COMMUNITY_REDEEM_URL=http://localhost/redeem/{code} -COMMUNITY_REDEEM_CONTRIBUTION_URL=http://localhost/redeem/CL-{code} -COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido. - -# Login Server -LOGIN_APP_SECRET=21ffbbc616fe -LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a - -# EMail -EMAIL=false -EMAIL_USERNAME=gradido_email -EMAIL_SENDER=info@gradido.net -EMAIL_PASSWORD=xxx -EMAIL_SMTP_URL=gmail.com -EMAIL_SMTP_PORT=587 -EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin}{code} -EMAIL_LINK_SETPASSWORD=http://localhost/reset-password/{optin} -EMAIL_LINK_FORGOTPASSWORD=http://localhost/forgot-password -EMAIL_LINK_OVERVIEW=http://localhost/overview -EMAIL_CODE_VALID_TIME=1440 -EMAIL_CODE_REQUEST_TIME=10 - -# Webhook -WEBHOOK_ELOPAGE_SECRET=secret - -# SET LOG LEVEL AS NEEDED IN YOUR .ENV -# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal -# LOG_LEVEL=info +CONFIG_VERSION=v9.2022-07-07 + +# Server +PORT=4000 +JWT_SECRET=secret123 +JWT_EXPIRES_IN=10m +GRAPHIQL=false +GDT_API_URL=https://gdt.gradido.net + +# Database +DB_HOST=localhost +DB_PORT=3306 +DB_USER=root +DB_PASSWORD= +DB_DATABASE=gradido_community +TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log + +# Klicktipp +KLICKTIPP=false +KLICKTTIPP_API_URL=https://api.klicktipp.com +KLICKTIPP_USER=gradido_test +KLICKTIPP_PASSWORD=secret321 +KLICKTIPP_APIKEY_DE=SomeFakeKeyDE +KLICKTIPP_APIKEY_EN=SomeFakeKeyEN + +# Community +COMMUNITY_NAME=Gradido Entwicklung +COMMUNITY_URL=http://localhost/ +COMMUNITY_REGISTER_URL=http://localhost/register +COMMUNITY_REDEEM_URL=http://localhost/redeem/{code} +COMMUNITY_REDEEM_CONTRIBUTION_URL=http://localhost/redeem/CL-{code} +COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido. + +# Login Server +LOGIN_APP_SECRET=21ffbbc616fe +LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a + +# EMail +EMAIL=false +EMAIL_USERNAME=gradido_email +EMAIL_SENDER=info@gradido.net +EMAIL_PASSWORD=xxx +EMAIL_SMTP_URL=gmail.com +EMAIL_SMTP_PORT=587 +EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin}{code} +EMAIL_LINK_SETPASSWORD=http://localhost/reset-password/{optin} +EMAIL_LINK_FORGOTPASSWORD=http://localhost/forgot-password +EMAIL_LINK_OVERVIEW=http://localhost/overview +EMAIL_CODE_VALID_TIME=1440 +EMAIL_CODE_REQUEST_TIME=10 + +# Webhook +WEBHOOK_ELOPAGE_SECRET=secret + +# EventProtocol +EVENT_PROTOCOL_DISABLED=false + +# SET LOG LEVEL AS NEEDED IN YOUR .ENV +# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal +# LOG_LEVEL=info diff --git a/backend/.env.template b/backend/.env.template index 94bf41550..beaa256ef 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -50,3 +50,6 @@ EMAIL_CODE_REQUEST_TIME=$EMAIL_CODE_REQUEST_TIME # Webhook WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET + +# EventProtocol +EVENT_PROTOCOL_DISABLED=$EVENT_PROTOCOL_DISABLED diff --git a/backend/README.md b/backend/README.md index e74750c46..b27ab16d9 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,16 +1,18 @@ # backend ## Project setup -``` + +```bash yarn install ``` ## Seed DB -``` + +```bash yarn seed ``` -Deletes all data in database. Then seeds data in database. +Deletes all data in database. Then seeds data in database. ## Seeded Users @@ -22,3 +24,47 @@ Deletes all data in database. Then seeds data in database. | bob@baumeister.de | `Aa12345_` | `false` | `true` | `false` | | garrick@ollivander.com | | `false` | `false` | `false` | | stephen@hawking.uk | `Aa12345_` | `false` | `true` | `true` | + +## Setup GraphQL Playground + +### Setup In The Code + +Setting up the GraphQL Playground in our code requires the following steps: + +- Create an empty `.env` file in the `backend` folder and set "GRAPHIQL=true" there. +- Start or restart Docker Compose. +- For verification, Docker should display `GraphQL available at http://localhost:4000` in the terminal. +- If you open "http://localhost:4000/" in your browser, you should see the GraphQL Playground. + +### Authentication + +You need to authenticate yourself in GraphQL Playground to be able to send queries and mutations, to do so follow the steps below: + +- in Firefox go to "Network Analysis" and delete all entries +- enter and send the login query: + +```gql +{ + login(email: "bibi@bloxberg.de", password:"Aa12345_") { + id + publisherId + email + firstName + lastName + emailChecked + language + hasElopage + } +} +``` + +- search in Firefox under „Network Analysis" for the smallest size of a header and copy the value of the token +- open the header section in GraphQL Playground and set your current token by filling in and replacing `XXX`: + +```qgl +{ + "Authorization": "XXX" +} +``` + +Now you can open a new tap in the Playground and enter your query or mutation there. diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index c10fc96de..639326a64 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -26,6 +26,9 @@ export enum RIGHTS { LIST_TRANSACTION_LINKS = 'LIST_TRANSACTION_LINKS', GDT_BALANCE = 'GDT_BALANCE', CREATE_CONTRIBUTION = 'CREATE_CONTRIBUTION', + LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS', + LIST_ALL_CONTRIBUTIONS = 'LIST_ALL_CONTRIBUTIONS', + UPDATE_CONTRIBUTION = 'UPDATE_CONTRIBUTION', // Admin SEARCH_USERS = 'SEARCH_USERS', SET_USER_ROLE = 'SET_USER_ROLE', diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index 2d9ac2deb..935bd94e5 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -24,6 +24,9 @@ export const ROLE_USER = new Role('user', [ RIGHTS.LIST_TRANSACTION_LINKS, RIGHTS.GDT_BALANCE, RIGHTS.CREATE_CONTRIBUTION, + RIGHTS.LIST_CONTRIBUTIONS, + RIGHTS.LIST_ALL_CONTRIBUTIONS, + RIGHTS.UPDATE_CONTRIBUTION, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index aa4197a43..2120fce71 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,123 +1,129 @@ -// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) - -import dotenv from 'dotenv' -import Decimal from 'decimal.js-light' -dotenv.config() - -Decimal.set({ - precision: 25, - rounding: Decimal.ROUND_HALF_UP, -}) - -const constants = { - DB_VERSION: '0040-add_contribution_link_id_to_user', - DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 - LOG4JS_CONFIG: 'log4js-config.json', - // default log level on production should be info - LOG_LEVEL: process.env.LOG_LEVEL || 'info', - CONFIG_VERSION: { - DEFAULT: 'DEFAULT', - EXPECTED: 'v8.2022-06-20', - CURRENT: '', - }, -} - -const server = { - PORT: process.env.PORT || 4000, - JWT_SECRET: process.env.JWT_SECRET || 'secret123', - JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m', - GRAPHIQL: process.env.GRAPHIQL === 'true' || false, - GDT_API_URL: process.env.GDT_API_URL || 'https://gdt.gradido.net', - PRODUCTION: process.env.NODE_ENV === 'production' || false, -} - -const database = { - DB_HOST: process.env.DB_HOST || 'localhost', - DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306, - DB_USER: process.env.DB_USER || 'root', - DB_PASSWORD: process.env.DB_PASSWORD || '', - DB_DATABASE: process.env.DB_DATABASE || 'gradido_community', - TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.backend.log', -} - -const klicktipp = { - KLICKTIPP: process.env.KLICKTIPP === 'true' || false, - KLICKTTIPP_API_URL: process.env.KLICKTIPP_API_URL || 'https://api.klicktipp.com', - KLICKTIPP_USER: process.env.KLICKTIPP_USER || 'gradido_test', - KLICKTIPP_PASSWORD: process.env.KLICKTIPP_PASSWORD || 'secret321', - KLICKTIPP_APIKEY_DE: process.env.KLICKTIPP_APIKEY_DE || 'SomeFakeKeyDE', - KLICKTIPP_APIKEY_EN: process.env.KLICKTIPP_APIKEY_EN || 'SomeFakeKeyEN', -} - -const community = { - COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung', - COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/', - COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register', - COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/{code}', - COMMUNITY_REDEEM_CONTRIBUTION_URL: - process.env.COMMUNITY_REDEEM_CONTRIBUTION_URL || 'http://localhost/redeem/CL-{code}', - COMMUNITY_DESCRIPTION: - process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.', -} - -const loginServer = { - LOGIN_APP_SECRET: process.env.LOGIN_APP_SECRET || '21ffbbc616fe', - LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a', -} - -const email = { - EMAIL: process.env.EMAIL === 'true' || false, - EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email', - EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net', - EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx', - EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com', - EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', - EMAIL_LINK_VERIFICATION: - process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}', - EMAIL_LINK_SETPASSWORD: - process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{optin}', - EMAIL_LINK_FORGOTPASSWORD: - process.env.EMAIL_LINK_FORGOTPASSWORD || 'http://localhost/forgot-password', - EMAIL_LINK_OVERVIEW: process.env.EMAIL_LINK_OVERVIEW || 'http://localhost/overview', - // time in minutes a optin code is valid - EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME - ? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 1440 - : 1440, - // time in minutes that must pass to request a new optin code - EMAIL_CODE_REQUEST_TIME: process.env.EMAIL_CODE_REQUEST_TIME - ? parseInt(process.env.EMAIL_CODE_REQUEST_TIME) || 10 - : 10, -} - -const webhook = { - // Elopage - WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret', -} - -// This is needed by graphql-directive-auth -process.env.APP_SECRET = server.JWT_SECRET - -// Check config version -constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT -if ( - ![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes( - constants.CONFIG_VERSION.CURRENT, - ) -) { - throw new Error( - `Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`, - ) -} - -const CONFIG = { - ...constants, - ...server, - ...database, - ...klicktipp, - ...community, - ...email, - ...loginServer, - ...webhook, -} - -export default CONFIG +// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) + +import dotenv from 'dotenv' +import Decimal from 'decimal.js-light' +dotenv.config() + +Decimal.set({ + precision: 25, + rounding: Decimal.ROUND_HALF_UP, +}) + +const constants = { + DB_VERSION: '0043-add_event_protocol_table', + DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 + LOG4JS_CONFIG: 'log4js-config.json', + // default log level on production should be info + LOG_LEVEL: process.env.LOG_LEVEL || 'info', + CONFIG_VERSION: { + DEFAULT: 'DEFAULT', + EXPECTED: 'v9.2022-07-07', + CURRENT: '', + }, +} + +const server = { + PORT: process.env.PORT || 4000, + JWT_SECRET: process.env.JWT_SECRET || 'secret123', + JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m', + GRAPHIQL: process.env.GRAPHIQL === 'true' || false, + GDT_API_URL: process.env.GDT_API_URL || 'https://gdt.gradido.net', + PRODUCTION: process.env.NODE_ENV === 'production' || false, +} + +const database = { + DB_HOST: process.env.DB_HOST || 'localhost', + DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306, + DB_USER: process.env.DB_USER || 'root', + DB_PASSWORD: process.env.DB_PASSWORD || '', + DB_DATABASE: process.env.DB_DATABASE || 'gradido_community', + TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.backend.log', +} + +const klicktipp = { + KLICKTIPP: process.env.KLICKTIPP === 'true' || false, + KLICKTTIPP_API_URL: process.env.KLICKTIPP_API_URL || 'https://api.klicktipp.com', + KLICKTIPP_USER: process.env.KLICKTIPP_USER || 'gradido_test', + KLICKTIPP_PASSWORD: process.env.KLICKTIPP_PASSWORD || 'secret321', + KLICKTIPP_APIKEY_DE: process.env.KLICKTIPP_APIKEY_DE || 'SomeFakeKeyDE', + KLICKTIPP_APIKEY_EN: process.env.KLICKTIPP_APIKEY_EN || 'SomeFakeKeyEN', +} + +const community = { + COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung', + COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/', + COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register', + COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/{code}', + COMMUNITY_REDEEM_CONTRIBUTION_URL: + process.env.COMMUNITY_REDEEM_CONTRIBUTION_URL || 'http://localhost/redeem/CL-{code}', + COMMUNITY_DESCRIPTION: + process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.', +} + +const loginServer = { + LOGIN_APP_SECRET: process.env.LOGIN_APP_SECRET || '21ffbbc616fe', + LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a', +} + +const email = { + EMAIL: process.env.EMAIL === 'true' || false, + EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email', + EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net', + EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx', + EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com', + EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', + EMAIL_LINK_VERIFICATION: + process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}', + EMAIL_LINK_SETPASSWORD: + process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{optin}', + EMAIL_LINK_FORGOTPASSWORD: + process.env.EMAIL_LINK_FORGOTPASSWORD || 'http://localhost/forgot-password', + EMAIL_LINK_OVERVIEW: process.env.EMAIL_LINK_OVERVIEW || 'http://localhost/overview', + // time in minutes a optin code is valid + EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME + ? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 1440 + : 1440, + // time in minutes that must pass to request a new optin code + EMAIL_CODE_REQUEST_TIME: process.env.EMAIL_CODE_REQUEST_TIME + ? parseInt(process.env.EMAIL_CODE_REQUEST_TIME) || 10 + : 10, +} + +const webhook = { + // Elopage + WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret', +} + +const eventProtocol = { + // global switch to enable writing of EventProtocol-Entries + EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false, +} + +// This is needed by graphql-directive-auth +process.env.APP_SECRET = server.JWT_SECRET + +// Check config version +constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT +if ( + ![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes( + constants.CONFIG_VERSION.CURRENT, + ) +) { + throw new Error( + `Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`, + ) +} + +const CONFIG = { + ...constants, + ...server, + ...database, + ...klicktipp, + ...community, + ...email, + ...loginServer, + ...webhook, + ...eventProtocol, +} + +export default CONFIG diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts new file mode 100644 index 000000000..6f07661f1 --- /dev/null +++ b/backend/src/event/Event.ts @@ -0,0 +1,301 @@ +import { EventProtocol } from '@entity/EventProtocol' +import decimal from 'decimal.js-light' +import { EventProtocolType } from './EventProtocolType' + +export class EventBasic { + type: string + createdAt: Date +} +export class EventBasicUserId extends EventBasic { + userId: number +} + +export class EventBasicTx extends EventBasicUserId { + xUserId: number + xCommunityId: number + transactionId: number + amount: decimal +} + +export class EventBasicCt extends EventBasicUserId { + contributionId: number + amount: decimal +} + +export class EventBasicRedeem extends EventBasicUserId { + transactionId?: number + contributionId?: number +} + +export class EventVisitGradido extends EventBasic {} +export class EventRegister extends EventBasicUserId {} +export class EventRedeemRegister extends EventBasicRedeem {} +export class EventInactiveAccount extends EventBasicUserId {} +export class EventSendConfirmationEmail extends EventBasicUserId {} +export class EventConfirmationEmail extends EventBasicUserId {} +export class EventRegisterEmailKlicktipp extends EventBasicUserId {} +export class EventLogin extends EventBasicUserId {} +export class EventRedeemLogin extends EventBasicRedeem {} +export class EventActivateAccount extends EventBasicUserId {} +export class EventPasswordChange extends EventBasicUserId {} +export class EventTransactionSend extends EventBasicTx {} +export class EventTransactionSendRedeem extends EventBasicTx {} +export class EventTransactionRepeateRedeem extends EventBasicTx {} +export class EventTransactionCreation extends EventBasicUserId { + transactionId: number + amount: decimal +} +export class EventTransactionReceive extends EventBasicTx {} +export class EventTransactionReceiveRedeem extends EventBasicTx {} +export class EventContributionCreate extends EventBasicCt {} +export class EventContributionConfirm extends EventBasicCt { + xUserId: number + xCommunityId: number +} +export class EventContributionLinkDefine extends EventBasicCt {} +export class EventContributionLinkActivateRedeem extends EventBasicCt {} + +export class Event { + constructor() + constructor(event?: EventProtocol) { + if (event) { + this.id = event.id + this.type = event.type + this.createdAt = event.createdAt + this.userId = event.userId + this.xUserId = event.xUserId + this.xCommunityId = event.xCommunityId + this.transactionId = event.transactionId + this.contributionId = event.contributionId + this.amount = event.amount + } + } + + public setEventBasic(): Event { + this.type = EventProtocolType.BASIC + this.createdAt = new Date() + + return this + } + + public setEventVisitGradido(): Event { + this.setEventBasic() + this.type = EventProtocolType.VISIT_GRADIDO + + return this + } + + public setEventRegister(ev: EventRegister): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.REGISTER + + return this + } + + public setEventRedeemRegister(ev: EventRedeemRegister): Event { + this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) + this.type = EventProtocolType.REDEEM_REGISTER + + return this + } + + public setEventInactiveAccount(ev: EventInactiveAccount): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.INACTIVE_ACCOUNT + + return this + } + + public setEventSendConfirmationEmail(ev: EventSendConfirmationEmail): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.SEND_CONFIRMATION_EMAIL + + return this + } + + public setEventConfirmationEmail(ev: EventConfirmationEmail): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.CONFIRM_EMAIL + + return this + } + + public setEventRegisterEmailKlicktipp(ev: EventRegisterEmailKlicktipp): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.REGISTER_EMAIL_KLICKTIPP + + return this + } + + public setEventLogin(ev: EventLogin): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.LOGIN + + return this + } + + public setEventRedeemLogin(ev: EventRedeemLogin): Event { + this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) + this.type = EventProtocolType.REDEEM_LOGIN + + return this + } + + public setEventActivateAccount(ev: EventActivateAccount): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.ACTIVATE_ACCOUNT + + return this + } + + public setEventPasswordChange(ev: EventPasswordChange): Event { + this.setByBasicUser(ev.userId) + this.type = EventProtocolType.PASSWORD_CHANGE + + return this + } + + public setEventTransactionSend(ev: EventTransactionSend): Event { + this.setByBasicTx(ev.userId, ev.xUserId, ev.xCommunityId, ev.transactionId, ev.amount) + this.type = EventProtocolType.TRANSACTION_SEND + + return this + } + + public setEventTransactionSendRedeem(ev: EventTransactionSendRedeem): Event { + this.setByBasicTx(ev.userId, ev.xUserId, ev.xCommunityId, ev.transactionId, ev.amount) + this.type = EventProtocolType.TRANSACTION_SEND_REDEEM + + return this + } + + public setEventTransactionRepeateRedeem(ev: EventTransactionRepeateRedeem): Event { + this.setByBasicTx(ev.userId, ev.xUserId, ev.xCommunityId, ev.transactionId, ev.amount) + this.type = EventProtocolType.TRANSACTION_REPEATE_REDEEM + + return this + } + + public setEventTransactionCreation(ev: EventTransactionCreation): Event { + this.setByBasicUser(ev.userId) + if (ev.transactionId) this.transactionId = ev.transactionId + if (ev.amount) this.amount = ev.amount + this.type = EventProtocolType.TRANSACTION_CREATION + + return this + } + + public setEventTransactionReceive(ev: EventTransactionReceive): Event { + this.setByBasicTx(ev.userId, ev.xUserId, ev.xCommunityId, ev.transactionId, ev.amount) + this.type = EventProtocolType.TRANSACTION_RECEIVE + + return this + } + + public setEventTransactionReceiveRedeem(ev: EventTransactionReceiveRedeem): Event { + this.setByBasicTx(ev.userId, ev.xUserId, ev.xCommunityId, ev.transactionId, ev.amount) + this.type = EventProtocolType.TRANSACTION_RECEIVE_REDEEM + + return this + } + + public setEventContributionCreate(ev: EventContributionCreate): Event { + this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) + this.type = EventProtocolType.CONTRIBUTION_CREATE + + return this + } + + public setEventContributionConfirm(ev: EventContributionConfirm): Event { + this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) + if (ev.xUserId) this.xUserId = ev.xUserId + if (ev.xCommunityId) this.xCommunityId = ev.xCommunityId + this.type = EventProtocolType.CONTRIBUTION_CONFIRM + + return this + } + + public setEventContributionLinkDefine(ev: EventContributionLinkDefine): Event { + this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) + this.type = EventProtocolType.CONTRIBUTION_LINK_DEFINE + + return this + } + + public setEventContributionLinkActivateRedeem(ev: EventContributionLinkActivateRedeem): Event { + this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) + this.type = EventProtocolType.CONTRIBUTION_LINK_ACTIVATE_REDEEM + + return this + } + + setByBasicUser(userId: number): Event { + this.setEventBasic() + this.userId = userId + + return this + } + + setByBasicTx( + userId: number, + xUserId?: number, + xCommunityId?: number, + transactionId?: number, + amount?: decimal, + ): Event { + this.setByBasicUser(userId) + if (xUserId) this.xUserId = xUserId + if (xCommunityId) this.xCommunityId = xCommunityId + if (transactionId) this.transactionId = transactionId + if (amount) this.amount = amount + + return this + } + + setByBasicCt(userId: number, contributionId: number, amount?: decimal): Event { + this.setByBasicUser(userId) + if (contributionId) this.contributionId = contributionId + if (amount) this.amount = amount + + return this + } + + setByBasicRedeem(userId: number, transactionId?: number, contributionId?: number): Event { + this.setByBasicUser(userId) + if (transactionId) this.transactionId = transactionId + if (contributionId) this.contributionId = contributionId + + return this + } + + setByEventTransactionCreation(event: EventTransactionCreation): Event { + this.type = event.type + this.createdAt = event.createdAt + this.userId = event.userId + this.transactionId = event.transactionId + this.amount = event.amount + + return this + } + + setByEventContributionConfirm(event: EventContributionConfirm): Event { + this.type = event.type + this.createdAt = event.createdAt + this.userId = event.userId + this.xUserId = event.xUserId + this.xCommunityId = event.xCommunityId + this.amount = event.amount + + return this + } + + id: number + type: string + createdAt: Date + userId: number + xUserId?: number + xCommunityId?: number + transactionId?: number + contributionId?: number + amount?: decimal +} diff --git a/backend/src/event/EventProtocolEmitter.ts b/backend/src/event/EventProtocolEmitter.ts new file mode 100644 index 000000000..6dea99810 --- /dev/null +++ b/backend/src/event/EventProtocolEmitter.ts @@ -0,0 +1,39 @@ +import { Event } from '@/event/Event' +import { backendLogger as logger } from '@/server/logger' +import { EventProtocol } from '@entity/EventProtocol' +import CONFIG from '@/config' + +class EventProtocolEmitter { + /* }extends EventEmitter { */ + private events: Event[] + + public addEvent(event: Event) { + this.events.push(event) + } + + public getEvents(): Event[] { + return this.events + } + + public isDisabled() { + logger.info(`EventProtocol - isDisabled=${CONFIG.EVENT_PROTOCOL_DISABLED}`) + return CONFIG.EVENT_PROTOCOL_DISABLED === true + } + + public async writeEvent(event: Event): Promise { + if (!eventProtocol.isDisabled()) { + logger.info(`writeEvent(${JSON.stringify(event)})`) + const dbEvent = new EventProtocol() + dbEvent.type = event.type + dbEvent.createdAt = event.createdAt + dbEvent.userId = event.userId + if (event.xUserId) dbEvent.xUserId = event.xUserId + if (event.xCommunityId) dbEvent.xCommunityId = event.xCommunityId + if (event.contributionId) dbEvent.contributionId = event.contributionId + if (event.transactionId) dbEvent.transactionId = event.transactionId + if (event.amount) dbEvent.amount = event.amount + await dbEvent.save() + } + } +} +export const eventProtocol = new EventProtocolEmitter() diff --git a/backend/src/event/EventProtocolType.ts b/backend/src/event/EventProtocolType.ts new file mode 100644 index 000000000..0f61f787a --- /dev/null +++ b/backend/src/event/EventProtocolType.ts @@ -0,0 +1,24 @@ +export enum EventProtocolType { + BASIC = 'BASIC', + VISIT_GRADIDO = 'VISIT_GRADIDO', + REGISTER = 'REGISTER', + REDEEM_REGISTER = 'REDEEM_REGISTER', + INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', + SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL', + CONFIRM_EMAIL = 'CONFIRM_EMAIL', + REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', + LOGIN = 'LOGIN', + REDEEM_LOGIN = 'REDEEM_LOGIN', + ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT', + PASSWORD_CHANGE = 'PASSWORD_CHANGE', + TRANSACTION_SEND = 'TRANSACTION_SEND', + TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM', + TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM', + TRANSACTION_CREATION = 'TRANSACTION_CREATION', + TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE', + TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM', + CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE', + CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM', + CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE', + CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM', +} diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts new file mode 100644 index 000000000..13c2d40d7 --- /dev/null +++ b/backend/src/graphql/model/Contribution.ts @@ -0,0 +1,60 @@ +import { ObjectType, Field, Int } from 'type-graphql' +import Decimal from 'decimal.js-light' +import { Contribution as dbContribution } from '@entity/Contribution' +import { User } from './User' + +@ObjectType() +export class Contribution { + constructor(contribution: dbContribution, user: User) { + this.id = contribution.id + this.firstName = user ? user.firstName : null + this.lastName = user ? user.lastName : null + this.amount = contribution.amount + this.memo = contribution.memo + this.createdAt = contribution.createdAt + this.deletedAt = contribution.deletedAt + this.confirmedAt = contribution.confirmedAt + this.confirmedBy = contribution.confirmedBy + } + + @Field(() => Number) + id: number + + @Field(() => String, { nullable: true }) + firstName: string | null + + @Field(() => String, { nullable: true }) + lastName: string | null + + @Field(() => Decimal) + amount: Decimal + + @Field(() => String) + memo: string + + @Field(() => Date) + createdAt: Date + + @Field(() => Date, { nullable: true }) + deletedAt: Date | null + + @Field(() => Date, { nullable: true }) + confirmedAt: Date | null + + @Field(() => Number, { nullable: true }) + confirmedBy: number | null +} + +@ObjectType() +export class ContributionListResult { + constructor(count: number, list: Contribution[]) { + this.contributionCount = count + this.contributionList = list + } + + @Field(() => Int) + contributionCount: number + + @Field(() => [Contribution]) + contributionList: Contribution[] +} diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 86c56312f..0642be630 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -1,10 +1,12 @@ import { ObjectType, Field } from 'type-graphql' import { KlickTipp } from './KlickTipp' import { User as dbUser } from '@entity/User' +import Decimal from 'decimal.js-light' +import { FULL_CREATION_AVAILABLE } from '../resolver/const/const' @ObjectType() export class User { - constructor(user: dbUser) { + constructor(user: dbUser, creation: Decimal[] = FULL_CREATION_AVAILABLE) { this.id = user.id this.email = user.email this.firstName = user.firstName @@ -17,6 +19,7 @@ export class User { this.isAdmin = user.isAdmin this.klickTipp = null this.hasElopage = null + this.creation = creation } @Field(() => Number) @@ -64,4 +67,7 @@ export class User { @Field(() => Boolean, { nullable: true }) hasElopage: boolean | null + + @Field(() => [Decimal]) + creation: Decimal[] } diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 75175edc2..12cad529c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -47,11 +47,11 @@ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver' import CONFIG from '@/config' import { - getCreationIndex, getUserCreation, getUserCreations, validateContribution, isStartEndDateValid, + updateCreations, } from './util/creations' import { CONTRIBUTIONLINK_MEMO_MAX_CHARS, @@ -321,6 +321,10 @@ export class AdminResolver { throw new Error('user of the pending contribution and send user does not correspond') } + if (contributionToUpdate.moderatorId === null) { + throw new Error('An admin is not allowed to update a user contribution.') + } + const creationDateObj = new Date(creationDate) let creations = await getUserCreation(user.id) if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) { @@ -688,13 +692,3 @@ export class AdminResolver { return new ContributionLink(dbContributionLink) } } - -function updateCreations(creations: Decimal[], contribution: Contribution): Decimal[] { - const index = getCreationIndex(contribution.contributionDate.getMonth()) - - if (index < 0) { - throw new Error('You cannot create GDD for a month older than the last three months.') - } - creations[index] = creations[index].plus(contribution.amount.toString()) - return creations -} diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 01e9b123e..37ff45f25 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2,14 +2,22 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -import { createContribution } from '@/seeds/graphql/mutations' -import { login } from '@/seeds/graphql/queries' +import { + adminUpdateContribution, + createContribution, + updateContribution, +} from '@/seeds/graphql/mutations' +import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' import { userFactory } from '@/seeds/factory/user' +import { creationFactory } from '@/seeds/factory/creation' +import { creations } from '@/seeds/creation/index' +import { peterLustig } from '@/seeds/users/peter-lustig' let mutate: any, query: any, con: any let testEnv: any +let result: any beforeAll(async () => { testEnv = await testEnvironment() @@ -111,6 +119,7 @@ describe('ContributionResolver', () => { expect.objectContaining({ data: { createContribution: { + id: expect.any(Number), amount: '100', memo: 'Test env contribution', }, @@ -121,4 +130,396 @@ describe('ContributionResolver', () => { }) }) }) + + describe('listContributions', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + query({ + query: listContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await creationFactory(testEnv, bibisCreation!) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('filter confirmed is false', () => { + it('returns creations', async () => { + await expect( + query({ + query: listContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listContributions: { + contributionCount: 2, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + }) + + describe('filter confirmed is true', () => { + it('returns only unconfirmed creations', async () => { + await expect( + query({ + query: listContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: true, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listContributions: { + contributionCount: 1, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + }) + }) + }) + + describe('updateContribution', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: 1, + amount: 100.0, + memo: 'Test Contribution', + creationDate: 'not-valid', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, peterLustig) + await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + result = await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('wrong contribution id', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: -1, + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No contribution found to given id.')], + }), + ) + }) + }) + + describe('wrong user tries to update the contribution', () => { + beforeAll(async () => { + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + it('throws an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 10.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'user of the pending contribution and send user does not correspond', + ), + ], + }), + ) + }) + }) + + describe('admin tries to update a user contribution', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: adminUpdateContribution, + variables: { + id: result.data.createContribution.id, + email: 'bibi@bloxberg.de', + amount: 10.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('An admin is not allowed to update a user contribution.')], + }), + ) + }) + }) + + describe('update too much so that the limit is exceeded', () => { + beforeAll(async () => { + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + it('throws an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 1019.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'The amount (1019 GDD) to be created exceeds the amount (1000 GDD) still available for this month.', + ), + ], + }), + ) + }) + }) + + describe('update creation to a date that is older than 3 months', () => { + it('throws an error', async () => { + const date = new Date() + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 10.0, + memo: 'Test env contribution', + creationDate: date.setMonth(date.getMonth() - 3).toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError('No information for available creations for the given date'), + ], + }), + ) + }) + }) + + describe('valid input', () => { + it('updates contribution', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 10.0, + memo: 'Test contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + updateContribution: { + id: result.data.createContribution.id, + amount: '10', + memo: 'Test contribution', + }, + }, + }), + ) + }) + }) + }) + }) + + describe('listAllContribution', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await creationFactory(testEnv, bibisCreation!) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + }) + + it('returns allCreation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + contributionCount: 2, + contributionList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) + }) + }) }) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 98492b510..f21c65068 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -1,11 +1,16 @@ import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { backendLogger as logger } from '@/server/logger' -import { Contribution } from '@entity/Contribution' -import { Args, Authorized, Ctx, Mutation, Resolver } from 'type-graphql' -import ContributionArgs from '../arg/ContributionArgs' -import { UnconfirmedContribution } from '../model/UnconfirmedContribution' -import { validateContribution, getUserCreation } from './util/creations' +import { Contribution as dbContribution } from '@entity/Contribution' +import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' +import { FindOperator, IsNull } from '@dbTools/typeorm' +import ContributionArgs from '@arg/ContributionArgs' +import Paginated from '@arg/Paginated' +import { Order } from '@enum/Order' +import { Contribution, ContributionListResult } from '@model/Contribution' +import { UnconfirmedContribution } from '@model/UnconfirmedContribution' +import { User } from '@model/User' +import { validateContribution, getUserCreation, updateCreations } from './util/creations' @Resolver() export class ContributionResolver { @@ -21,7 +26,7 @@ export class ContributionResolver { const creationDateObj = new Date(creationDate) validateContribution(creations, amount, creationDateObj) - const contribution = Contribution.create() + const contribution = dbContribution.create() contribution.userId = user.id contribution.amount = amount contribution.createdAt = new Date() @@ -29,7 +34,95 @@ export class ContributionResolver { contribution.memo = memo logger.trace('contribution to save', contribution) - await Contribution.save(contribution) + await dbContribution.save(contribution) return new UnconfirmedContribution(contribution, user, creations) } + + @Authorized([RIGHTS.LIST_CONTRIBUTIONS]) + @Query(() => ContributionListResult) + async listContributions( + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + @Arg('filterConfirmed', () => Boolean) + filterConfirmed: boolean | null, + @Ctx() context: Context, + ): Promise { + const user = getUser(context) + const where: { + userId: number + confirmedBy?: FindOperator | null + } = { userId: user.id } + if (filterConfirmed) where.confirmedBy = IsNull() + const [contributions, count] = await dbContribution.findAndCount({ + where, + order: { + createdAt: order, + }, + withDeleted: true, + skip: (currentPage - 1) * pageSize, + take: pageSize, + }) + return new ContributionListResult( + count, + contributions.map((contribution) => new Contribution(contribution, new User(user))), + ) + } + + @Authorized([RIGHTS.LIST_ALL_CONTRIBUTIONS]) + @Query(() => ContributionListResult) + async listAllContributions( + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + ): Promise { + const [dbContributions, count] = await dbContribution.findAndCount({ + relations: ['user'], + order: { + createdAt: order, + }, + skip: (currentPage - 1) * pageSize, + take: pageSize, + }) + return new ContributionListResult( + count, + dbContributions.map( + (contribution) => new Contribution(contribution, new User(contribution.user)), + ), + ) + } + + @Authorized([RIGHTS.UPDATE_CONTRIBUTION]) + @Mutation(() => UnconfirmedContribution) + async updateContribution( + @Arg('contributionId', () => Int) + contributionId: number, + @Args() { amount, memo, creationDate }: ContributionArgs, + @Ctx() context: Context, + ): Promise { + const user = getUser(context) + + const contributionToUpdate = await dbContribution.findOne({ + where: { id: contributionId, confirmedAt: IsNull() }, + }) + if (!contributionToUpdate) { + throw new Error('No contribution found to given id.') + } + if (contributionToUpdate.userId !== user.id) { + throw new Error('user of the pending contribution and send user does not correspond') + } + + const creationDateObj = new Date(creationDate) + let creations = await getUserCreation(user.id) + if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) { + creations = updateCreations(creations, contributionToUpdate) + } + + // all possible cases not to be true are thrown in this function + validateContribution(creations, amount, creationDateObj) + contributionToUpdate.amount = amount + contributionToUpdate.memo = memo + contributionToUpdate.contributionDate = new Date(creationDate) + dbContribution.save(contributionToUpdate) + + return new UnconfirmedContribution(contributionToUpdate, user, creations) + } } diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 0bde22ae6..a89a8cb0b 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -23,6 +23,15 @@ import { sendAccountMultiRegistrationEmail } from '@/mailer/sendAccountMultiRegi import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' +import { eventProtocol } from '@/event/EventProtocolEmitter' +import { + Event, + EventLogin, + EventRedeemRegister, + EventRegister, + EventSendConfirmationEmail, +} from '@/event/Event' +import { getUserCreation } from './util/creations' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -224,7 +233,7 @@ export class UserResolver { logger.info('verifyLogin...') // TODO refactor and do not have duplicate code with login(see below) const userEntity = getUser(context) - const user = new User(userEntity) + const user = new User(userEntity, await getUserCreation(userEntity.id)) // user.pubkey = userEntity.pubKey.toString('hex') // Elopage Status & Stored PublisherId user.hasElopage = await this.hasElopage(context) @@ -274,7 +283,7 @@ export class UserResolver { logger.addContext('user', dbUser.id) logger.debug('login credentials valid...') - const user = new User(dbUser) + const user = new User(dbUser, await getUserCreation(dbUser.id)) logger.debug('user=' + user) // Elopage Status & Stored PublisherId @@ -290,6 +299,9 @@ export class UserResolver { key: 'token', value: encode(dbUser.pubKey), }) + const ev = new EventLogin() + ev.userId = user.id + eventProtocol.writeEvent(new Event().setEventLogin(ev)) logger.info('successful Login:' + user) return user } @@ -367,6 +379,9 @@ export class UserResolver { // const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) const emailHash = getEmailHash(email) + const eventRegister = new EventRegister() + const eventRedeemRegister = new EventRedeemRegister() + const eventSendConfirmEmail = new EventSendConfirmationEmail() const dbUser = new DbUser() dbUser.email = email dbUser.firstName = firstName @@ -384,12 +399,14 @@ export class UserResolver { logger.info('redeemCode found contributionLink=' + contributionLink) if (contributionLink) { dbUser.contributionLinkId = contributionLink.id + eventRedeemRegister.contributionId = contributionLink.id } } else { const transactionLink = await dbTransactionLink.findOne({ code: redeemCode }) logger.info('redeemCode found transactionLink=' + transactionLink) if (transactionLink) { dbUser.referrerId = transactionLink.userId + eventRedeemRegister.transactionId = transactionLink.id } } } @@ -400,6 +417,7 @@ export class UserResolver { // loginUser.pubKey = keyPair[0] // loginUser.privKey = encryptedPrivkey + const event = new Event() const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') @@ -429,6 +447,9 @@ export class UserResolver { duration: printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME), }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) + eventSendConfirmEmail.userId = dbUser.id + eventProtocol.writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail)) + /* uncomment this, when you need the activation link on the console */ // In case EMails are disabled log the activation link for the user if (!emailSent) { @@ -445,6 +466,14 @@ export class UserResolver { } logger.info('createUser() successful...') + if (redeemCode) { + eventRedeemRegister.userId = dbUser.id + eventProtocol.writeEvent(event.setEventRedeemRegister(eventRedeemRegister)) + } else { + eventRegister.userId = dbUser.id + eventProtocol.writeEvent(event.setEventRegister(eventRegister)) + } + return new User(dbUser) } diff --git a/backend/src/graphql/resolver/util/creations.ts b/backend/src/graphql/resolver/util/creations.ts index dcdce2bfa..ad15ebec6 100644 --- a/backend/src/graphql/resolver/util/creations.ts +++ b/backend/src/graphql/resolver/util/creations.ts @@ -1,6 +1,7 @@ import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId' import { backendLogger as logger } from '@/server/logger' import { getConnection } from '@dbTools/typeorm' +import { Contribution } from '@entity/Contribution' import Decimal from 'decimal.js-light' import { FULL_CREATION_AVAILABLE, MAX_CREATION_AMOUNT } from '../const/const' @@ -117,3 +118,13 @@ export const isStartEndDateValid = ( throw new Error(`The value of validFrom must before or equals the validTo!`) } } + +export const updateCreations = (creations: Decimal[], contribution: Contribution): Decimal[] => { + const index = getCreationIndex(contribution.contributionDate.getMonth()) + + if (index < 0) { + throw new Error('You cannot create GDD for a month older than the last three months.') + } + creations[index] = creations[index].plus(contribution.amount.toString()) + return creations +} diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 4926f706f..4e7fa8a90 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -234,6 +234,22 @@ export const deleteContributionLink = gql` export const createContribution = gql` mutation ($amount: Decimal!, $memo: String!, $creationDate: String!) { createContribution(amount: $amount, memo: $memo, creationDate: $creationDate) { + id + amount + memo + } + } +` + +export const updateContribution = gql` + mutation ($contributionId: Int!, $amount: Decimal!, $memo: String!, $creationDate: String!) { + updateContribution( + contributionId: $contributionId + amount: $amount + memo: $memo + creationDate: $creationDate + ) { + id amount memo } diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 531aebf0f..9f7a02e70 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -172,6 +172,46 @@ export const queryTransactionLink = gql` } ` +export const listContributions = gql` + query ( + $currentPage: Int = 1 + $pageSize: Int = 5 + $order: Order + $filterConfirmed: Boolean = false + ) { + listContributions( + currentPage: $currentPage + pageSize: $pageSize + order: $order + filterConfirmed: $filterConfirmed + ) { + contributionCount + contributionList { + id + amount + memo + } + } + } +` + +export const listAllContributions = ` +query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC) { + listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + contributionCount + contributionList { + id + firstName + lastName + amount + memo + createdAt + confirmedAt + confirmedBy + } + } +} +` // from admin interface export const listUnconfirmedContributions = gql` diff --git a/backend/src/server/plugins.ts b/backend/src/server/plugins.ts index 1972bc1c8..24df45baa 100644 --- a/backend/src/server/plugins.ts +++ b/backend/src/server/plugins.ts @@ -31,20 +31,24 @@ const filterVariables = (variables: any) => { const logPlugin = { requestDidStart(requestContext: any) { const { logger } = requestContext - const { query, mutation, variables } = requestContext.request - logger.info(`Request: + const { query, mutation, variables, operationName } = requestContext.request + if (operationName !== 'IntrospectionQuery') { + logger.info(`Request: ${mutation || query}variables: ${JSON.stringify(filterVariables(variables), null, 2)}`) + } return { willSendResponse(requestContext: any) { - if (requestContext.context.user) logger.info(`User ID: ${requestContext.context.user.id}`) - if (requestContext.response.data) { - logger.info('Response Success!') - logger.trace(`Response-Data: + if (operationName !== 'IntrospectionQuery') { + if (requestContext.context.user) logger.info(`User ID: ${requestContext.context.user.id}`) + if (requestContext.response.data) { + logger.info('Response Success!') + logger.trace(`Response-Data: ${JSON.stringify(requestContext.response.data, null, 2)}`) - } - if (requestContext.response.errors) - logger.error(`Response-Errors: + } + if (requestContext.response.errors) + logger.error(`Response-Errors: ${JSON.stringify(requestContext.response.errors, null, 2)}`) + } return requestContext }, } diff --git a/database/entity/0039-contributions_table/Contribution.ts b/database/entity/0039-contributions_table/Contribution.ts index 6c7358f90..b5e6ac0e0 100644 --- a/database/entity/0039-contributions_table/Contribution.ts +++ b/database/entity/0039-contributions_table/Contribution.ts @@ -1,6 +1,15 @@ import Decimal from 'decimal.js-light' -import { BaseEntity, Column, Entity, PrimaryGeneratedColumn, DeleteDateColumn } from 'typeorm' +import { + BaseEntity, + Column, + Entity, + PrimaryGeneratedColumn, + DeleteDateColumn, + JoinColumn, + ManyToOne, +} from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' +import { User } from '../User' @Entity('contributions') export class Contribution extends BaseEntity { @@ -10,6 +19,10 @@ export class Contribution extends BaseEntity { @Column({ unsigned: true, nullable: false, name: 'user_id' }) userId: number + @ManyToOne(() => User, (user) => user.contributions) + @JoinColumn({ name: 'user_id' }) + user: User + @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' }) createdAt: Date diff --git a/database/entity/0040-add_contribution_link_id_to_user/User.ts b/database/entity/0040-add_contribution_link_id_to_user/User.ts index 9bf76e5f5..56047345a 100644 --- a/database/entity/0040-add_contribution_link_id_to_user/User.ts +++ b/database/entity/0040-add_contribution_link_id_to_user/User.ts @@ -1,4 +1,13 @@ -import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, +} from 'typeorm' +import { Contribution } from '../Contribution' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { @@ -76,4 +85,8 @@ export class User extends BaseEntity { default: null, }) passphrase: string + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] } diff --git a/database/entity/0043-add_event_protocol_table/EventProtocol.ts b/database/entity/0043-add_event_protocol_table/EventProtocol.ts new file mode 100644 index 000000000..72470d2ed --- /dev/null +++ b/database/entity/0043-add_event_protocol_table/EventProtocol.ts @@ -0,0 +1,39 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('event_protocol') +export class EventProtocol extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ length: 100, nullable: false, collation: 'utf8mb4_unicode_ci' }) + type: string + + @Column({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) + createdAt: Date + + @Column({ name: 'user_id', unsigned: true, nullable: false }) + userId: number + + @Column({ name: 'x_user_id', unsigned: true, nullable: true }) + xUserId: number + + @Column({ name: 'x_community_id', unsigned: true, nullable: true }) + xCommunityId: number + + @Column({ name: 'transaction_id', unsigned: true, nullable: true }) + transactionId: number + + @Column({ name: 'contribution_id', unsigned: true, nullable: true }) + contributionId: number + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + transformer: DecimalTransformer, + }) + amount: Decimal +} diff --git a/database/entity/EventProtocol.ts b/database/entity/EventProtocol.ts new file mode 100644 index 000000000..4a73f146c --- /dev/null +++ b/database/entity/EventProtocol.ts @@ -0,0 +1 @@ +export { EventProtocol } from './0043-add_event_protocol_table/EventProtocol' diff --git a/database/entity/index.ts b/database/entity/index.ts index 266c40740..733c99a3a 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -6,6 +6,7 @@ import { Transaction } from './Transaction' import { TransactionLink } from './TransactionLink' import { User } from './User' import { Contribution } from './Contribution' +import { EventProtocol } from './EventProtocol' export const entities = [ Contribution, @@ -16,4 +17,5 @@ export const entities = [ Transaction, TransactionLink, User, + EventProtocol, ] diff --git a/database/migrations/0041-move_users_creation_date.ts b/database/migrations/0041-move_users_creation_date.ts new file mode 100644 index 000000000..27353a5b4 --- /dev/null +++ b/database/migrations/0041-move_users_creation_date.ts @@ -0,0 +1,130 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/* +Move forward the creation date of the users by 1 or 2 hours, +for which there are transactions created before their account creation. + +Because of a error by importing data from old db format into new, all older transactions balance_date +are 1 or 2 hours off + +*/ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + /* generate raw mysql queries + const usersToMove = await queryFn( + ` + SELECT u.id, u.created, t.balance_date + FROM \`users\` as u + LEFT JOIN \`transactions\` as t + ON t.user_id = u.id where t.balance_date < u.created + order by id + ` + ) + let downgradeQueries = '' + for(const id in usersToMove) { + const user = usersToMove[id] + const diff = (user.created - user.balance_date) / 1000 + const correcture = diff < 3600 ? 1: 2 + const correctedDate = new Date(user.created) + correctedDate.setHours(correctedDate.getHours() - correcture) + //console.log("%d, %s, %s, %s, %d", user.id, user.created, user.balance_date, diff, correcture) + console.log('await queryFn(`UPDATE \\`users\\` SET \\`created\\` = \'%s\' WHERE \\`id\\` = %d`)', + correctedDate.toISOString().slice(0, 19).replace('T', ' '), + user.id + ) + downgradeQueries += 'await queryFn(`UPDATE \\`users\\` SET \\`created\\` = \'' + downgradeQueries += user.created.toISOString().slice(0, 19).replace('T', ' ') + downgradeQueries += '\' WHERE \\`id\\` = ' + downgradeQueries += user.id + downgradeQueries += '`)\n' + } + console.log('downgrade: \n%s', downgradeQueries) + */ + + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-01-25 08:01:52' WHERE \`id\` = 179`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-05-26 10:21:57' WHERE \`id\` = 443`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-06-08 17:04:41' WHERE \`id\` = 490`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-06-12 20:07:17' WHERE \`id\` = 508`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-07-17 19:20:36' WHERE \`id\` = 621`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-11-22 16:31:48' WHERE \`id\` = 788`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-12-17 20:09:16' WHERE \`id\` = 825`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-01-26 13:09:35' WHERE \`id\` = 949`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-03-20 16:55:46' WHERE \`id\` = 1057`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 15:59:30' WHERE \`id\` = 1228`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 17:38:47' WHERE \`id\` = 1229`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 17:38:47' WHERE \`id\` = 1229`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 17:38:47' WHERE \`id\` = 1229`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 17:58:15' WHERE \`id\` = 1230`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 17:58:15' WHERE \`id\` = 1230`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 14:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 14:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 14:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 14:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-17 22:51:19' WHERE \`id\` = 1239`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-03 07:23:28' WHERE \`id\` = 1273`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-09 06:16:18' WHERE \`id\` = 1287`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-17 11:26:41' WHERE \`id\` = 1298`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-30 15:56:27' WHERE \`id\` = 1315`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-30 15:56:27' WHERE \`id\` = 1315`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-30 15:56:27' WHERE \`id\` = 1315`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-08 07:24:57' WHERE \`id\` = 1326`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-08 07:24:57' WHERE \`id\` = 1326`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-13 12:07:29' WHERE \`id\` = 1342`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-16 15:32:48' WHERE \`id\` = 1348`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-09-30 14:06:40' WHERE \`id\` = 1470`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-10-15 14:35:37' WHERE \`id\` = 1490`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-10-16 06:42:00' WHERE \`id\` = 1492`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-10-16 06:42:00' WHERE \`id\` = 1492`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-11-22 09:45:15' WHERE \`id\` = 1576`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-11-23 13:55:37' WHERE \`id\` = 1582`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-11 14:58:12' WHERE \`id\` = 1729`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-11 18:03:10' WHERE \`id\` = 1732`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-19 15:00:38' WHERE \`id\` = 1756`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-19 20:01:58' WHERE \`id\` = 1757`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-21 15:58:48' WHERE \`id\` = 1762`) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-01-25 09:01:52' WHERE \`id\` = 179`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-05-26 11:21:57' WHERE \`id\` = 443`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-06-08 19:04:41' WHERE \`id\` = 490`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-06-12 22:07:17' WHERE \`id\` = 508`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-07-17 21:20:36' WHERE \`id\` = 621`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-11-22 17:31:48' WHERE \`id\` = 788`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2020-12-17 21:09:16' WHERE \`id\` = 825`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-01-26 14:09:35' WHERE \`id\` = 949`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-03-20 17:55:46' WHERE \`id\` = 1057`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 17:59:30' WHERE \`id\` = 1228`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 19:38:47' WHERE \`id\` = 1229`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 19:38:47' WHERE \`id\` = 1229`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 19:38:47' WHERE \`id\` = 1229`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 19:58:15' WHERE \`id\` = 1230`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-13 19:58:15' WHERE \`id\` = 1230`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 16:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 16:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 16:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-14 16:27:49' WHERE \`id\` = 1231`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-05-18 00:51:19' WHERE \`id\` = 1239`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-03 09:23:28' WHERE \`id\` = 1273`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-09 08:16:18' WHERE \`id\` = 1287`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-17 13:26:41' WHERE \`id\` = 1298`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-30 17:56:27' WHERE \`id\` = 1315`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-30 17:56:27' WHERE \`id\` = 1315`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-06-30 17:56:27' WHERE \`id\` = 1315`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-08 09:24:57' WHERE \`id\` = 1326`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-08 09:24:57' WHERE \`id\` = 1326`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-13 14:07:29' WHERE \`id\` = 1342`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-07-16 16:32:48' WHERE \`id\` = 1348`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-09-30 16:06:40' WHERE \`id\` = 1470`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-10-15 16:35:37' WHERE \`id\` = 1490`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-10-16 08:42:00' WHERE \`id\` = 1492`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-10-16 08:42:00' WHERE \`id\` = 1492`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-11-22 10:45:15' WHERE \`id\` = 1576`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2021-11-23 14:55:37' WHERE \`id\` = 1582`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-11 15:58:12' WHERE \`id\` = 1729`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-11 19:03:10' WHERE \`id\` = 1732`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-19 16:00:38' WHERE \`id\` = 1756`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-19 21:01:58' WHERE \`id\` = 1757`) + await queryFn(`UPDATE \`users\` SET \`created\` = '2022-01-21 16:58:48' WHERE \`id\` = 1762`) +} diff --git a/database/migrations/0042-update_transactions_for_blockchain.ts b/database/migrations/0042-update_transactions_for_blockchain.ts new file mode 100644 index 000000000..d7b26abbd --- /dev/null +++ b/database/migrations/0042-update_transactions_for_blockchain.ts @@ -0,0 +1,1917 @@ +/* MIGRATION for updating transactions from the past to follow the blockchain rules */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // split creation transaction with 3000 GDD created in one transaction what isn't allowed + const transactionMemos: string[] = [ + 'Aktives Grundeinkommen für GL. Dez', + 'Aktives Grundeinkommen für GL. Jan', + 'Aktives Grundeinkommen für GL. Feb', + ] + const creationDate = new Date('2020-03-30 06:59:55') + + await queryFn( + `UPDATE \`transactions\` set \`amount\` = 1000, \`balance\` = 1000, \`memo\` = ? WHERE \`id\` = 150`, + [transactionMemos[0]], + ) + + // [ RowDataPacket { 'MAX(`id`)': 6828 } ] + const lastTransactionId = (await queryFn(`SELECT MAX(\`id\`) as max_id from \`transactions\``))[0] + .max_id + // dummy id to insert two transactions before this (previous has an index on it) + await queryFn(`UPDATE \`transactions\` set \`previous\` = ? WHERE \`id\` = 278`, [ + lastTransactionId + 30, + ]) + + await queryFn( + `INSERT INTO \`transactions\`( + \`user_id\`, \`previous\`, \`type_id\`, \`amount\`, \`balance\`, + \`balance_date\`, \`decay\`, \`memo\`, \`creation_date\` + ) VALUES( + 275, 150, 1, 1000, 2000, + ?, 0, ?, ? + )`, + [ + creationDate, + transactionMemos[1], + new Date(creationDate.getFullYear(), creationDate.getMonth() - 1, 1), + ], + ) + await queryFn( + `INSERT INTO \`transactions\`( + \`user_id\`, \`previous\`, \`type_id\`, \`amount\`, \`balance\`, + \`balance_date\`, \`decay\`, \`memo\`, \`creation_date\` + ) VALUES( + 275, LAST_INSERT_ID(), 1, 1000, 3000, + ?, 0, ?, ? + )`, + [ + creationDate, + transactionMemos[2], + new Date(creationDate.getFullYear(), creationDate.getMonth() - 2, 1), + ], + ) + await queryFn(`UPDATE \`transactions\` set \`previous\` = LAST_INSERT_ID() WHERE \`id\` = 278`) + + // ---------------------------------------------------------------------------------------------- + // update creation_date for transactions with creation_date == balance_date + /* js code for generating mysql raw queries + // print mysql raw querys + interface ReplaceSet { + monthName: RegExp + monthValue: number + } + // js month starting with 0 for Jan + const replaceSets: ReplaceSet[] = [ + { monthName: new RegExp('.*Dez.*'), monthValue: -1}, + { monthName: new RegExp('.*Jan.*'), monthValue: 0 }, + { monthName: new RegExp('.*Feb.*'), monthValue: 1 }, + { monthName: new RegExp('.*M.rz.*'), monthValue: 2 }, + { monthName: new RegExp('.*April.*'), monthValue: 3 }, + ] + const transaction_ids = await queryFn(` + SELECT id, balance_date, creation_date, memo + FROM \`transactions\` + WHERE \`balance_date\` = \`creation_date\` + AND \`type_id\` = 1 + `) + let downgradeQueries = '' + for(let id in transaction_ids) { + const transaction = transaction_ids[id] + let updatedCreationDate: Date | null = null + // determine correct creation date + for(const replaceSet of replaceSets) { + // check if target creation can be determine which help of the memo + if(transaction.memo.match(replaceSet.monthName)) { + const oldCreationDate = transaction.creation_date + updatedCreationDate = new Date(oldCreationDate) + updatedCreationDate.setMonth(replaceSet.monthValue) + break + } + } + // couldn't find info in memo, simply set date 1 month back + if(updatedCreationDate === null) { + //date.setMonth(date.getMonth() - numOfMonths); + updatedCreationDate = new Date(transaction.creation_date) + updatedCreationDate.setMonth(transaction.creation_date.getMonth() - 1) + } + if(updatedCreationDate.getMonth() == 1) { + // only 28 februars exist so let us fix it + if(updatedCreationDate.getDate() > 28) { + updatedCreationDate.setDate(28) + } + } + console.log('// %s, original creation date: %s\nawait queryFn(`UPDATE \\`transactions\\` SET creation_date = \'%s\' WHERE \\`id\\` = %d`)\n', + transaction.memo, + transaction.creation_date.toISOString().slice(0, 19).replace('T', ' '), + updatedCreationDate.toISOString().slice(0, 19).replace('T', ' '), + transaction.id + ) + downgradeQueries += 'await queryFn(`UPDATE \\`transactions\\` SET creation_date = \'' + downgradeQueries += transaction.creation_date.toISOString().slice(0, 19).replace('T', ' ') + downgradeQueries += '\' WHERE \\`id\\` = ' + downgradeQueries += transaction.id + downgradeQueries += '`)\n' + } + console.log("downgrade: \n%s", downgradeQueries) + console.log("transactions count: %d", transaction_ids.length) // 224 +*/ + + // Erste Schöpfung, viel Spaß damit ;), original creation date: 2019-12-17 13:06:13 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-11-17 13:06:13' WHERE \`id\` = 1`, + ) + + // Erste Schöpfung, viel Spaß damit ;), original creation date: 2019-12-17 13:06:16 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-11-17 13:06:16' WHERE \`id\` = 2`, + ) + + // Erste Schöpfung, viel Spaß damit ;), original creation date: 2019-12-17 13:06:19 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-11-17 13:06:19' WHERE \`id\` = 3`, + ) + + // Erste Schöpfung, viel Spaß damit ;), original creation date: 2019-12-17 13:06:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-11-17 13:06:21' WHERE \`id\` = 4`, + ) + + // Test1 , original creation date: 2019-12-17 13:07:01 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-11-17 13:07:01' WHERE \`id\` = 5`, + ) + + // EIn bisschen Nachschub für dich ;), original creation date: 2019-12-19 17:35:16 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-11-19 17:35:16' WHERE \`id\` = 6`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-08 10:34:40 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-08 10:34:40' WHERE \`id\` = 7`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-08 11:02:41 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-08 11:02:41' WHERE \`id\` = 8`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-08 11:06:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-08 11:06:42' WHERE \`id\` = 9`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-08 11:16:11 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-08 11:16:11' WHERE \`id\` = 10`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-08 11:16:15 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-08 11:16:15' WHERE \`id\` = 11`, + ) + + // Für deine tolle Arbeit ;), original creation date: 2020-01-08 13:24:29 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-08 13:24:29' WHERE \`id\` = 12`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-09 12:02:48 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-09 12:02:48' WHERE \`id\` = 13`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-09 12:02:52 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-09 12:02:52' WHERE \`id\` = 14`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-09 12:02:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-09 12:02:54' WHERE \`id\` = 15`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-09 12:09:08 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-09 12:09:08' WHERE \`id\` = 16`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 11:10:55 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 11:10:55' WHERE \`id\` = 17`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 11:11:24 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 11:11:24' WHERE \`id\` = 18`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 11:12:09 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 11:12:09' WHERE \`id\` = 19`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 11:31:20 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 11:31:20' WHERE \`id\` = 20`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 11:37:13 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 11:37:13' WHERE \`id\` = 21`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 11:37:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 11:37:42' WHERE \`id\` = 22`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-11 19:01:06 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-11 19:01:06' WHERE \`id\` = 23`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-13 16:27:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-13 16:27:07' WHERE \`id\` = 24`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-13 16:28:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-13 16:28:05' WHERE \`id\` = 25`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-13 16:28:57 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-13 16:28:57' WHERE \`id\` = 26`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-13 16:29:45 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-13 16:29:45' WHERE \`id\` = 27`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-17 13:47:28 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:47:28' WHERE \`id\` = 28`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-17 13:48:52 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:48:52' WHERE \`id\` = 29`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-17 13:49:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:49:21' WHERE \`id\` = 30`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-23 13:08:44 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-23 13:08:44' WHERE \`id\` = 31`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-27 08:12:16 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-27 08:12:16' WHERE \`id\` = 32`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:49:58 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:49:58' WHERE \`id\` = 33`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:50:35 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:50:35' WHERE \`id\` = 34`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:50:55 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:50:55' WHERE \`id\` = 35`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:52:03 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:52:03' WHERE \`id\` = 36`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:52:33 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:52:33' WHERE \`id\` = 37`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:52:53 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:52:53' WHERE \`id\` = 38`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:53:45 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:53:45' WHERE \`id\` = 39`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:54:13 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:54:13' WHERE \`id\` = 40`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 14:54:50 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 14:54:50' WHERE \`id\` = 41`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 15:03:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 15:03:26' WHERE \`id\` = 42`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 15:10:39 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 15:10:39' WHERE \`id\` = 43`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 15:13:10 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 15:13:10' WHERE \`id\` = 44`, + ) + + // AGE Dezember 2019, original creation date: 2020-01-30 15:26:45 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 15:26:45' WHERE \`id\` = 45`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:54:31 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:54:31' WHERE \`id\` = 46`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:30 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:30' WHERE \`id\` = 47`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:33 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:33' WHERE \`id\` = 48`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:36 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:36' WHERE \`id\` = 49`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:39 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:39' WHERE \`id\` = 50`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:43 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:43' WHERE \`id\` = 51`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:46 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:46' WHERE \`id\` = 52`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:48 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:48' WHERE \`id\` = 53`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:50 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:50' WHERE \`id\` = 54`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:52 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:52' WHERE \`id\` = 55`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:54' WHERE \`id\` = 56`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:56 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:56' WHERE \`id\` = 57`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:55:59 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:55:59' WHERE \`id\` = 58`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:02 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:02' WHERE \`id\` = 59`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:05' WHERE \`id\` = 60`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:07' WHERE \`id\` = 61`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:10 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:10' WHERE \`id\` = 62`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:12 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:12' WHERE \`id\` = 63`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:14 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:14' WHERE \`id\` = 64`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:21' WHERE \`id\` = 65`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:24 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:24' WHERE \`id\` = 66`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:26' WHERE \`id\` = 67`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:28 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:28' WHERE \`id\` = 68`, + ) + + // Grundeinkommen für Gemeinschaftsleistungen Jan. 2020, original creation date: 2020-02-19 08:56:31 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-19 08:56:31' WHERE \`id\` = 69`, + ) + + // Grundeinkommen rückwirkend Dezember, original creation date: 2020-02-20 06:40:36 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-20 06:40:36' WHERE \`id\` = 70`, + ) + + // Grundeinkommen für GLJanuar 2020, original creation date: 2020-02-20 06:43:41 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-20 06:43:41' WHERE \`id\` = 71`, + ) + + // Grundeinkommen für GLJanuar 2020, original creation date: 2020-02-20 06:43:43 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-20 06:43:43' WHERE \`id\` = 72`, + ) + + // Grundeinkommen für GLJanuar 2020, original creation date: 2020-02-20 06:43:45 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-20 06:43:45' WHERE \`id\` = 73`, + ) + + // Grundeinkommen für GLJanuar 2020, original creation date: 2020-02-20 06:43:47 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-20 06:43:47' WHERE \`id\` = 74`, + ) + + // Grundeinkommen, original creation date: 2020-02-20 07:09:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-20 07:09:05' WHERE \`id\` = 75`, + ) + + // Grundeinkommen für GLJanuar 2020, original creation date: 2020-02-20 08:03:31 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-20 08:03:31' WHERE \`id\` = 76`, + ) + + // AGE für Gemeinschaftsleistungen Januar 2020, original creation date: 2020-02-29 16:17:24 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-29 16:17:24' WHERE \`id\` = 77`, + ) + + // AGE für Gemeinschaftsleistungen Januar 2020, original creation date: 2020-02-29 16:17:35 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-29 16:17:35' WHERE \`id\` = 78`, + ) + + // AGE für Gemeinschaftsleistungen Januar 2020, original creation date: 2020-02-29 16:17:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-29 16:17:42' WHERE \`id\` = 79`, + ) + + // AGE für Gemeinschaftsleistungen Januar 2020, original creation date: 2020-02-29 16:29:47 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-29 16:29:47' WHERE \`id\` = 80`, + ) + + // AGE für Gemeinschaftsleistungen Januar 2020, original creation date: 2020-02-29 16:29:48 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-29 16:29:48' WHERE \`id\` = 81`, + ) + + // AGE für Gemeinschaftsleistungen Januar 2020, original creation date: 2020-02-29 16:29:55 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-29 16:29:55' WHERE \`id\` = 82`, + ) + + // AGE rückwirkend für Januar 2020, original creation date: 2020-03-04 09:08:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-04 09:08:54' WHERE \`id\` = 83`, + ) + + // AGE für Gemeinschaftsleitungen Januar, original creation date: 2020-03-07 07:59:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-07 07:59:42' WHERE \`id\` = 84`, + ) + + // AGE für Gemeinschaftsleitungen Januar, original creation date: 2020-03-07 07:59:51 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-07 07:59:51' WHERE \`id\` = 85`, + ) + + // AGE für Gemeinschaftsleitungen Januar, original creation date: 2020-03-07 07:59:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-07 07:59:54' WHERE \`id\` = 86`, + ) + + // AGE für Gemeinschaftsleitungen Januar, original creation date: 2020-03-07 07:59:57 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-07 07:59:57' WHERE \`id\` = 87`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:15:48 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:15:48' WHERE \`id\` = 88`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:15:53 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:15:53' WHERE \`id\` = 89`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:15:56 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:15:56' WHERE \`id\` = 90`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:15:59 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:15:59' WHERE \`id\` = 91`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:16:01 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:16:01' WHERE \`id\` = 92`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:16:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:16:05' WHERE \`id\` = 93`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:16:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:16:07' WHERE \`id\` = 94`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:16:10 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:16:10' WHERE \`id\` = 95`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:00 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:00' WHERE \`id\` = 96`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:03 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:03' WHERE \`id\` = 97`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:05' WHERE \`id\` = 98`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:07' WHERE \`id\` = 99`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:09 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:09' WHERE \`id\` = 100`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:11 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:11' WHERE \`id\` = 101`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:13 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:13' WHERE \`id\` = 102`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:21' WHERE \`id\` = 103`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:25 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:25' WHERE \`id\` = 104`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-07 08:17:28 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:17:28' WHERE \`id\` = 105`, + ) + + // AGE für Gemeinschaftsleistungen Februar, original creation date: 2020-03-07 08:19:15 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-07 08:19:15' WHERE \`id\` = 106`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 geschöpft und gutgeschrieben., original creation date: 2020-03-08 18:00:20 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-08 18:00:20' WHERE \`id\` = 107`, + ) + + // AGE für Februar, original creation date: 2020-03-09 11:43:30 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-09 11:43:30' WHERE \`id\` = 108`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-09 17:48:34 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-09 17:48:34' WHERE \`id\` = 109`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-09 17:48:36 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-09 17:48:36' WHERE \`id\` = 110`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-09 17:48:38 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-09 17:48:38' WHERE \`id\` = 111`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-09 17:48:40 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-09 17:48:40' WHERE \`id\` = 112`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020, original creation date: 2020-03-09 17:48:43 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-09 17:48:43' WHERE \`id\` = 113`, + ) + + // AGE für Gemeinschaftsleistungen rückwirkend für Januar 2020 , original creation date: 2020-03-11 21:11:03 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 21:11:03' WHERE \`id\` = 114`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-11 21:17:22 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-11 21:17:22' WHERE \`id\` = 115`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-11 21:17:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-11 21:17:26' WHERE \`id\` = 116`, + ) + + // AGE für Gemeinschaftsleistungen rückwirkend für Januar, original creation date: 2020-03-18 15:44:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-18 15:44:07' WHERE \`id\` = 117`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:54:59 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:54:59' WHERE \`id\` = 118`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:55:32 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:55:32' WHERE \`id\` = 119`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:56:32 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:56:32' WHERE \`id\` = 120`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:56:48 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:56:48' WHERE \`id\` = 121`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:57:14 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:57:14' WHERE \`id\` = 122`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:58:41 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:58:41' WHERE \`id\` = 123`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:59:03 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:59:03' WHERE \`id\` = 124`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 15:59:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 15:59:26' WHERE \`id\` = 125`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 16:00:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 16:00:05' WHERE \`id\` = 126`, + ) + + // AGE für Gemeinschaftsleistungen Februar 2020 , original creation date: 2020-03-18 16:00:12 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-18 16:00:12' WHERE \`id\` = 127`, + ) + + // Aktives Grundeinkommen, original creation date: 2020-03-23 07:35:35 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-23 07:35:35' WHERE \`id\` = 128`, + ) + + // Aktives Grundeinkommen, original creation date: 2020-03-23 07:35:38 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-23 07:35:38' WHERE \`id\` = 129`, + ) + + // Aktives Grundeinkommen, original creation date: 2020-03-23 07:35:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-23 07:35:42' WHERE \`id\` = 130`, + ) + + // Aktives Grundeinkommen Januar , original creation date: 2020-03-24 17:00:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-24 17:00:05' WHERE \`id\` = 131`, + ) + + // Aktives Grundeinkommen Februar, original creation date: 2020-03-24 17:06:50 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-24 17:06:50' WHERE \`id\` = 132`, + ) + + // Aktives Grundeinkommen Februar, original creation date: 2020-03-24 17:06:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-24 17:06:54' WHERE \`id\` = 133`, + ) + + // Aktives Grundeinkommen März, original creation date: 2020-03-24 17:14:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:21' WHERE \`id\` = 134`, + ) + + // Aktives Grundeinkommen März, original creation date: 2020-03-24 17:14:27 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:27' WHERE \`id\` = 135`, + ) + + // Aktives Grundeinkommen März, original creation date: 2020-03-24 17:14:28 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:28' WHERE \`id\` = 136`, + ) + + // Aktives Grundeinkommen März, original creation date: 2020-03-24 17:14:30 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:30' WHERE \`id\` = 137`, + ) + + // Aktives Grundeinkommen geschöpft , original creation date: 2020-03-27 09:06:16 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:06:16' WHERE \`id\` = 138`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:27 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:27' WHERE \`id\` = 139`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:29 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:29' WHERE \`id\` = 140`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:30 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:30' WHERE \`id\` = 141`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:35 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:35' WHERE \`id\` = 142`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:36 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:36' WHERE \`id\` = 143`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:40 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:40' WHERE \`id\` = 144`, + ) + + // Aktives Grundeinkommen für GL Februar, original creation date: 2020-03-27 09:44:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-27 09:44:42' WHERE \`id\` = 145`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-03-28 09:28:24 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-28 09:28:24' WHERE \`id\` = 146`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-03-28 09:34:30 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-28 09:34:30' WHERE \`id\` = 147`, + ) + + // , original creation date: 2020-03-29 17:21:33 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-28 17:21:33' WHERE \`id\` = 148`, + ) + + // , original creation date: 2020-03-29 17:21:35 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-28 17:21:35' WHERE \`id\` = 149`, + ) + + // Aktives Grundeinkommen für GL. Dez, original creation date: 2020-03-30 06:59:55 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 06:59:55' WHERE \`id\` = 150`, + ) + + // Aktives Grundeinkommen für GL. Dez 2019, original creation date: 2020-03-30 07:05:14 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-30 07:05:14' WHERE \`id\` = 151`, + ) + + // Aktives Grundeinkommen für GL. Jan 2020, original creation date: 2020-03-30 07:09:52 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 07:09:52' WHERE \`id\` = 152`, + ) + + // Aktives Grundeinkommen für GL. Februar 2020, original creation date: 2020-03-30 07:15:43 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 07:15:43' WHERE \`id\` = 153`, + ) + + // Aktives Grundeinkommen für GL., original creation date: 2020-03-30 07:16:33 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 07:16:33' WHERE \`id\` = 154`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-03-30 07:21:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 07:21:07' WHERE \`id\` = 155`, + ) + + // Aktives Grundeinkommen für GL. Feb. 2020, original creation date: 2020-03-30 11:47:55 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 11:47:55' WHERE \`id\` = 156`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-01 06:56:20 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 06:56:20' WHERE \`id\` = 157`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-01 06:56:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 06:56:26' WHERE \`id\` = 158`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-01 06:56:29 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 06:56:29' WHERE \`id\` = 159`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-01 06:56:31 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 06:56:31' WHERE \`id\` = 160`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-01 06:58:10 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-01 06:58:10' WHERE \`id\` = 161`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-07 14:09:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 14:09:26' WHERE \`id\` = 162`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-07 14:09:28 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 14:09:28' WHERE \`id\` = 163`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-07 14:09:29 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 14:09:29' WHERE \`id\` = 164`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-07 14:09:32 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 14:09:32' WHERE \`id\` = 165`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-08 18:41:17 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:41:17' WHERE \`id\` = 166`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-08 18:41:20 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:41:20' WHERE \`id\` = 167`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-08 18:41:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:41:21' WHERE \`id\` = 168`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-08 18:41:23 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:41:23' WHERE \`id\` = 169`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-08 18:41:27 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:41:27' WHERE \`id\` = 170`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-08 18:58:15 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:58:15' WHERE \`id\` = 171`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:10:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:10:54' WHERE \`id\` = 172`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:10:56 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:10:56' WHERE \`id\` = 173`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:10:58 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:10:58' WHERE \`id\` = 174`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:11:01 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:11:01' WHERE \`id\` = 175`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:11:02 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:11:02' WHERE \`id\` = 176`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:11:04 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:11:04' WHERE \`id\` = 177`, + ) + + // Aktives Grundeinkommen für GL. März, original creation date: 2020-04-15 16:11:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-15 16:11:07' WHERE \`id\` = 178`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-17 16:54:39 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-17 16:54:39' WHERE \`id\` = 179`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-17 16:54:40 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-17 16:54:40' WHERE \`id\` = 180`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-17 16:54:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-17 16:54:42' WHERE \`id\` = 181`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-17 16:54:44 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-17 16:54:44' WHERE \`id\` = 182`, + ) + + // Aktives Grundeinkommen geschöpft und gutgeschrieben. + // Tausend Dank, weil Du bei uns bist und gemeinsam mit uns das Lebensgeld der Zukunft erschaffst!, original creation date: 2020-04-18 08:58:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 08:58:42' WHERE \`id\` = 183`, + ) + + // Aktives Grundeinkommen geschöpft und gutgeschrieben. + // Tausend Dank, weil Du bei uns bist und gemeinsam mit uns das Lebensgeld der Zukunft erschaffst!, original creation date: 2020-04-18 08:58:44 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 08:58:44' WHERE \`id\` = 184`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-20 17:44:36 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-20 17:44:36' WHERE \`id\` = 185`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-20 17:44:38 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-20 17:44:38' WHERE \`id\` = 186`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-20 17:44:40 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-20 17:44:40' WHERE \`id\` = 187`, + ) + + // Aktives Grundeinkommen für GL.März 2020, original creation date: 2020-04-20 17:44:43 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-20 17:44:43' WHERE \`id\` = 188`, + ) + + // Aktives Grundeinkommen für GL März 2020, original creation date: 2020-04-25 06:30:13 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 06:30:13' WHERE \`id\` = 189`, + ) + + // Aktives Grundeinkommen für GL März 2020, original creation date: 2020-04-25 06:30:19 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 06:30:19' WHERE \`id\` = 190`, + ) + + // Aktives Grundeinkommen für GL März 2020, original creation date: 2020-04-25 06:30:21 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 06:30:21' WHERE \`id\` = 191`, + ) + + // Aktives Grundeinkommen für GL März 2020, original creation date: 2020-04-25 06:30:23 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 06:30:23' WHERE \`id\` = 192`, + ) + + // Aktives Grundeinkommen für GL März 2020, original creation date: 2020-04-25 06:30:24 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 06:30:24' WHERE \`id\` = 193`, + ) + + // Aktives Grundeinkommen für GL März 2020, original creation date: 2020-04-25 06:30:26 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 06:30:26' WHERE \`id\` = 194`, + ) + + // Aktives Grundeinkommen für G März 2020, original creation date: 2020-04-25 18:41:55 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 18:41:55' WHERE \`id\` = 195`, + ) + + // Aktives Grundeinkommen für G März 2020, original creation date: 2020-04-25 18:42:25 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 18:42:25' WHERE \`id\` = 196`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-25 18:46:01 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-25 18:46:01' WHERE \`id\` = 197`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-30 06:40:39 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:40:39' WHERE \`id\` = 198`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-30 06:40:41 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:40:41' WHERE \`id\` = 199`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-30 06:40:42 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:40:42' WHERE \`id\` = 200`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-30 06:40:45 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:40:45' WHERE \`id\` = 201`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-30 06:40:46 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:40:46' WHERE \`id\` = 202`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-04-30 06:40:50 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:40:50' WHERE \`id\` = 203`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-05-02 08:25:27 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-02 08:25:27' WHERE \`id\` = 204`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-05-02 08:25:29 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-02 08:25:29' WHERE \`id\` = 205`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-05-02 08:25:32 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-02 08:25:32' WHERE \`id\` = 206`, + ) + + // Aktives Grundeinkommen für März 2020, original creation date: 2020-05-02 08:28:14 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-02 08:28:14' WHERE \`id\` = 207`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:43:49 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:43:49' WHERE \`id\` = 208`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:43:51 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:43:51' WHERE \`id\` = 209`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:43:54 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:43:54' WHERE \`id\` = 210`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:43:56 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:43:56' WHERE \`id\` = 211`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:43:57 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:43:57' WHERE \`id\` = 212`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:43:59 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:43:59' WHERE \`id\` = 213`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:01 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:01' WHERE \`id\` = 214`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:02 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:02' WHERE \`id\` = 215`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:05 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:05' WHERE \`id\` = 216`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:10 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:10' WHERE \`id\` = 217`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:12 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:12' WHERE \`id\` = 218`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:14 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:14' WHERE \`id\` = 219`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-02 08:44:17 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-02 08:44:17' WHERE \`id\` = 220`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-03 09:00:04 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-03 09:00:04' WHERE \`id\` = 221`, + ) + + // Aktives Grundeinkommen für April 2020, original creation date: 2020-05-03 09:00:07 + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-03 09:00:07' WHERE \`id\` = 222`, + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // reverse creation date changes + + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:06:13' WHERE \`id\` = 1`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:06:16' WHERE \`id\` = 2`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:06:19' WHERE \`id\` = 3`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:06:21' WHERE \`id\` = 4`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-17 13:07:01' WHERE \`id\` = 5`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2019-12-19 17:35:16' WHERE \`id\` = 6`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-08 10:34:40' WHERE \`id\` = 7`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-08 11:02:41' WHERE \`id\` = 8`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-08 11:06:42' WHERE \`id\` = 9`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-08 11:16:11' WHERE \`id\` = 10`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-08 11:16:15' WHERE \`id\` = 11`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-08 13:24:29' WHERE \`id\` = 12`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-09 12:02:48' WHERE \`id\` = 13`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-09 12:02:52' WHERE \`id\` = 14`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-09 12:02:54' WHERE \`id\` = 15`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-09 12:09:08' WHERE \`id\` = 16`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 11:10:55' WHERE \`id\` = 17`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 11:11:24' WHERE \`id\` = 18`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 11:12:09' WHERE \`id\` = 19`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 11:31:20' WHERE \`id\` = 20`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 11:37:13' WHERE \`id\` = 21`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 11:37:42' WHERE \`id\` = 22`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-11 19:01:06' WHERE \`id\` = 23`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-13 16:27:07' WHERE \`id\` = 24`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-13 16:28:05' WHERE \`id\` = 25`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-13 16:28:57' WHERE \`id\` = 26`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-13 16:29:45' WHERE \`id\` = 27`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-17 13:47:28' WHERE \`id\` = 28`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-17 13:48:52' WHERE \`id\` = 29`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-17 13:49:21' WHERE \`id\` = 30`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-23 13:08:44' WHERE \`id\` = 31`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-27 08:12:16' WHERE \`id\` = 32`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:49:58' WHERE \`id\` = 33`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:50:35' WHERE \`id\` = 34`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:50:55' WHERE \`id\` = 35`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:52:03' WHERE \`id\` = 36`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:52:33' WHERE \`id\` = 37`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:52:53' WHERE \`id\` = 38`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:53:45' WHERE \`id\` = 39`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:54:13' WHERE \`id\` = 40`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 14:54:50' WHERE \`id\` = 41`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 15:03:26' WHERE \`id\` = 42`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 15:10:39' WHERE \`id\` = 43`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 15:13:10' WHERE \`id\` = 44`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-01-30 15:26:45' WHERE \`id\` = 45`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:54:31' WHERE \`id\` = 46`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:30' WHERE \`id\` = 47`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:33' WHERE \`id\` = 48`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:36' WHERE \`id\` = 49`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:39' WHERE \`id\` = 50`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:43' WHERE \`id\` = 51`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:46' WHERE \`id\` = 52`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:48' WHERE \`id\` = 53`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:50' WHERE \`id\` = 54`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:52' WHERE \`id\` = 55`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:54' WHERE \`id\` = 56`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:56' WHERE \`id\` = 57`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:55:59' WHERE \`id\` = 58`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:02' WHERE \`id\` = 59`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:05' WHERE \`id\` = 60`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:07' WHERE \`id\` = 61`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:10' WHERE \`id\` = 62`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:12' WHERE \`id\` = 63`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:14' WHERE \`id\` = 64`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:21' WHERE \`id\` = 65`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:24' WHERE \`id\` = 66`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:26' WHERE \`id\` = 67`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:28' WHERE \`id\` = 68`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-19 08:56:31' WHERE \`id\` = 69`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 06:40:36' WHERE \`id\` = 70`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 06:43:41' WHERE \`id\` = 71`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 06:43:43' WHERE \`id\` = 72`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 06:43:45' WHERE \`id\` = 73`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 06:43:47' WHERE \`id\` = 74`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 07:09:05' WHERE \`id\` = 75`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-20 08:03:31' WHERE \`id\` = 76`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-29 16:17:24' WHERE \`id\` = 77`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-29 16:17:35' WHERE \`id\` = 78`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-29 16:17:42' WHERE \`id\` = 79`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-29 16:29:47' WHERE \`id\` = 80`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-29 16:29:48' WHERE \`id\` = 81`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-02-29 16:29:55' WHERE \`id\` = 82`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-04 09:08:54' WHERE \`id\` = 83`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 07:59:42' WHERE \`id\` = 84`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 07:59:51' WHERE \`id\` = 85`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 07:59:54' WHERE \`id\` = 86`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 07:59:57' WHERE \`id\` = 87`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:15:48' WHERE \`id\` = 88`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:15:53' WHERE \`id\` = 89`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:15:56' WHERE \`id\` = 90`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:15:59' WHERE \`id\` = 91`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:16:01' WHERE \`id\` = 92`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:16:05' WHERE \`id\` = 93`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:16:07' WHERE \`id\` = 94`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:16:10' WHERE \`id\` = 95`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:00' WHERE \`id\` = 96`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:03' WHERE \`id\` = 97`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:05' WHERE \`id\` = 98`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:07' WHERE \`id\` = 99`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:09' WHERE \`id\` = 100`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:11' WHERE \`id\` = 101`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:13' WHERE \`id\` = 102`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:21' WHERE \`id\` = 103`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:25' WHERE \`id\` = 104`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:17:28' WHERE \`id\` = 105`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-07 08:19:15' WHERE \`id\` = 106`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-08 18:00:20' WHERE \`id\` = 107`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-09 11:43:30' WHERE \`id\` = 108`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-09 17:48:34' WHERE \`id\` = 109`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-09 17:48:36' WHERE \`id\` = 110`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-09 17:48:38' WHERE \`id\` = 111`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-09 17:48:40' WHERE \`id\` = 112`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-09 17:48:43' WHERE \`id\` = 113`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-11 21:11:03' WHERE \`id\` = 114`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-11 21:17:22' WHERE \`id\` = 115`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-11 21:17:26' WHERE \`id\` = 116`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:44:07' WHERE \`id\` = 117`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:54:59' WHERE \`id\` = 118`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:55:32' WHERE \`id\` = 119`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:56:32' WHERE \`id\` = 120`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:56:48' WHERE \`id\` = 121`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:57:14' WHERE \`id\` = 122`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:58:41' WHERE \`id\` = 123`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:59:03' WHERE \`id\` = 124`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 15:59:26' WHERE \`id\` = 125`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 16:00:05' WHERE \`id\` = 126`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-18 16:00:12' WHERE \`id\` = 127`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-23 07:35:35' WHERE \`id\` = 128`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-23 07:35:38' WHERE \`id\` = 129`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-23 07:35:42' WHERE \`id\` = 130`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:00:05' WHERE \`id\` = 131`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:06:50' WHERE \`id\` = 132`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:06:54' WHERE \`id\` = 133`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:21' WHERE \`id\` = 134`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:27' WHERE \`id\` = 135`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:28' WHERE \`id\` = 136`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-24 17:14:30' WHERE \`id\` = 137`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:06:16' WHERE \`id\` = 138`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:27' WHERE \`id\` = 139`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:29' WHERE \`id\` = 140`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:30' WHERE \`id\` = 141`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:35' WHERE \`id\` = 142`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:36' WHERE \`id\` = 143`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:40' WHERE \`id\` = 144`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-27 09:44:42' WHERE \`id\` = 145`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-28 09:28:24' WHERE \`id\` = 146`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-28 09:34:30' WHERE \`id\` = 147`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-29 17:21:33' WHERE \`id\` = 148`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-29 17:21:35' WHERE \`id\` = 149`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 06:59:55' WHERE \`id\` = 150`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 07:05:14' WHERE \`id\` = 151`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 07:09:52' WHERE \`id\` = 152`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 07:15:43' WHERE \`id\` = 153`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 07:16:33' WHERE \`id\` = 154`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 07:21:07' WHERE \`id\` = 155`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-03-30 11:47:55' WHERE \`id\` = 156`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-01 06:56:20' WHERE \`id\` = 157`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-01 06:56:26' WHERE \`id\` = 158`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-01 06:56:29' WHERE \`id\` = 159`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-01 06:56:31' WHERE \`id\` = 160`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-01 06:58:10' WHERE \`id\` = 161`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-07 14:09:26' WHERE \`id\` = 162`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-07 14:09:28' WHERE \`id\` = 163`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-07 14:09:29' WHERE \`id\` = 164`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-07 14:09:32' WHERE \`id\` = 165`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-08 18:41:17' WHERE \`id\` = 166`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-08 18:41:20' WHERE \`id\` = 167`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-08 18:41:21' WHERE \`id\` = 168`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-08 18:41:23' WHERE \`id\` = 169`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-08 18:41:27' WHERE \`id\` = 170`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-08 18:58:15' WHERE \`id\` = 171`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:10:54' WHERE \`id\` = 172`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:10:56' WHERE \`id\` = 173`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:10:58' WHERE \`id\` = 174`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:11:01' WHERE \`id\` = 175`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:11:02' WHERE \`id\` = 176`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:11:04' WHERE \`id\` = 177`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-15 16:11:07' WHERE \`id\` = 178`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-17 16:54:39' WHERE \`id\` = 179`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-17 16:54:40' WHERE \`id\` = 180`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-17 16:54:42' WHERE \`id\` = 181`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-17 16:54:44' WHERE \`id\` = 182`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-18 08:58:42' WHERE \`id\` = 183`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-18 08:58:44' WHERE \`id\` = 184`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-20 17:44:36' WHERE \`id\` = 185`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-20 17:44:38' WHERE \`id\` = 186`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-20 17:44:40' WHERE \`id\` = 187`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-20 17:44:43' WHERE \`id\` = 188`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 06:30:13' WHERE \`id\` = 189`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 06:30:19' WHERE \`id\` = 190`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 06:30:21' WHERE \`id\` = 191`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 06:30:23' WHERE \`id\` = 192`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 06:30:24' WHERE \`id\` = 193`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 06:30:26' WHERE \`id\` = 194`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 18:41:55' WHERE \`id\` = 195`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 18:42:25' WHERE \`id\` = 196`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-25 18:46:01' WHERE \`id\` = 197`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-30 06:40:39' WHERE \`id\` = 198`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-30 06:40:41' WHERE \`id\` = 199`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-30 06:40:42' WHERE \`id\` = 200`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-30 06:40:45' WHERE \`id\` = 201`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-30 06:40:46' WHERE \`id\` = 202`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-04-30 06:40:50' WHERE \`id\` = 203`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:25:27' WHERE \`id\` = 204`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:25:29' WHERE \`id\` = 205`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:25:32' WHERE \`id\` = 206`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:28:14' WHERE \`id\` = 207`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:43:49' WHERE \`id\` = 208`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:43:51' WHERE \`id\` = 209`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:43:54' WHERE \`id\` = 210`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:43:56' WHERE \`id\` = 211`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:43:57' WHERE \`id\` = 212`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:43:59' WHERE \`id\` = 213`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:01' WHERE \`id\` = 214`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:02' WHERE \`id\` = 215`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:05' WHERE \`id\` = 216`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:10' WHERE \`id\` = 217`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:12' WHERE \`id\` = 218`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:14' WHERE \`id\` = 219`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-02 08:44:17' WHERE \`id\` = 220`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-03 09:00:04' WHERE \`id\` = 221`, + ) + await queryFn( + `UPDATE \`transactions\` SET creation_date = '2020-05-03 09:00:07' WHERE \`id\` = 222`, + ) + + // remove added transaction + await queryFn( + `DELETE FROM \`transactions\` + WHERE \`user_id\` = 275 AND \`balance\` IN (2000, 3000) AND \`amount\` = 1000`, + ) + + // rewind transaction to split + await queryFn( + `UPDATE \`transactions\` set \`amount\` = 3000, \`memo\` = 'Aktives Grundeinkommen für GL. Dez, Jan, Feb' + WHERE \`id\` = 150`, + ) + + await queryFn(`UPDATE \`transactions\` set \`previous\` = 150 WHERE \`id\` = 278`) +} diff --git a/database/migrations/0043-add_event_protocol_table.ts b/database/migrations/0043-add_event_protocol_table.ts new file mode 100644 index 000000000..c3669f857 --- /dev/null +++ b/database/migrations/0043-add_event_protocol_table.ts @@ -0,0 +1,28 @@ +/* MIGRATION TO ADD EVENT_PROTOCOL + * + * This migration adds the table `event_protocol` in order to store all sorts of business event data + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE IF NOT EXISTS \`event_protocol\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`type\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + \`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + \`user_id\` int(10) unsigned NOT NULL, + \`x_user_id\` int(10) unsigned NULL DEFAULT NULL, + \`x_community_id\` int(10) unsigned NULL DEFAULT NULL, + \`transaction_id\` int(10) unsigned NULL DEFAULT NULL, + \`contribution_id\` int(10) unsigned NULL DEFAULT NULL, + \`amount\` bigint(20) NULL DEFAULT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // write downgrade logic as parameter of queryFn + await queryFn(`DROP TABLE IF EXISTS \`event_protocol\`;`) +} diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index edb878040..d53ffebbe 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -1,80 +1,84 @@ -GRADIDO_LOG_PATH=/home/gradido/gradido/deployment/bare_metal/log - -# start script -DEPLOY_SEED_DATA=false - -# nginx -NGINX_REWRITE_LEGACY_URLS=true -NGINX_SSL=true -NGINX_SERVER_NAME=stage1.gradido.net -NGINX_SSL_CERTIFICATE=/etc/letsencrypt/live/stage1.gradido.net/fullchain.pem -NGINX_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/stage1.gradido.net/privkey.pem -NGINX_SSL_DHPARAM=/etc/letsencrypt/ssl-dhparams.pem -NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf -NGINX_UPDATE_PAGE_ROOT=/home/gradido/gradido/deployment/bare_metal/nginx/update-page - -# webhook -WEBHOOK_GITHUB_SECRET=secret -WEBHOOK_GITHUB_BRANCH=master - -# community -COMMUNITY_NAME="Gradido Development Stage1" -COMMUNITY_URL=https://stage1.gradido.net/ -COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register -COMMUNITY_REDEEM_URL=https://stage1.gradido.net/redeem/{code} -COMMUNITY_REDEEM_CONTRIBUTION_URL=https://stage1.gradido.net/redeem/CL-{code} -COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community" - -# backend -BACKEND_CONFIG_VERSION=v8.2022-06-20 - -JWT_EXPIRES_IN=10m -GDT_API_URL=https://gdt.gradido.net - -TYPEORM_LOGGING_RELATIVE_PATH=../deployment/bare_metal/log/typeorm.backend.log - -KLICKTIPP=false -KLICKTIPP_USER= -KLICKTIPP_PASSWORD= -KLICKTIPP_APIKEY_DE= -KLICKTIPP_APIKEY_EN= - -EMAIL=true -EMAIL_USERNAME=peter@lustig.de -EMAIL_SENDER=peter@lustig.de -EMAIL_PASSWORD=1234 -EMAIL_SMTP_URL=smtp.lustig.de -EMAIL_LINK_VERIFICATION=https://stage1.gradido.net/checkEmail/{optin}{code} -EMAIL_LINK_SETPASSWORD=https://stage1.gradido.net/reset-password/{optin} -EMAIL_LINK_FORGOTPASSWORD=https://stage1.gradido.net/forgot-password -EMAIL_LINK_OVERVIEW=https://stage1.gradido.net/overview -EMAIL_CODE_VALID_TIME=1440 -EMAIL_CODE_REQUEST_TIME=10 - -WEBHOOK_ELOPAGE_SECRET=secret - -# database -DATABASE_CONFIG_VERSION=v1.2022-03-18 - -# frontend -FRONTEND_CONFIG_VERSION=v2.2022-04-07 - -GRAPHQL_URI=https://stage1.gradido.net/graphql -ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} - -DEFAULT_PUBLISHER_ID=2896 - -META_URL=http://localhost -META_TITLE_DE="Gradido – Dein Dankbarkeitskonto" -META_TITLE_EN="Gradido - Your gratitude account" -META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle." -META_DESCRIPTION_EN="Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all." -META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem" -META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" -META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" - -# admin -ADMIN_CONFIG_VERSION=v1.2022-03-18 - -WALLET_AUTH_URL=https://stage1.gradido.net/authenticate?token={token} +GRADIDO_LOG_PATH=/home/gradido/gradido/deployment/bare_metal/log + +# start script +DEPLOY_SEED_DATA=false + +# nginx +NGINX_REWRITE_LEGACY_URLS=true +NGINX_SSL=true +NGINX_SERVER_NAME=stage1.gradido.net +NGINX_SSL_CERTIFICATE=/etc/letsencrypt/live/stage1.gradido.net/fullchain.pem +NGINX_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/stage1.gradido.net/privkey.pem +NGINX_SSL_DHPARAM=/etc/letsencrypt/ssl-dhparams.pem +NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf +NGINX_UPDATE_PAGE_ROOT=/home/gradido/gradido/deployment/bare_metal/nginx/update-page + +# webhook +WEBHOOK_GITHUB_SECRET=secret +WEBHOOK_GITHUB_BRANCH=master + +# community +COMMUNITY_NAME="Gradido Development Stage1" +COMMUNITY_URL=https://stage1.gradido.net/ +COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register +COMMUNITY_REDEEM_URL=https://stage1.gradido.net/redeem/{code} +COMMUNITY_REDEEM_CONTRIBUTION_URL=https://stage1.gradido.net/redeem/CL-{code} +COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community" + +# backend +BACKEND_CONFIG_VERSION=v9.2022-07-07 + +JWT_EXPIRES_IN=10m +GDT_API_URL=https://gdt.gradido.net + +TYPEORM_LOGGING_RELATIVE_PATH=../deployment/bare_metal/log/typeorm.backend.log + +KLICKTIPP=false +KLICKTIPP_USER= +KLICKTIPP_PASSWORD= +KLICKTIPP_APIKEY_DE= +KLICKTIPP_APIKEY_EN= + +EMAIL=true +EMAIL_USERNAME=peter@lustig.de +EMAIL_SENDER=peter@lustig.de +EMAIL_PASSWORD=1234 +EMAIL_SMTP_URL=smtp.lustig.de +EMAIL_LINK_VERIFICATION=https://stage1.gradido.net/checkEmail/{optin}{code} +EMAIL_LINK_SETPASSWORD=https://stage1.gradido.net/reset-password/{optin} +EMAIL_LINK_FORGOTPASSWORD=https://stage1.gradido.net/forgot-password +EMAIL_LINK_OVERVIEW=https://stage1.gradido.net/overview +EMAIL_CODE_VALID_TIME=1440 +EMAIL_CODE_REQUEST_TIME=10 + +WEBHOOK_ELOPAGE_SECRET=secret + +# EventProtocol +EVENT_PROTOCOL_DISABLED=false + + +# database +DATABASE_CONFIG_VERSION=v1.2022-03-18 + +# frontend +FRONTEND_CONFIG_VERSION=v2.2022-04-07 + +GRAPHQL_URI=https://stage1.gradido.net/graphql +ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} + +DEFAULT_PUBLISHER_ID=2896 + +META_URL=http://localhost +META_TITLE_DE="Gradido – Dein Dankbarkeitskonto" +META_TITLE_EN="Gradido - Your gratitude account" +META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle." +META_DESCRIPTION_EN="Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all." +META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem" +META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" +META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" + +# admin +ADMIN_CONFIG_VERSION=v1.2022-03-18 + +WALLET_AUTH_URL=https://stage1.gradido.net/authenticate?token={token} WALLET_URL=https://stage1.gradido.net/login \ No newline at end of file diff --git a/docu/Concepts/TechnicalRequirements/BusinessEventProtocol.md b/docu/Concepts/TechnicalRequirements/BusinessEventProtocol.md index 5a436d057..f8cf2cd03 100644 --- a/docu/Concepts/TechnicalRequirements/BusinessEventProtocol.md +++ b/docu/Concepts/TechnicalRequirements/BusinessEventProtocol.md @@ -6,29 +6,30 @@ With the business event protocol the gradido application will capture and persis The different event types will be defined as Enum. The following list is a first draft and will grow with further event types in the future. -| EventType | Value | Description | -| --------------------------- | ----- | ---------------------------------------------------------------------------------------------------- | -| BasicEvent | 0 | the basic event is the root of all further extending event types | -| VisitGradidoEvent | 10 | if a user visits a gradido page without login or register | -| RegisterEvent | 20 | the user presses the register button | -| RedeemRegisterEvent | 21 | the user presses the register button initiated by the redeem link | -| InActiveAccountEvent | 22 | the systems create an inactive account during the register process | -| SendConfirmEmailEvent | 23 | the system send a confirmation email to the user during the register process | -| ConfirmEmailEvent | 24 | the user confirms his email during the register process | -| RegisterEmailKlickTippEvent | 25 | the system registers the confirmed email at klicktipp | -| LoginEvent | 30 | the user presses the login button | -| RedeemLoginEvent | 31 | the user presses the login button initiated by the redeem link | -| ActivateAccountEvent | 32 | the system activates the users account during the first login process | -| PasswordChangeEvent | 33 | the user changes his password | -| TxSendEvent | 40 | the user creates a transaction and sends it online | -| TxSendRedeemEvent | 41 | the user creates a transaction and sends it per redeem link | -| TxRepeateRedeemEvent | 42 | the user recreates a redeem link of a still open transaction | -| TxCreationEvent | 50 | the user receives a creation transaction for his confirmed contribution | -| TxReceiveEvent | 51 | the user receives a transaction from an other user and posts the amount on his account | -| TxReceiveRedeemEvent | 52 | the user activates the redeem link and receives the transaction and posts the amount on his account | -| ContribCreateEvent | 60 | the user enters his contribution and asks for confirmation | -| ContribConfirmEvent | 61 | the user confirms a contribution of an other user (for future multi confirmation from several users) | -| | | | +| EventType | Value | Description | +| ----------------------------------- | ----- | ------------------------------------------------------------------------------------------------------ | +| BasicEvent | 0 | the basic event is the root of all further extending event types | +| VisitGradidoEvent | 10 | if a user visits a gradido page without login or register | +| RegisterEvent | 20 | the user presses the register button | +| RedeemRegisterEvent | 21 | the user presses the register button initiated by the redeem link | +| InActiveAccountEvent | 22 | the systems create an inactive account during the register process | +| SendConfirmEmailEvent | 23 | the system send a confirmation email to the user during the register process | +| ConfirmEmailEvent | 24 | the user confirms his email during the register process | +| RegisterEmailKlickTippEvent | 25 | the system registers the confirmed email at klicktipp | +| LoginEvent | 30 | the user presses the login button | +| RedeemLoginEvent | 31 | the user presses the login button initiated by the redeem link | +| ActivateAccountEvent | 32 | the system activates the users account during the first login process | +| PasswordChangeEvent | 33 | the user changes his password | +| TransactionSendEvent | 40 | the user creates a transaction and sends it online | +| TransactionSendRedeemEvent | 41 | the user creates a transaction and sends it per redeem link | +| TransactionRepeateRedeemEvent | 42 | the user recreates a redeem link of a still open transaction | +| TransactionCreationEvent | 50 | the user receives a creation transaction for his confirmed contribution | +| TransactionReceiveEvent | 51 | the user receives a transaction from an other user and posts the amount on his account | +| TransactionReceiveRedeemEvent | 52 | the user activates the redeem link and receives the transaction and posts the amount on his account | +| ContributionCreateEvent | 60 | the user enters his contribution and asks for confirmation | +| ContributionConfirmEvent | 61 | the user confirms a contribution of an other user (for future multi confirmation from several users) | +| ContributionLinkDefineEvent | 70 | the admin user defines a contributionLink, which could be send per Link/QR-Code on an other medium | +| ContributionLinkActivateRedeemEvent | 71 | the user activates a received contributionLink to create a contribution entry for the contributionLink | ## EventProtocol - Entity @@ -50,29 +51,30 @@ The business events will be stored in database in the new table `EventProtocol`. The following table lists for each event type the mandatory attributes, which have to be initialized at event occurence and to be written in the database event protocol table: -| EventType | id | type | createdAt | userID | XuserID | XCommunityID | transactionID | contribID | amount | -| :-------------------------- | :-: | :--: | :-------: | :----: | :-----: | :----------: | :-----------: | :-------: | :----: | -| BasicEvent | x | x | x | | | | | | | -| VisitGradidoEvent | x | x | x | | | | | | | -| RegisterEvent | x | x | x | x | | | | | | -| RedeemRegisterEvent | x | x | x | x | | | | | | -| InActiveAccountEvent | x | x | x | x | | | | | | -| SendConfirmEmailEvent | x | x | x | x | | | | | | -| ConfirmEmailEvent | x | x | x | x | | | | | | -| RegisterEmailKlickTippEvent | x | x | x | x | | | | | | -| LoginEvent | x | x | x | x | | | | | | -| RedeemLoginEvent | x | x | x | x | | | | | | -| ActivateAccountEvent | x | x | x | x | | | | | | -| PasswordChangeEvent | x | x | x | x | | | | | | -| TxSendEvent | x | x | x | x | x | x | x | | x | -| TxSendRedeemEvent | x | x | x | x | x | x | x | | x | -| TxRepeateRedeemEvent | x | x | x | x | x | x | x | | x | -| TxCreationEvent | x | x | x | x | | | x | | x | -| TxReceiveEvent | x | x | x | x | x | x | x | | x | -| TxReceiveRedeemEvent | x | x | x | x | x | x | x | | x | -| ContribCreateEvent | x | x | x | x | | | | x | | -| ContribConfirmEvent | x | x | x | x | x | x | | x | | -| | | | | | | | | | | +| EventType | id | type | createdAt | userID | XuserID | XCommunityID | transactionID | contribID | amount | +| :---------------------------------- | :-: | :--: | :-------: | :----: | :-----: | :----------: | :-----------: | :-------: | :----: | +| BasicEvent | x | x | x | | | | | | | +| VisitGradidoEvent | x | x | x | | | | | | | +| RegisterEvent | x | x | x | x | | | | | | +| RedeemRegisterEvent | x | x | x | x | | | (x) | (x) | | +| InActiveAccountEvent | x | x | x | x | | | | | | +| SendConfirmEmailEvent | x | x | x | x | | | | | | +| ConfirmEmailEvent | x | x | x | x | | | | | | +| RegisterEmailKlickTippEvent | x | x | x | x | | | | | | +| LoginEvent | x | x | x | x | | | | | | +| RedeemLoginEvent | x | x | x | x | | | (x) | (x) | | +| ActivateAccountEvent | x | x | x | x | | | | | | +| PasswordChangeEvent | x | x | x | x | | | | | | +| TransactionSendEvent | x | x | x | x | x | x | x | | x | +| TransactionSendRedeemEvent | x | x | x | x | x | x | x | | x | +| TransactionRepeateRedeemEvent | x | x | x | x | x | x | x | | x | +| TransactionCreationEvent | x | x | x | x | | | x | | x | +| TransactionReceiveEvent | x | x | x | x | x | x | x | | x | +| TransactionReceiveRedeemEvent | x | x | x | x | x | x | x | | x | +| ContributionCreateEvent | x | x | x | x | | | | x | x | +| ContributionConfirmEvent | x | x | x | x | x | x | | x | x | +| ContributionLinkDefineEvent | x | x | x | x | | | | | x | +| ContributionLinkActivateRedeemEvent | x | x | x | x | | | | x | x | ## Event creation diff --git a/frontend/src/components/Auth/AuthMobileStart.vue b/frontend/src/components/Auth/AuthMobileStart.vue index 09985dd77..690b8f895 100644 --- a/frontend/src/components/Auth/AuthMobileStart.vue +++ b/frontend/src/components/Auth/AuthMobileStart.vue @@ -6,7 +6,7 @@ {{ $t('auth.left.gratitude') }}
- {{ $t('auth.left.newCurrency') }} + {{ $t('auth.left.oneGratitude') }}
{ }) }) - describe('token is expired', () => { + describe('token is expired for several seconds', () => { beforeEach(() => { mocks.$store.state.tokenTime = setTokenTime(-60) wrapper = Wrapper() }) + it('has value for remaining seconds equal 0', () => { + expect(wrapper.tokenExpiresInSeconds === 0) + }) + it('emits logout', () => { expect(wrapper.emitted('logout')).toBeTruthy() }) diff --git a/frontend/src/components/SessionLogoutTimeout.vue b/frontend/src/components/SessionLogoutTimeout.vue index 1e5a27998..1ebff752a 100644 --- a/frontend/src/components/SessionLogoutTimeout.vue +++ b/frontend/src/components/SessionLogoutTimeout.vue @@ -65,7 +65,7 @@ export default { this.$timer.restart('tokenExpires') this.$bvModal.show('modalSessionTimeOut') } - if (this.tokenExpiresInSeconds <= 0) { + if (this.tokenExpiresInSeconds === 0) { this.$timer.stop('tokenExpires') this.$emit('logout') } @@ -90,7 +90,10 @@ export default { }, computed: { tokenExpiresInSeconds() { - return Math.floor((new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000) + const remainingSecs = Math.floor( + (new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000, + ) + return remainingSecs <= 0 ? 0 : remainingSecs }, }, beforeDestroy() { diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index adcd653a4..27e63d568 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -13,6 +13,7 @@ export const login = gql` hasElopage publisherId isAdmin + creation } } ` @@ -30,6 +31,7 @@ export const verifyLogin = gql` hasElopage publisherId isAdmin + creation } } ` diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index fec24b1dc..378dcc919 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -12,10 +12,9 @@ "hasAccount": "Du hast schon einen Account?", "hereLogin": "Hier Anmelden", "learnMore": "Erfahre mehr …", - "newCurrency": "Die neue Währung", "oneDignity": "Wir schenken einander und danken mit Gradido.", "oneDonation": "Du bist ein Geschenk für die Gemeinschaft. 1000 Dank, weil du bei uns bist.", - "oneGratitude": "Die neue Währung. Für einander, für alle Menschen, für die Natur." + "oneGratitude": "Für einander, für alle Menschen, für die Natur." }, "navbar": { "aboutGradido": "Über Gradido" diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index a0ef6723d..0bfde65ed 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -12,10 +12,9 @@ "hasAccount": "You already have an account?", "hereLogin": "Log in here", "learnMore": "Learn more …", - "newCurrency": "The new currency", "oneDignity": "We gift to each other and give thanks with Gradido.", "oneDonation": "You are a gift for the community. 1000 thanks because you are with us.", - "oneGratitude": "The new currency. For each other, for all people, for nature." + "oneGratitude": "For each other, for all people, for nature." }, "navbar": { "aboutGradido": "About Gradido" diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index e95eec7b9..8fdbc519e 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -47,6 +47,9 @@ export const mutations = { hasElopage: (state, hasElopage) => { state.hasElopage = hasElopage }, + creation: (state, creation) => { + state.creation = creation + }, } export const actions = { @@ -60,6 +63,7 @@ export const actions = { commit('hasElopage', data.hasElopage) commit('publisherId', data.publisherId) commit('isAdmin', data.isAdmin) + commit('creation', data.creation) }, logout: ({ commit, state }) => { commit('token', null) @@ -71,6 +75,7 @@ export const actions = { commit('hasElopage', false) commit('publisherId', null) commit('isAdmin', false) + commit('creation', null) localStorage.clear() }, } @@ -96,6 +101,7 @@ try { newsletterState: null, hasElopage: false, publisherId: null, + creation: null, }, getters: {}, // Syncronous mutation of the state diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index 3f942fa35..651a3ccc5 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -30,6 +30,7 @@ const { publisherId, isAdmin, hasElopage, + creation, } = mutations const { login, logout } = actions @@ -139,6 +140,14 @@ describe('Vuex store', () => { expect(state.hasElopage).toBeTruthy() }) }) + + describe('creation', () => { + it('sets the state of creation', () => { + const state = { creation: null } + creation(state, true) + expect(state.creation).toEqual(true) + }) + }) }) describe('actions', () => { @@ -156,11 +165,12 @@ describe('Vuex store', () => { hasElopage: false, publisherId: 1234, isAdmin: true, + creation: ['1000', '1000', '1000'], } it('calls nine commits', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenCalledTimes(8) + expect(commit).toHaveBeenCalledTimes(9) }) it('commits email', () => { @@ -202,6 +212,11 @@ describe('Vuex store', () => { login({ commit, state }, commitedData) expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', true) }) + + it('commits creation', () => { + login({ commit, state }, commitedData) + expect(commit).toHaveBeenNthCalledWith(9, 'creation', ['1000', '1000', '1000']) + }) }) describe('logout', () => { @@ -210,7 +225,7 @@ describe('Vuex store', () => { it('calls nine commits', () => { logout({ commit, state }) - expect(commit).toHaveBeenCalledTimes(8) + expect(commit).toHaveBeenCalledTimes(9) }) it('commits token', () => { @@ -253,6 +268,11 @@ describe('Vuex store', () => { expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', false) }) + it('commits creation', () => { + logout({ commit, state }) + expect(commit).toHaveBeenNthCalledWith(9, 'creation', null) + }) + // how to get this working? it.skip('calls localStorage.clear()', () => { const clearStorageMock = jest.fn()