Merge branch '3211_edit_contribution_memo_by_admin' of github.com:gradido/gradido into 3211_edit_contribution_memo_by_admin

This commit is contained in:
einhornimmond 2023-11-17 08:32:30 +01:00
commit bbeb9f0bec
26 changed files with 148 additions and 86 deletions

View File

@ -2,24 +2,23 @@
ROOT_DIR=$(dirname "$0")/..
tmp=$(mktemp)
exit_code=0
for locale_file in $ROOT_DIR/src/locales/*.json
do
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
jq -M 'to_entries | sort_by(.key) | from_entries' "$locale_file" > tmp.json
if [ "$*" == "--fix" ]
then
mv "$tmp" $locale_file
mv tmp.json "$locale_file"
else
if diff -q "$tmp" $locale_file > /dev/null ;
if ! diff -q tmp.json "$locale_file" > /dev/null ;
then
: # all good
else
exit_code=$?
echo "$(basename -- $locale_file) is not sorted by keys"
exit_code=1
echo "$(basename -- "$locale_file") is not sorted by keys"
fi
fi
done
rm -f tmp.json
exit $exit_code

View File

@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils'
import ContributionMessagesFormular from './ContributionMessagesFormular'
import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup'
import { adminCreateContributionMessage } from '@/graphql/adminCreateContributionMessage'
import { adminUpdateContribution } from '@/graphql/adminUpdateContribution'
const localVue = global.localVue
@ -136,6 +137,32 @@ describe('ContributionMessagesFormular', () => {
})
})
describe('update contribution memo from moderator for user created contributions', () => {
beforeEach(async () => {
await wrapper.setData({
form: {
memo: 'changed memo',
},
chatOrMemo: 1,
})
await wrapper.find('button[data-test="submit-dialog"]').trigger('click')
})
it('adminUpdateContribution was called with contributionId and updated memo', () => {
expect(apolloMutateMock).toBeCalledWith({
mutation: adminUpdateContribution,
variables: {
id: 42,
memo: 'changed memo',
},
})
})
it('toasts an success message', () => {
expect(toastSuccessSpy).toBeCalledWith('message.request')
})
})
describe('send contribution message with error', () => {
beforeEach(async () => {
apolloMutateMock.mockRejectedValue({ message: 'OUCH!' })

View File

@ -64,15 +64,7 @@
</template>
<script>
import { adminCreateContributionMessage } from '@/graphql/adminCreateContributionMessage'
import gql from 'graphql-tag'
export const adminUpdateContribution = gql`
mutation ($id: Int!, $memo: String!) {
adminUpdateContribution(id: $id, memo: $memo) {
memo
}
}
`
import { adminUpdateContribution } from '@/graphql/adminUpdateContribution'
export default {
name: 'ContributionMessagesFormular',
@ -148,7 +140,7 @@ export default {
},
onReset(event) {
this.form.text = ''
this.form.memo = this.contributionMemo
this.form.memo = this.contributionMemo
},
enableMemo() {
this.chatOrMemo = 1

View File

@ -134,5 +134,26 @@ describe('ContributionMessagesList', () => {
expect(wrapper.findComponent({ name: 'ContributionMessagesFormular' }).exists()).toBe(true)
})
})
describe('call updateStatus', () => {
beforeEach(() => {
wrapper.vm.updateStatus(4)
})
it('emits update-status', () => {
expect(wrapper.vm.$root.$emit('update-status', 4)).toBeTruthy()
})
})
describe('test reload-contribution', () => {
beforeEach(() => {
wrapper.vm.reloadContribution(3)
})
it('emits reload-contribution', () => {
expect(wrapper.emitted('reload-contribution')).toBeTruthy()
expect(wrapper.emitted('reload-contribution')[0]).toEqual([3])
})
})
})
})

View File

@ -140,5 +140,16 @@ describe('OpenCreationsTable', () => {
expect(wrapper.vm.$root.$emit('update-status', 4)).toBeTruthy()
})
})
describe('test reload-contribution', () => {
beforeEach(() => {
wrapper.vm.reloadContribution(3)
})
it('emits reload-contribution', () => {
expect(wrapper.emitted('reload-contribution')).toBeTruthy()
expect(wrapper.emitted('reload-contribution')[0]).toEqual([3])
})
})
})
})

View File

@ -1,7 +1,7 @@
import gql from 'graphql-tag'
export const adminUpdateContribution = gql`
mutation ($id: Int!, $amount: Decimal!, $memo: String!, $creationDate: String!) {
mutation ($id: Int!, $amount: Decimal, $memo: String, $creationDate: String) {
adminUpdateContribution(id: $id, amount: $amount, memo: $memo, creationDate: $creationDate) {
amount
date

View File

@ -24,4 +24,4 @@ export const getContribution = gql`
userId
}
}
`
`

View File

@ -1,4 +1,5 @@
{
"GDD": "GDD",
"all_emails": "Alle Nutzer",
"back": "zurück",
"change_user_role": "Nutzerrolle ändern",
@ -42,6 +43,7 @@
"createdAt": "Angelegt",
"creation": "Schöpfung",
"creationList": "Schöpfungsliste",
"creation_for_month": "Schöpfung für Monat",
"creation_form": {
"creation_for": "Aktives Grundeinkommen für",
"enter_text": "Text eintragen",
@ -58,16 +60,15 @@
"toasted_update": "`Offene Schöpfung {value} GDD) für {email} wurde geändert und liegt zur Bestätigung bereit",
"update_creation": "Schöpfung aktualisieren"
},
"creation_for_month": "Schöpfung für Monat",
"delete": "Löschen",
"delete_user": "Nutzer löschen",
"deleted": "gelöscht",
"deleted_user": "Alle gelöschten Nutzer",
"delete_user": "Nutzer löschen",
"deny": "Ablehnen",
"e_mail": "E-Mail",
"enabled": "aktiviert",
"error": "Fehler",
"expired": "abgelaufen",
"e_mail": "E-Mail",
"federation": {
"createdAt": "Erstellt am",
"gradidoInstances": "Gradido Instanzen",
@ -88,7 +89,6 @@
"cancel": "Abbrechen",
"submit": "Senden"
},
"GDD": "GDD",
"help": {
"help": "Hilfe",
"transactionlist": {
@ -127,9 +127,9 @@
"statistic": "Statistik",
"user_search": "Nutzersuche"
},
"not_open_creations": "Keine offenen Schöpfungen",
"no_hashtag": "#Hashtags verbergen",
"no_hashtag_tooltip": "Zeigt nur Beiträge ohne Hashtag im Text",
"not_open_creations": "Keine offenen Schöpfungen",
"open": "offen",
"open_creations": "Offene Schöpfungen",
"overlay": {
@ -200,7 +200,6 @@
"title": "Alle geschöpften Transaktionen für den Nutzer"
},
"undelete_user": "Nutzer wiederherstellen",
"unregistered_emails": "Nur unregistrierte Nutzer",
"unregister_mail": {
"button": "Registrierungs-Email bestätigen, jetzt senden",
"error": "Fehler beim Senden des Bestätigungs-Links an den Benutzer: {message}",
@ -210,6 +209,7 @@
"text_false": " Die letzte Email wurde am {date} Uhr an das Mitglied ({email}) gesendet.",
"text_true": " Die Email wurde bestätigt."
},
"unregistered_emails": "Nur unregistrierte Nutzer",
"userRole": {
"notChangeYourSelf": "Als Admin/Moderator kannst du nicht selber deine Rolle ändern.",
"selectLabel": "Rolle:",

View File

@ -1,4 +1,5 @@
{
"GDD": "GDD",
"all_emails": "All users",
"back": "back",
"change_user_role": "Change user role",
@ -42,6 +43,7 @@
"createdAt": "Created at",
"creation": "Creation",
"creationList": "Creation list",
"creation_for_month": "Creation for month",
"creation_form": {
"creation_for": "Active Basic Income for",
"enter_text": "Enter text",
@ -58,16 +60,15 @@
"toasted_update": "Open creation {value} GDD) for {email} has been changed and is ready for confirmation.",
"update_creation": "Creation update"
},
"creation_for_month": "Creation for month",
"delete": "Delete",
"delete_user": "Delete user",
"deleted": "deleted",
"deleted_user": "All deleted user",
"delete_user": "Delete user",
"deny": "Reject",
"e_mail": "E-mail",
"enabled": "enabled",
"error": "Error",
"expired": "expired",
"e_mail": "E-mail",
"federation": {
"createdAt": "Created At ",
"gradidoInstances": "Gradido Instances",
@ -88,7 +89,6 @@
"cancel": "Cancel",
"submit": "Send"
},
"GDD": "GDD",
"help": {
"help": "Help",
"transactionlist": {
@ -127,9 +127,9 @@
"statistic": "Statistic",
"user_search": "User search"
},
"not_open_creations": "No open creations",
"no_hashtag": "Hide #hashtags",
"no_hashtag_tooltip": "Shows only contributions without hashtag in text",
"not_open_creations": "No open creations",
"open": "open",
"open_creations": "Open creations",
"overlay": {
@ -200,7 +200,6 @@
"title": "All creation-transactions for the user"
},
"undelete_user": "Undelete User",
"unregistered_emails": "Only unregistered users",
"unregister_mail": {
"button": "Confirm registration email, send now",
"error": "Error sending the confirmation link to the user: {message}",
@ -210,6 +209,7 @@
"text_false": "The last email was sent to the member ({email}) on {date}.",
"text_true": "The email was confirmed."
},
"unregistered_emails": "Only unregistered users",
"userRole": {
"notChangeYourSelf": "As an admin/moderator, you cannot change your own role.",
"selectLabel": "Role:",

View File

@ -127,10 +127,7 @@ describe('CreationConfirm', () => {
confirmContributionMock.mockResolvedValue({ data: { confirmContribution: true } }),
)
mockClient.setRequestHandler(
getContribution,
getContributionMock.mockResolvedValue({ data: {} }),
)
mockClient.setRequestHandler(getContribution, getContributionMock.mockResolvedValue({ data: {} }))
const Wrapper = () => {
return mount(CreationConfirm, { localVue, mocks, apolloProvider })
@ -161,7 +158,7 @@ describe('CreationConfirm', () => {
expect(wrapper.find('tbody').findAll('tr')).toHaveLength(2)
})
})
describe('actions in overlay', () => {
describe('delete creation', () => {
beforeEach(async () => {
@ -523,12 +520,14 @@ describe('CreationConfirm', () => {
describe('reload contribution', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'OpenCreationsTable' }).vm.$emit('reload-contribution', 1)
await wrapper
.findComponent({ name: 'OpenCreationsTable' })
.vm.$emit('reload-contribution', 1)
})
it('reloaded contribution', () => {
expect(getContributionMock).toBeCalledWith({
id: 1
id: 1,
})
})
})

View File

@ -2,24 +2,23 @@
ROOT_DIR=$(dirname "$0")/..
tmp=$(mktemp)
exit_code=0
for locale_file in $ROOT_DIR/src/locales/*.json
do
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
jq -M 'to_entries | sort_by(.key) | from_entries' "$locale_file" > tmp.json
if [ "$*" == "--fix" ]
then
mv "$tmp" $locale_file
mv tmp.json "$locale_file"
else
if diff -q "$tmp" $locale_file > /dev/null ;
if ! diff -q tmp.json "$locale_file" > /dev/null ;
then
: # all good
else
exit_code=$?
echo "$(basename -- $locale_file) is not sorted by keys"
exit_code=1
echo "$(basename -- "$locale_file") is not sorted by keys"
fi
fi
done
rm -f tmp.json
exit $exit_code

View File

@ -11,8 +11,6 @@ import { logger, i18n as localization } from '@test/testSetup'
import { CONFIG } from '@/config'
CONFIG.EMAIL_SENDER = 'info@gradido.net'
import { sendEmailTranslated } from './sendEmailTranslated'
import {
sendAddedContributionMessageEmail,
@ -27,6 +25,8 @@ import {
sendContributionChangedByModeratorEmail,
} from './sendEmailVariants'
CONFIG.EMAIL_SENDER = 'info@gradido.net'
let con: Connection
let testEnv: {
mutate: ApolloServerTestClient['mutate']
@ -299,7 +299,7 @@ describe('sendEmailVariants', () => {
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionMemoUpdated: 'This is a better contribution memo.'
contributionMemoUpdated: 'This is a better contribution memo.',
})
})
@ -328,7 +328,6 @@ describe('sendEmailVariants', () => {
describe('result', () => {
it('is the expected object', () => {
console.log(result.originalMessage.text)
expect(result).toMatchObject({
envelope: {
from: 'info@gradido.net',

View File

@ -22,6 +22,12 @@ import { OpenCreation } from '@model/OpenCreation'
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
import { RIGHTS } from '@/auth/RIGHTS'
import {
sendContributionChangedByModeratorEmail,
sendContributionConfirmedEmail,
sendContributionDeletedEmail,
sendContributionDeniedEmail,
} from '@/emails/sendEmailVariants'
import {
EVENT_CONTRIBUTION_CREATE,
EVENT_CONTRIBUTION_DELETE,
@ -45,7 +51,6 @@ import { getUserCreation, validateContribution, getOpenCreations } from './util/
import { findContributions } from './util/findContributions'
import { getLastTransaction } from './util/getLastTransaction'
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
import { sendContributionChangedByModeratorEmail, sendContributionConfirmedEmail, sendContributionDeletedEmail, sendContributionDeniedEmail } from '@/emails/sendEmailVariants'
@Resolver()
export class ContributionResolver {
@ -255,7 +260,8 @@ export class ContributionResolver {
adminUpdateContributionArgs,
context,
)
const { contribution, contributionMessage, createdByUserChangedByModerator } = await updateUnconfirmedContributionContext.run()
const { contribution, contributionMessage, createdByUserChangedByModerator } =
await updateUnconfirmedContributionContext.run()
await getConnection().transaction(async (transactionalEntityManager: EntityManager) => {
await Promise.all([
transactionalEntityManager.save(contribution),
@ -288,7 +294,7 @@ export class ContributionResolver {
senderFirstName: moderator.firstName,
senderLastName: moderator.lastName,
contributionMemo: updateUnconfirmedContributionContext.getOldMemo(),
contributionMemoUpdated: contribution.memo
contributionMemoUpdated: contribution.memo,
})
}

View File

@ -41,13 +41,16 @@ export class UnconfirmedContributionAdminRole extends AbstractUnconfirmedContrib
}
return this
}
protected async validate(clientTimezoneOffset: number): Promise<void> {
await super.validate(clientTimezoneOffset)
// creation date is currently not changeable
if (this.self.memo === this.updateData.memo &&
this.self.amount === this.updatedAmount &&
this.self.contributionDate.getTime() === (new Date(this.updatedCreationDate).getTime())) {
throw new LogError("the contribution wasn't changed at all")
if (
this.self.memo === this.updateData.memo &&
this.self.amount === this.updatedAmount &&
this.self.contributionDate.getTime() === new Date(this.updatedCreationDate).getTime()
) {
throw new LogError("the contribution wasn't changed at all")
}
}
}

View File

@ -47,10 +47,12 @@ export class UnconfirmedContributionUserRole extends AbstractUnconfirmedContribu
protected async validate(clientTimezoneOffset: number): Promise<void> {
await super.validate(clientTimezoneOffset)
// creation date is currently not changeable
if (this.self.memo === this.updateData.memo &&
this.self.amount === this.updatedAmount &&
this.self.contributionDate.getTime() === (new Date(this.updatedCreationDate).getTime())) {
throw new LogError("the contribution wasn't changed at all")
if (
this.self.memo === this.updateData.memo &&
this.self.amount === this.updatedAmount &&
this.self.contributionDate.getTime() === new Date(this.updatedCreationDate).getTime()
) {
throw new LogError("the contribution wasn't changed at all")
}
}
}

View File

@ -34,7 +34,7 @@ export class UpdateUnconfirmedContributionContext {
public async run(): Promise<{
contribution: Contribution
contributionMessage: ContributionMessage
availableCreationSums: Decimal[],
availableCreationSums: Decimal[]
createdByUserChangedByModerator: boolean
}> {
let createdByUserChangedByModerator = false
@ -84,7 +84,7 @@ export class UpdateUnconfirmedContributionContext {
contribution: contributionToUpdate,
contributionMessage: contributionMessageBuilder.build(),
availableCreationSums: unconfirmedContributionRole.getAvailableCreationSums(),
createdByUserChangedByModerator
createdByUserChangedByModerator,
}
}

View File

@ -1,4 +1,6 @@
export const getTimeDurationObject = (time: number): {
export const getTimeDurationObject = (
time: number,
): {
hours?: number
minutes: number
} => {

View File

@ -3679,7 +3679,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
"gradido-database@file:../database":
version "2.0.0"
version "2.0.1"
dependencies:
"@types/uuid" "^8.3.4"
cross-env "^7.0.3"

View File

@ -2,24 +2,23 @@
ROOT_DIR=$(dirname "$0")/..
tmp=$(mktemp)
exit_code=0
for locale_file in $ROOT_DIR/src/locales/*.json
do
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
jq -M 'to_entries | sort_by(.key) | from_entries' "$locale_file" > tmp.json
if [ "$*" == "--fix" ]
then
mv "$tmp" $locale_file
mv tmp.json "$locale_file"
else
if diff -q "$tmp" $locale_file > /dev/null ;
if ! diff -q tmp.json "$locale_file" > /dev/null ;
then
: # all good
else
exit_code=$?
echo "$(basename -- $locale_file) is not sorted by keys"
exit_code=1
echo "$(basename -- "$locale_file") is not sorted by keys"
fi
fi
done
rm -f tmp.json
exit $exit_code

View File

@ -5,6 +5,9 @@
"1000thanks": "1000 Dank, weil du bei uns bist!",
"125": "125%",
"85": "85%",
"GDD": "GDD",
"GDT": "GDT",
"PersonalDetails": "Persönliche Angaben",
"advanced-calculation": "Vorausberechnung",
"asterisks": "****",
"auth": {
@ -179,7 +182,6 @@
},
"your_amount": "Dein Betrag"
},
"GDD": "GDD",
"gddKonto": "GDD Konto",
"gdd_per_link": {
"choose-amount": "Wähle einen Betrag aus, welchen du per Link versenden möchtest, und trage eine Nachricht ein. Die Nachricht ist ein Pflichtfeld.",
@ -214,7 +216,6 @@
"validUntil": "Gültig bis",
"validUntilDate": "Der Link ist bis zum {date} gültig."
},
"GDT": "GDT",
"gdt": {
"calculation": "Berechnung der Gradido Transform",
"contribution": "Beitrag",
@ -278,7 +279,6 @@
"settings": "Einstellungen",
"transactions": "Deine Transaktionen"
},
"PersonalDetails": "Persönliche Angaben",
"qrCode": "QR Code",
"send_gdd": "GDD versenden",
"send_per_link": "GDD versenden per Link",

View File

@ -5,6 +5,9 @@
"1000thanks": "1000 thanks for being with us!",
"125": "125%",
"85": "85%",
"GDD": "GDD",
"GDT": "GDT",
"PersonalDetails": "Personal details",
"advanced-calculation": "Advanced calculation",
"asterisks": "****",
"auth": {
@ -179,7 +182,6 @@
},
"your_amount": "Your amount"
},
"GDD": "GDD",
"gddKonto": "GDD Konto",
"gdd_per_link": {
"choose-amount": "Select an amount you want to send via link and enter a message. The message is mandatory.",
@ -214,7 +216,6 @@
"validUntil": "Valid until",
"validUntilDate": "The link is valid until {date}."
},
"GDT": "GDT",
"gdt": {
"calculation": "Calculation of Gradido Transform",
"contribution": "Contribution",
@ -278,7 +279,6 @@
"settings": "Settings",
"transactions": "Your transactions"
},
"PersonalDetails": "Personal details",
"qrCode": "QR Code",
"send_gdd": "Send GDD",
"send_per_link": "Send GDD via Link",

View File

@ -3,6 +3,7 @@
"1000thanks": "1000 Gracias, por estar con nosotros!",
"125": "125%",
"85": "85%",
"GDD": "GDD",
"advanced-calculation": "Proyección",
"asterisks": "****",
"auth": {
@ -157,7 +158,6 @@
},
"your_amount": "Tu importe"
},
"GDD": "GDD",
"gdd_per_link": {
"choose-amount": "Selecciona una cantidad que te gustaría enviar a través de un enlace. También puedes ingresar un mensaje. Cuando haces clic en 'Generar ahora', se crea un enlace que puedes enviar.",
"copy-link": "Copiar enlace",

View File

@ -5,6 +5,8 @@
"1000thanks": "1000 mercis d'être avec nous!",
"125": "125%",
"85": "85%",
"GDD": "GDD",
"GDT": "GDT",
"advanced-calculation": "Calcul avancé",
"asterisks": "****",
"auth": {
@ -163,7 +165,6 @@
},
"your_amount": "Votre montant"
},
"GDD": "GDD",
"gddKonto": "Compte GDD",
"gdd_per_link": {
"choose-amount": "Sélectionnez le montant que vous souhaitez envoyer via lien. Vous pouvez également joindre un message. Cliquez sur créer maintenant pour établir un lien que vous pourrez partager.",
@ -197,7 +198,6 @@
"validUntil": "Valide jusqu'au",
"validUntilDate": "Le lien est valide jusqu'au {date}."
},
"GDT": "GDT",
"gdt": {
"calculation": "Calcul de Gradido Transform",
"contribution": "Contribution",

View File

@ -3,6 +3,7 @@
"1000thanks": "1000 dank, omdat je bij ons bent!",
"125": "125%",
"85": "85%",
"GDD": "GDD",
"advanced-calculation": "Voorcalculatie",
"asterisks": "****",
"auth": {
@ -157,7 +158,6 @@
},
"your_amount": "Jouw bijdrage"
},
"GDD": "GDD",
"gdd_per_link": {
"choose-amount": "Kies een bedrag dat je per link versturen wil. Je kunt ook nog een bericht invullen. Wanneer je „Nu genereren“ klikt, wordt er een link gecreëerd die je kunt versturen.",
"copy-link": "Link kopiëren",

View File

@ -3,6 +3,7 @@
"1000thanks": "Bizimle olduğun için 1000lerce teşekkür!",
"125": "%125",
"85": "%85",
"GDD": "GDD",
"advanced-calculation": "Önceden hesaplama",
"auth": {
"left": {
@ -148,7 +149,6 @@
},
"your_amount": "Sendeki tutar"
},
"GDD": "GDD",
"gdd_per_link": {
"choose-amount": "Linke tıklayıp göndermek istediğiniz tutarı seç. Ayrıca bir mesaj da girebilirsin. Paylaşabileceğin bir bağlantı oluşturmak için 'Şimdi oluştur'a tıkla.",
"copy-link": "Linki kopyala ",

View File

@ -187,7 +187,10 @@ export default {
const originalContributionDate = new Date(this.originalContributionDate)
if (this.openCreations && this.openCreations.length)
return this.openCreations.slice(1).map((creation) => {
if (creation.year === originalContributionDate.getFullYear() && creation.month === originalContributionDate.getMonth())
if (
creation.year === originalContributionDate.getFullYear() &&
creation.month === originalContributionDate.getMonth()
)
return parseInt(creation.amount) + this.amountToAdd
return parseInt(creation.amount)
})