Merge branch 'master' into insert-missing-contributions-migration

This commit is contained in:
Moriz Wahl 2022-07-26 13:51:20 +02:00 committed by GitHub
commit bd957c38f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 113 additions and 56 deletions

View File

@ -28,7 +28,7 @@ const propsData = {
amount: 210, amount: 210,
memo: 'Aktives Grundeinkommen für Januar 2022', memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z', date: '2022-01-01T00:00:00.000Z',
moderator: 1, moderator: null,
creation: [790, 1000, 1000], creation: [790, 1000, 1000],
__typename: 'PendingCreation', __typename: 'PendingCreation',
}, },
@ -66,7 +66,7 @@ const propsData = {
}, },
}, },
{ key: 'moderator', label: 'moderator' }, { key: 'moderator', label: 'moderator' },
{ key: 'edit_creation', label: 'edit' }, { key: 'editCreation', label: 'edit' },
{ key: 'confirm', label: 'save' }, { key: 'confirm', label: 'save' },
], ],
toggleDetails: false, toggleDetails: false,
@ -113,6 +113,10 @@ describe('OpenCreationsTable', () => {
expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBe(true) expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBe(true)
}) })
it('has no button.bi-pencil-square for user contribution ', () => {
expect(wrapper.findAll('tr').at(2).find('.bi-pencil-square').exists()).toBe(false)
})
describe('show edit details', () => { describe('show edit details', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.findAll('tr').at(1).find('.bi-pencil-square').trigger('click') await wrapper.findAll('tr').at(1).find('.bi-pencil-square').trigger('click')

View File

@ -11,8 +11,14 @@
<b-icon icon="x" variant="light"></b-icon> <b-icon icon="x" variant="light"></b-icon>
</b-button> </b-button>
</template> </template>
<template #cell(edit_creation)="row"> <template #cell(editCreation)="row">
<b-button variant="info" size="md" @click="rowToggleDetails(row, 0)" class="mr-2"> <b-button
v-if="row.item.moderator"
variant="info"
size="md"
@click="rowToggleDetails(row, 0)"
class="mr-2"
>
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon> <b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
</b-button> </b-button>
</template> </template>

View File

@ -35,6 +35,7 @@
"creation_form": { "creation_form": {
"creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.", "creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.",
"creation_for": "Aktives Grundeinkommen für", "creation_for": "Aktives Grundeinkommen für",
"deleteNow": "Möchtest du diesen Beitrag zur Gemeinschaft wirklich löschen?",
"enter_text": "Text eintragen", "enter_text": "Text eintragen",
"form": "Schöpfungsformular", "form": "Schöpfungsformular",
"min_characters": "Mindestens 10 Zeichen eingeben", "min_characters": "Mindestens 10 Zeichen eingeben",

View File

@ -35,6 +35,7 @@
"creation_form": { "creation_form": {
"creation_failed": "Could not create pending creation for {email}", "creation_failed": "Could not create pending creation for {email}",
"creation_for": "Active Basic Income for", "creation_for": "Active Basic Income for",
"deleteNow": "Do you really want to delete this contribution to the community?",
"enter_text": "Enter text", "enter_text": "Enter text",
"form": "Creation form", "form": "Creation form",
"min_characters": "Enter at least 10 characters", "min_characters": "Enter at least 10 characters",

View File

@ -18,7 +18,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
amount: 500, amount: 500,
memo: 'Danke für alles', memo: 'Danke für alles',
date: new Date(), date: new Date(),
moderator: 0, moderator: 2,
}, },
{ {
id: 2, id: 2,
@ -28,7 +28,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
amount: 1000000, amount: 1000000,
memo: 'Gut Ergattert', memo: 'Gut Ergattert',
date: new Date(), date: new Date(),
moderator: 0, moderator: 2,
}, },
], ],
}, },
@ -80,10 +80,19 @@ describe('CreationConfirm', () => {
}) })
describe('remove creation with success', () => { describe('remove creation with success', () => {
let spy
describe('admin confirms deletion', () => {
beforeEach(async () => { beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve('some value'))
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click') await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
}) })
it('opens a modal', () => {
expect(spy).toBeCalled()
})
it('calls the adminDeleteContribution mutation', () => { it('calls the adminDeleteContribution mutation', () => {
expect(apolloMutateMock).toBeCalledWith({ expect(apolloMutateMock).toBeCalledWith({
mutation: adminDeleteContribution, mutation: adminDeleteContribution,
@ -100,8 +109,25 @@ describe('CreationConfirm', () => {
}) })
}) })
describe('remove creation with error', () => { describe('admin cancels deletion', () => {
beforeEach(async () => { beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve(false))
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
})
it('does not call the adminDeleteContribution mutation', () => {
expect(apolloMutateMock).not.toBeCalled()
})
})
})
describe('remove creation with error', () => {
let spy
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve('some value'))
apolloMutateMock.mockRejectedValue({ message: 'Ouchhh!' }) apolloMutateMock.mockRejectedValue({ message: 'Ouchhh!' })
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click') await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
}) })

View File

@ -34,7 +34,9 @@ export default {
}, },
methods: { methods: {
removeCreation(item) { removeCreation(item) {
this.$apollo this.$bvModal.msgBoxConfirm(this.$t('creation_form.deleteNow')).then(async (value) => {
if (value)
await this.$apollo
.mutate({ .mutate({
mutation: adminDeleteContribution, mutation: adminDeleteContribution,
variables: { variables: {
@ -48,6 +50,7 @@ export default {
.catch((error) => { .catch((error) => {
this.toastError(error.message) this.toastError(error.message)
}) })
})
}, },
confirmCreation() { confirmCreation() {
this.$apollo this.$apollo
@ -114,7 +117,7 @@ export default {
}, },
}, },
{ key: 'moderator', label: this.$t('moderator') }, { key: 'moderator', label: this.$t('moderator') },
{ key: 'edit_creation', label: this.$t('edit') }, { key: 'editCreation', label: this.$t('edit') },
{ key: 'confirm', label: this.$t('save') }, { key: 'confirm', label: this.$t('save') },
] ]
}, },

View File

@ -35,12 +35,17 @@ export const creationFactory = async (
if (creation.confirmed) { if (creation.confirmed) {
await mutate({ mutation: confirmContribution, variables: { id: pendingCreation.id } }) await mutate({ mutation: confirmContribution, variables: { id: pendingCreation.id } })
const confirmedCreation = await Contribution.findOneOrFail({ id: pendingCreation.id })
if (creation.moveCreationDate) { if (creation.moveCreationDate) {
const transaction = await Transaction.findOneOrFail({ const transaction = await Transaction.findOneOrFail({
where: { userId: user.id, creationDate: new Date(creation.creationDate) }, where: { userId: user.id, creationDate: new Date(creation.creationDate) },
order: { balanceDate: 'DESC' }, order: { balanceDate: 'DESC' },
}) })
if (transaction.decay.equals(0) && transaction.creationDate) { if (transaction.decay.equals(0) && transaction.creationDate) {
confirmedCreation.contributionDate = new Date(
nMonthsBefore(transaction.creationDate, creation.moveCreationDate),
)
transaction.creationDate = new Date( transaction.creationDate = new Date(
nMonthsBefore(transaction.creationDate, creation.moveCreationDate), nMonthsBefore(transaction.creationDate, creation.moveCreationDate),
) )
@ -48,6 +53,7 @@ export const creationFactory = async (
nMonthsBefore(transaction.balanceDate, creation.moveCreationDate), nMonthsBefore(transaction.balanceDate, creation.moveCreationDate),
) )
await transaction.save() await transaction.save()
await confirmedCreation.save()
} }
} }
} else { } else {

View File

@ -23,6 +23,9 @@ describe('ContributionForm', () => {
creation: ['1000', '1000', '1000'], creation: ['1000', '1000', '1000'],
}, },
}, },
$i18n: {
locale: 'en',
},
} }
const Wrapper = () => { const Wrapper = () => {

View File

@ -18,6 +18,7 @@
id="contribution-date" id="contribution-date"
v-model="form.date" v-model="form.date"
size="lg" size="lg"
:locale="$i18n.locale"
:max="maximalDate" :max="maximalDate"
:min="minimalDate" :min="minimalDate"
class="mb-4" class="mb-4"
@ -25,6 +26,15 @@
:label-no-date-selected="$t('contribution.noDateSelected')" :label-no-date-selected="$t('contribution.noDateSelected')"
required required
></b-form-datepicker> ></b-form-datepicker>
<validation-provider
:rules="{
required: true,
min: minlength,
max: maxlength,
}"
:name="$t('form.message')"
v-slot="{ errors }"
>
<label class="mt-3">{{ $t('contribution.activity') }}</label> <label class="mt-3">{{ $t('contribution.activity') }}</label>
<b-form-textarea <b-form-textarea
id="contribution-memo" id="contribution-memo"
@ -32,18 +42,11 @@
rows="3" rows="3"
max-rows="6" max-rows="6"
required required
:minlength="minlength"
:maxlength="maxlength"
></b-form-textarea> ></b-form-textarea>
<div <b-col v-if="errors">
v-show="form.memo.length > 0" <span v-for="error in errors" class="errors" :key="error">{{ error }}</span>
class="text-right" </b-col>
:class="form.memo.length < minlength ? 'text-danger' : 'text-success'" </validation-provider>
>
{{ form.memo.length }}
<span v-if="form.memo.length < minlength">{{ $t('math.lower') }} {{ minlength }}</span>
<span v-else>{{ $t('math.divide') }} {{ maxlength }}</span>
</div>
<label class="mt-3">{{ $t('form.amount') }}</label> <label class="mt-3">{{ $t('form.amount') }}</label>
<b-input-group size="lg" prepend="GDD" append=".00"> <b-input-group size="lg" prepend="GDD" append=".00">
<b-form-input <b-form-input
@ -90,7 +93,7 @@ export default {
}, },
data() { data() {
return { return {
minlength: 50, minlength: 5,
maxlength: 255, maxlength: 255,
maximalDate: new Date(), maximalDate: new Date(),
form: this.value, form: this.value,
@ -166,3 +169,8 @@ export default {
}, },
} }
</script> </script>
<style>
span.errors {
color: red;
}
</style>

View File

@ -203,10 +203,8 @@
"login": "Anmeldung", "login": "Anmeldung",
"math": { "math": {
"aprox": "~", "aprox": "~",
"divide": "/",
"equal": "=", "equal": "=",
"exclaim": "!", "exclaim": "!",
"lower": "<",
"minus": "", "minus": "",
"pipe": "|" "pipe": "|"
}, },

View File

@ -203,10 +203,8 @@
"login": "Login", "login": "Login",
"math": { "math": {
"aprox": "~", "aprox": "~",
"divide": "/",
"equal": "=", "equal": "=",
"exclaim": "!", "exclaim": "!",
"lower": "<",
"minus": "", "minus": "",
"pipe": "|" "pipe": "|"
}, },

View File

@ -26,6 +26,9 @@ describe('Community', () => {
creation: ['1000', '1000', '1000'], creation: ['1000', '1000', '1000'],
}, },
}, },
$i18n: {
locale: 'en',
},
} }
const Wrapper = () => { const Wrapper = () => {