mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into fix-balance-migration
This commit is contained in:
commit
2c3cac5939
@ -74,7 +74,10 @@ export class TransactionLinkResolver {
|
||||
const holdAvailableAmount = amount.minus(calculateDecay(amount, createdDate, validUntil).decay)
|
||||
|
||||
// validate amount
|
||||
await calculateBalance(user.id, holdAvailableAmount, createdDate)
|
||||
const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate)
|
||||
if (!sendBalance) {
|
||||
throw new Error("user hasn't enough GDD or amount is < 0")
|
||||
}
|
||||
|
||||
const transactionLink = dbTransactionLink.create()
|
||||
transactionLink.userId = user.id
|
||||
|
||||
@ -16,7 +16,7 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { EventProtocol } from '@entity/EventProtocol'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { User } from '@entity/User'
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { findUserByEmail } from './UserResolver'
|
||||
@ -253,50 +253,21 @@ describe('send coins', () => {
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError(`User has not received any GDD yet`)],
|
||||
errors: [new GraphQLError(`user hasn't enough GDD or amount is < 0`)],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
`No prior transaction found for user with id: ${user[1].id}`,
|
||||
`user hasn't enough GDD or amount is < 0 : balance=null`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sending negative amount', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
email: 'peter@lustig.de',
|
||||
amount: -50,
|
||||
memo: 'testing negative',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Transaction amount must be greater than 0')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Transaction amount must be greater than 0: -50')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('user has some GDD', () => {
|
||||
beforeAll(async () => {
|
||||
resetToken()
|
||||
|
||||
// login as bob again
|
||||
await query({ mutation: login, variables: bobData })
|
||||
|
||||
// create contribution as user bob
|
||||
const contribution = await mutate({
|
||||
mutation: createContribution,
|
||||
@ -316,6 +287,37 @@ describe('send coins', () => {
|
||||
await query({ mutation: login, variables: bobData })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
/*
|
||||
describe('trying to send negative amount', () => {
|
||||
it('throws an error', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
email: 'peter@lustig.de',
|
||||
amount: -50,
|
||||
memo: 'testing negative',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError(`user hasn't enough GDD or amount is < 0`)],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
`user hasn't enough GDD or amount is < 0 : balance=null`,
|
||||
)
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
describe('good transaction', () => {
|
||||
it('sends the coins', async () => {
|
||||
expect(
|
||||
|
||||
@ -39,7 +39,6 @@ import { findUserByEmail } from './UserResolver'
|
||||
import { sendTransactionLinkRedeemedEmail } from '@/mailer/sendTransactionLinkRedeemed'
|
||||
import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event'
|
||||
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
||||
import { Decay } from '../model/Decay'
|
||||
|
||||
export const executeTransaction = async (
|
||||
amount: Decimal,
|
||||
@ -69,8 +68,17 @@ export const executeTransaction = async (
|
||||
|
||||
// validate amount
|
||||
const receivedCallDate = new Date()
|
||||
|
||||
const sendBalance = await calculateBalance(sender.id, amount, receivedCallDate, transactionLink)
|
||||
const sendBalance = await calculateBalance(
|
||||
sender.id,
|
||||
amount.mul(-1),
|
||||
receivedCallDate,
|
||||
transactionLink,
|
||||
)
|
||||
logger.debug(`calculated Balance=${sendBalance}`)
|
||||
if (!sendBalance) {
|
||||
logger.error(`user hasn't enough GDD or amount is < 0 : balance=${sendBalance}`)
|
||||
throw new Error("user hasn't enough GDD or amount is < 0")
|
||||
}
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
@ -100,24 +108,7 @@ export const executeTransaction = async (
|
||||
transactionReceive.userId = recipient.id
|
||||
transactionReceive.linkedUserId = sender.id
|
||||
transactionReceive.amount = amount
|
||||
|
||||
// state received balance
|
||||
let receiveBalance: {
|
||||
balance: Decimal
|
||||
decay: Decay
|
||||
lastTransactionId: number
|
||||
} | null
|
||||
|
||||
// try received balance
|
||||
try {
|
||||
receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate)
|
||||
} catch (e) {
|
||||
logger.info(
|
||||
`User with no transactions sent: ${recipient.id}, has received a transaction of ${amount} GDD from user: ${sender.id}`,
|
||||
)
|
||||
receiveBalance = null
|
||||
}
|
||||
|
||||
const receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate)
|
||||
transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount
|
||||
transactionReceive.balanceDate = receivedCallDate
|
||||
transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0)
|
||||
|
||||
@ -1,17 +1,5 @@
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
export const objectValuesToArray = (obj: { [x: string]: string }): Array<string> => {
|
||||
return Object.keys(obj).map(function (key) {
|
||||
return obj[key]
|
||||
})
|
||||
}
|
||||
|
||||
// to improve code readability, as String is needed, it is handled inside this utility function
|
||||
export const decimalAddition = (a: Decimal, b: Decimal): Decimal => {
|
||||
return a.add(b.toString())
|
||||
}
|
||||
|
||||
// to improve code readability, as String is needed, it is handled inside this utility function
|
||||
export const decimalSubtraction = (a: Decimal, b: Decimal): Decimal => {
|
||||
return a.minus(b.toString())
|
||||
}
|
||||
|
||||
@ -5,8 +5,6 @@ import { Decay } from '@model/Decay'
|
||||
import { getCustomRepository } from '@dbTools/typeorm'
|
||||
import { TransactionLinkRepository } from '@repository/TransactionLink'
|
||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||
import { decimalSubtraction, decimalAddition } from './utilities'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
function isStringBoolean(value: string): boolean {
|
||||
const lowerValue = value.toLowerCase()
|
||||
@ -25,26 +23,13 @@ async function calculateBalance(
|
||||
amount: Decimal,
|
||||
time: Date,
|
||||
transactionLink?: dbTransactionLink | null,
|
||||
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number }> {
|
||||
// negative or empty amount should not be allowed
|
||||
if (amount.lessThanOrEqualTo(0)) {
|
||||
logger.error(`Transaction amount must be greater than 0: ${amount}`)
|
||||
throw new Error('Transaction amount must be greater than 0')
|
||||
}
|
||||
|
||||
// check if user has prior transactions
|
||||
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> {
|
||||
const lastTransaction = await Transaction.findOne({ userId }, { order: { balanceDate: 'DESC' } })
|
||||
|
||||
if (!lastTransaction) {
|
||||
logger.error(`No prior transaction found for user with id: ${userId}`)
|
||||
throw new Error('User has not received any GDD yet')
|
||||
}
|
||||
if (!lastTransaction) return null
|
||||
|
||||
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
|
||||
|
||||
// new balance is the old balance minus the amount used
|
||||
const balance = decimalSubtraction(decay.balance, amount)
|
||||
|
||||
const balance = decay.balance.add(amount.toString())
|
||||
const transactionLinkRepository = getCustomRepository(TransactionLinkRepository)
|
||||
const { sumHoldAvailableAmount } = await transactionLinkRepository.summary(userId, time)
|
||||
|
||||
@ -52,16 +37,11 @@ async function calculateBalance(
|
||||
// else we cannot redeem links which are more or equal to half of what an account actually owns
|
||||
const releasedLinkAmount = transactionLink ? transactionLink.holdAvailableAmount : new Decimal(0)
|
||||
|
||||
const availableBalance = decimalSubtraction(balance, sumHoldAvailableAmount)
|
||||
|
||||
if (decimalAddition(availableBalance, releasedLinkAmount).lessThan(0)) {
|
||||
logger.error(
|
||||
`Not enough funds for a transaction of ${amount} GDD, user with id: ${userId} has only ${balance} GDD available`,
|
||||
)
|
||||
throw new Error('Not enough funds for transaction')
|
||||
if (
|
||||
balance.minus(sumHoldAvailableAmount.toString()).plus(releasedLinkAmount.toString()).lessThan(0)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
logger.debug(`calculated Balance=${balance}`)
|
||||
return { balance, lastTransactionId: lastTransaction.id, decay }
|
||||
}
|
||||
|
||||
|
||||
167
docu/RoadMap_2022-2023.md
Normal file
167
docu/RoadMap_2022-2023.md
Normal file
@ -0,0 +1,167 @@
|
||||
# Roadmap 2022 / 2023
|
||||
|
||||
## unsortierte Sammlung von Themen
|
||||
|
||||
1. backend access layer
|
||||
|
||||
- Refactoring der Resolver-Klassen
|
||||
- Daten-Zugriffschicht zur Kapselung der DB-Schicht
|
||||
- Transfer-Datenmodel zum Austausch von Daten zwischen den Schichten
|
||||
- technisches Transaktion-Handling und Lösung von Deadlocks
|
||||
- Konzept in Arbeit
|
||||
2. capturing alias
|
||||
|
||||
- Konzept fertig
|
||||
- Änderungen in Register- und Login-Prozess
|
||||
3. Passwort-Verschlüsselung: Refactoring
|
||||
|
||||
- Konzept aufteilen in Ausbaustufen
|
||||
- Altlasten entsorgen
|
||||
- Versionierung/Typisierung der verwendeten Verschlüsselungslogik notwendig
|
||||
- DB-Migration auf encryptionType=EMAIL
|
||||
4. Passwort-Verschlüsselung: Login mit impliziter Neuverschlüsselung
|
||||
|
||||
* Logik der Passwortverschlüsselung auf GradidoID einführen
|
||||
* bei Login mit encryptionType=Email oder OneTime triggern einer Neuverschlüsselung per GradidoID
|
||||
* Unabhängigkeit von Email erzeugen
|
||||
* Änderung der User-Email ermöglichen
|
||||
5. Contribution-Categories
|
||||
|
||||
- Bewertung und Kategorisierung von Schöpfungen: Was hat Wer für Wen geleistet?
|
||||
- Regeln auf Categories ermöglichen
|
||||
- Konzept in Arbeit
|
||||
6. Statistics / Analysen
|
||||
7. Contribution-Link editieren
|
||||
8. User-Tagging
|
||||
|
||||
- Eine UserTag dient zur einfachen Gruppierung gleichgesinnter oder örtlich gebundener User
|
||||
- Motivation des User-Taggings: bilden kleinerer lokaler User-Gruppen und jeder kennt jeden
|
||||
- Einführung einer UserTaggings-Tabelle und eine User-UserTaggings-Zuordnungs-Tabelle
|
||||
- Ein Moderator kann im AdminInterface die Liste der UserTags pflegen
|
||||
|
||||
- neues TAG anlegen
|
||||
- vorhandenes TAG umbenennen
|
||||
- ein TAG löschen, sofern kein User mehr diesem TAG zugeordnet ist
|
||||
- Will ein User ein TAG zugeordnet werden, so kann dies nur ein Moderator im AdminInterface tun
|
||||
- Ein Moderator kann im AdminInterface
|
||||
|
||||
- ein TAG einem User zuordnen
|
||||
- ein TAG von einem User entfernen
|
||||
- wichtige UseCases:
|
||||
|
||||
- Zuordnung eines Users zu einem TAG durch einen Moderator
|
||||
- TAG spezifische Schöpfung
|
||||
- User muss für seinen Beitrag ein TAG auswählen können, dem er zuvor zugeordnet wurde
|
||||
- TAG-Moderator kann den Beitrag bestätigen, weil er den User mit dem TAG (persönlich) kennt
|
||||
9. User-Beziehungen und Favoritenverwaltung
|
||||
|
||||
- User-User-Zuordnung
|
||||
- aus Tx-Liste die aktuellen Favoriten ermitteln
|
||||
- Verwaltung von Zuordnungen
|
||||
- Auswahl
|
||||
- Berechtigungen
|
||||
- Gruppierung
|
||||
- Community-übergreifend
|
||||
- User-Beziehungen
|
||||
10. technische Ablösung der Email und Ersatz durch GradidoID
|
||||
|
||||
* APIs / Links / etc mit Email anpassen, so dass keine Email mehr verwendet wird
|
||||
* Email soll aber im Aussen für User optional noch verwendbar bleiben
|
||||
* Intern erfolgt aber auf jedenfall ein Mapping auf GradidoID egal ob per Email oder Alias angefragt wird
|
||||
11. Zeitzone
|
||||
|
||||
- User sieht immer seine Locale-Zeit und Monate
|
||||
- Admin sieht immer UTC-Zeit und Monate
|
||||
- wichtiges Kriterium für Schöpfung ist das TargetDate ( heißt in DB contributionDate)
|
||||
- Berechnung der möglichen Schöpfungen muss somit auf dem TargetDate der Schöpfung ermittelt werden! **(Ist-Zustand)**
|
||||
- Kann es vorkommen, dass das TargetDate der Contribution vor dem CreationDate der TX liegt? Ja
|
||||
- Beispiel: User in Tokyo Locale mit Offest +09:00
|
||||
|
||||
- aktiviert Contribution-Link mit Locale: 01.11.2022 07:00:00+09:00 = TargetDate = Zieldatum der Schöpfung
|
||||
- die Contribution wird gespeichert mit
|
||||
|
||||
- creationDate=31.10.2022 22:00:00 UTC
|
||||
- contributionDate=01.11.2022 07:00:00
|
||||
- (neu) clientRequestTime=01.11.2022 07:00:00+09:00
|
||||
- durch automatische Bestätigung und sofortiger Transaktion wird die TX gespeichert mit
|
||||
|
||||
- creationDate=31.10.2022 22:00:00 UTC
|
||||
- **zwingende Prüfung aller Requeste: auf -12h <= ClientRequestTime <= +12h**
|
||||
|
||||
- Prüfung auf Sommerzeiten und exotische Länder beachten
|
||||
-
|
||||
- zur Analyse und Problemverfolgung von Contributions immer original ClientRequestTime mit Offset in DB speichern
|
||||
- Beispiel für täglichen Contribution-Link während des Monats:
|
||||
|
||||
- 17.10.2022 22:00 +09:00 => 17.10.2022 UTC: 17.10.2022 13:00 UTC => 17.10.2022
|
||||
- 18.10.2022 02:00 +09:00 => 18.10.2022 UTC: 17.10.2022 17:00 UTC => 17.10.2022 !!!! darf nicht weil gleicher Tag !!!
|
||||
- Beispiel für täglichen Contribution-Link am Monatswechsel:
|
||||
|
||||
- 31.10.2022 22:00 +09:00 => 31.10.2022 UTC: 31.10.2022 15:00 UTC => 31.10.2022
|
||||
- 01.11.2022 07:00 +09:00 => 01.11.2022 UTC: 31.10.2022 22:00 UTC => 31.10.2022 !!!! darf nicht weil gleicher Tag !!!
|
||||
12. Layout
|
||||
13. Lastschriften-Link
|
||||
14. Registrierung mit Redeem-Link:
|
||||
|
||||
* bei inaktivem Konto, sprich bisher noch keine Email-Bestätigung, keine Buchung möglich
|
||||
* somit speichern des Links zusammen mit OptIn-Code
|
||||
* damit kann in einem Resend der ConfirmationEmail der Link auch korrekt wieder mitgeliefert werden
|
||||
15. Manuelle User-Registrierung für Admin
|
||||
|
||||
- soll am 10.12.2022 für den Tag bei den Galliern produktiv sein
|
||||
16. Dezentralisierung / Federation
|
||||
|
||||
- Hyperswarm
|
||||
|
||||
- funktioniert schon im Prototyp
|
||||
- alle Instanzen finden sich gegenseitig
|
||||
- ToDo:
|
||||
- Infos aus HyperSwarm in der Community speichern
|
||||
- Prüfung ob neue mir noch unbekannte Community hinzugekommen ist?
|
||||
- Triggern der Authentifizierungs- und Autorisierungs-Handshake für neue Community
|
||||
- Authentifizierungs- und Autorisierungs-Handshake
|
||||
- Inter-Community-Communication
|
||||
- **ToDos**:
|
||||
|
||||
- DB-Migration für Community-Tabelle, User-Community-Zuordnungen, UserRights-Tabelle
|
||||
- Berechtigungen für Communities
|
||||
- Register- und Login-Prozess für Community-Anmeldung anpassen
|
||||
|
||||
- Auswahl-Box einer Community
|
||||
- createUser mit Zuordnung zur ausgewählten Community
|
||||
- Schöpfungsprozess auf angemeldete Community anpassen
|
||||
|
||||
- "Beitrag einreichen"-Dialog auf angemeldete Community anpassen
|
||||
- "meine Beiträge zum Gemeinwohl" mit Filter auf angemeldete Community anpassen
|
||||
- "Gemeinschaft"-Dialog auf angemeldete Community anpassen
|
||||
- "Mein Profil"-Dialog auf Communities anpassen
|
||||
|
||||
- Umzug-Service in andere Community
|
||||
- Löschen der Mitgliedschaft zu angemeldeter Community (Deaktivierung der Zuordnung "User-Community")
|
||||
- "Senden"-Dialog mit Community-Auswahl
|
||||
- "Transaktion"-Dialog mit Filter auf angemeldeter Community
|
||||
- AdminInterface auf angemeldete Community anpassen
|
||||
|
||||
- "Übersicht"-Dialog mit Filter auf angemeldete Community
|
||||
- "Nutzersuche"-Dialog mit Filter auf angemeldete Community
|
||||
- "Mehrfachschöpfung"-Dialog mit Filter auf angemeldete Comunity
|
||||
- Subject/Texte/Footer/... der Email-Benachrichtigungen auf angemeldete Community anpassen
|
||||
|
||||
## Priorisierung
|
||||
|
||||
1. Contribution-Link editieren (vlt schon im vorherigen Bugfix-Release Ende Okt. 2022 fertig)
|
||||
2. Passwort-Verschlüsselung: Refactoring **Konzeption fertig!!**!
|
||||
3. Manuelle User-Registrierung für Admin (10.12.2022) **Konzeption ongoing!!**!
|
||||
4. Passwort-Verschlüsselung: implizite Login-Neuverschlüsselung **Konzeption fertig!!**!
|
||||
5. Layout
|
||||
6. Zeitzone
|
||||
7. Dezentralisierung / Federation
|
||||
8. capturing alias **Konzeption fertig!!**!
|
||||
9. Registrierung mit Redeem-Link: bei inaktivem Konto keine Buchung möglich
|
||||
10. Subgruppierung / User-Tagging (einfacher Ansatz)
|
||||
11. backend access layer
|
||||
12. technische Ablösung der Email und Ersatz durch GradidoID
|
||||
13. User-Beziehungen und Favoritenverwaltung
|
||||
14. Lastschriften-Link
|
||||
15. Contribution-Categories
|
||||
16. Statistics / Analysen
|
||||
60
docu/graphics/RoadMap2022-2023.drawio
Normal file
60
docu/graphics/RoadMap2022-2023.drawio
Normal file
@ -0,0 +1,60 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="CdUoMVivL2xThNJutTjM" name="Seite-1">
|
||||
<mxGraphModel dx="1022" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2336" pageHeight="1654" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="14" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=16;" edge="1" parent="1" source="2" target="7">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="160" y="100"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="2" value="capturing alias" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="40" width="240" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="Manuelle User-Registrierung für Admin (10.12.2022)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="200" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="4" value="Zeitzone" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="280" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="5" value="User-Beziehungen und Favoritenverwaltung" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="360" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="Layout" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="440" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="15" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=16;" edge="1" parent="1" source="7" target="12">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="440" y="140"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="Passwort-Verschlüsselung" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="80" width="240" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" value="Subgruppierung / Subcommunities" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="520" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="Contribution-Categories" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="600" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="backend access layer" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="680" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="Statistics / Analysen" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="760" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="Ablösung der Email und Ersatz durch GradidoID" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="600" y="120" width="360" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="Dezentralisierung / Federation" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;gradientColor=#b3b3b3;strokeColor=#666666;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="840" width="440" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
docu/graphics/RoadMap2022-2023.png
Normal file
BIN
docu/graphics/RoadMap2022-2023.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Loading…
x
Reference in New Issue
Block a user