Compare commits
16 Commits
13f2ada2ee
...
e55603e9ee
Author | SHA1 | Date |
---|---|---|
|
e55603e9ee | |
|
4d4705d92f | |
|
2ad97bcfa9 | |
|
138335b10c | |
|
6e8fddbe72 | |
|
b62b44d575 | |
|
8ff2cf76a1 | |
|
080e60bbd8 | |
|
39d64df1de | |
|
cc93e6c006 | |
|
43be5c3a79 | |
|
e09e7f4827 | |
|
3423dcabb8 | |
|
210a8bdb13 | |
|
94297723a5 | |
|
1a4aeb73a1 |
|
@ -48,8 +48,7 @@ class FilterScope extends ScoutFilter
|
|||
public ?bool $hasBirthday = null,
|
||||
public ?bool $hasSvk = null,
|
||||
public ?bool $hasVk = null,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
|
@ -110,20 +109,20 @@ class FilterScope extends ScoutFilter
|
|||
}
|
||||
if ($this->subactivityIds && $this->activityIds) {
|
||||
$combinations = $this->combinations($this->activityIds, $this->subactivityIds)
|
||||
->map(fn ($combination) => implode('|', $combination))
|
||||
->map(fn ($combination) => str($combination)->wrap('"'));
|
||||
->map(fn($combination) => implode('|', $combination))
|
||||
->map(fn($combination) => str($combination)->wrap('"'));
|
||||
$filter->push($this->inExpression('memberships.both', $combinations));
|
||||
}
|
||||
|
||||
foreach ($this->memberships as $membership) {
|
||||
$filter->push($this->inExpression('memberships.with_group', $this->possibleValuesForMembership($membership)->map(fn ($value) => str($value)->wrap('"'))));
|
||||
$filter->push($this->inExpression('memberships.with_group', $this->possibleValuesForMembership($membership)->map(fn($value) => str($value)->wrap('"'))));
|
||||
}
|
||||
|
||||
if (count($this->exclude)) {
|
||||
$filter->push($this->notInExpression('id', $this->exclude));
|
||||
}
|
||||
|
||||
$andFilter = $filter->map(fn ($expression) => "($expression)")->implode(' AND ');
|
||||
$andFilter = $filter->map(fn($expression) => "($expression)")->implode(' AND ');
|
||||
|
||||
$options['filter'] = $this->implode(collect([$andFilter])->push($this->inExpression('id', $this->include)), 'OR');
|
||||
$options['sort'] = ['lastname:asc', 'firstname:asc'];
|
||||
|
@ -137,7 +136,7 @@ class FilterScope extends ScoutFilter
|
|||
*/
|
||||
protected function implode(Collection $values, string $between): string
|
||||
{
|
||||
return $values->filter(fn ($expression) => $expression)->implode(" {$between} ");
|
||||
return $values->filter(fn($expression) => $expression)->implode(" {$between} ");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,7 +176,7 @@ class FilterScope extends ScoutFilter
|
|||
$membership['activity_ids'] = count($membership['activity_ids']) === 0 ? Activity::pluck('id')->toArray() : $membership['activity_ids'];
|
||||
$membership['subactivity_ids'] = count($membership['subactivity_ids']) === 0 ? Subactivity::pluck('id')->toArray() : $membership['subactivity_ids'];
|
||||
return $this->combinations($membership['group_ids'], $membership['activity_ids'], $membership['subactivity_ids'])
|
||||
->map(fn ($combination) => collect($combination)->implode('|'));
|
||||
->map(fn($combination) => collect($combination)->implode('|'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,7 +190,7 @@ class FilterScope extends ScoutFilter
|
|||
|
||||
if (!count($otherParts)) {
|
||||
/** @var Collection<int, Collection<int, int>> */
|
||||
return collect($firstPart)->map(fn ($p) => [$p]);
|
||||
return collect($firstPart)->map(fn($p) => [$p]);
|
||||
}
|
||||
|
||||
/** @var Collection<int, mixed> */
|
||||
|
|
|
@ -204,6 +204,10 @@ class Member extends Model implements Geolocatable, Preventable
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!$this->email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (object) ['name' => $this->fullname, 'email' => $this->email];
|
||||
}
|
||||
|
||||
|
|
|
@ -133,11 +133,11 @@ class MemberResource extends JsonResource
|
|||
}
|
||||
|
||||
$activities = Activity::with('subactivities')->get();
|
||||
$createActivities = Activity::remote()->with(['subactivities' => fn ($q) => $q->remote()])->get();
|
||||
$createActivities = Activity::remote()->with(['subactivities' => fn($q) => $q->remote()])->get();
|
||||
|
||||
return [
|
||||
'filterActivities' => Activity::where('is_filterable', true)->pluck('name', 'id'),
|
||||
'filterSubactivities' => Subactivity::where('is_filterable', true)->pluck('name', 'id'),
|
||||
'filterActivities' => Activity::where('is_filterable', true)->get()->map(fn($a) => ['id' => $a->id, 'name' => $a->name]),
|
||||
'filterSubactivities' => Subactivity::where('is_filterable', true)->get()->map(fn($a) => ['id' => $a->id, 'name' => $a->name]),
|
||||
'formActivities' => $activities->pluck('name', 'id'),
|
||||
'formSubactivities' => $activities->map(function (Activity $activity) {
|
||||
return ['subactivities' => $activity->subactivities->pluck('name', 'id'), 'id' => $activity->id];
|
||||
|
@ -155,7 +155,7 @@ class MemberResource extends JsonResource
|
|||
'genders' => Gender::pluck('name', 'id'),
|
||||
'billKinds' => BillKind::forSelect(),
|
||||
'nationalities' => Nationality::pluck('name', 'id'),
|
||||
'members' => Member::ordered()->get()->map(fn ($member) => ['id' => $member->id, 'name' => $member->fullname]),
|
||||
'members' => Member::ordered()->get()->map(fn($member) => ['id' => $member->id, 'name' => $member->fullname]),
|
||||
'links' => [
|
||||
'index' => route('member.index'),
|
||||
'create' => route('member.create'),
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Prevention\Actions;
|
|||
|
||||
use App\Lib\Editor\EditorData;
|
||||
use App\Lib\Events\Succeeded;
|
||||
use App\Member\FilterScope;
|
||||
use App\Prevention\PreventionSettings;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
@ -34,6 +35,7 @@ class SettingStoreAction
|
|||
$settings->weeks = $request->weeks;
|
||||
$settings->freshRememberInterval = $request->freshRememberInterval;
|
||||
$settings->active = $request->active;
|
||||
$settings->yearlyMemberFilter = FilterScope::from($request->yearlyMemberFilter);
|
||||
$settings->save();
|
||||
|
||||
Succeeded::message('Einstellungen gespeichert.')->dispatch();
|
||||
|
|
|
@ -22,7 +22,12 @@ class YearlyRememberAction
|
|||
$settings = app(PreventionSettings::class);
|
||||
$expireDate = now()->addWeeks($settings->weeks);
|
||||
|
||||
foreach (Member::get() as $member) {
|
||||
foreach ($settings->yearlyMemberFilter->getQuery()->get() as $member) {
|
||||
// @todo add this check to FilterScope
|
||||
if ($member->getMailRecipient() === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$noticePreventions = $member->preventions($expireDate)
|
||||
->filter(fn($prevention) => $prevention->expiresAt($expireDate));
|
||||
|
||||
|
@ -33,7 +38,12 @@ class YearlyRememberAction
|
|||
Mail::send($this->createMail($member, $noticePreventions));
|
||||
}
|
||||
|
||||
foreach (Member::get() as $member) {
|
||||
foreach ($settings->yearlyMemberFilter->getQuery()->get() as $member) {
|
||||
// @todo add this check to FilterScope
|
||||
if ($member->getMailRecipient() === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$preventions = $member->preventions()
|
||||
->filter(fn($prevention) => $prevention->expiresAt(now()));
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Prevention;
|
||||
|
||||
use App\Lib\Editor\EditorData;
|
||||
use App\Member\FilterScope;
|
||||
use App\Setting\LocalSettings;
|
||||
|
||||
class PreventionSettings extends LocalSettings
|
||||
|
@ -13,6 +14,7 @@ class PreventionSettings extends LocalSettings
|
|||
public int $weeks;
|
||||
public int $freshRememberInterval;
|
||||
public bool $active;
|
||||
public FilterScope $yearlyMemberFilter;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
|
|
|
@ -8,3 +8,5 @@ sudo mysql adrema < db.tmp
|
|||
rm db.tmp
|
||||
|
||||
echo 'app(\App\Form\FormSettings::class)->fill(["registerUrl" => "http://stammsilva.test/anmeldung/{slug}/register", "clearCacheUrl" => "http://stammsilva.test/adrema/clear-cache"])->save();' | php artisan tinker
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Member\FilterScope;
|
||||
use Spatie\LaravelSettings\Migrations\SettingsMigration;
|
||||
|
||||
return new class extends SettingsMigration
|
||||
|
@ -10,5 +11,6 @@ return new class extends SettingsMigration
|
|||
$this->migrator->add('prevention.weeks', 8);
|
||||
$this->migrator->add('prevention.freshRememberInterval', 12);
|
||||
$this->migrator->add('prevention.active', false);
|
||||
$this->migrator->add('prevention.yearlyMemberFilter', FilterScope::from([])->toArray());
|
||||
}
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<label class="flex flex-col group" :for="id" :class="sizeClass(size)">
|
||||
<f-label v-if="label" :required="false" :value="label"></f-label>
|
||||
<div class="relative flex-none flex">
|
||||
<ui-icon-button :class="[fieldHeight, fieldAppearance, paddingX]" icon="filter" @click="visible = true">Filtern</ui-icon-button>
|
||||
<f-hint v-if="hint" :value="hint"></f-hint>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<ui-filter-sidebar v-model="visible">
|
||||
<member-filter-fields :model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)" />
|
||||
</ui-filter-sidebar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from 'vue';
|
||||
import useFieldSize from '../../composables/useFieldSize';
|
||||
import MemberFilterFields from '../../views/member/MemberFilterFields.vue';
|
||||
|
||||
const {sizeClass, fieldHeight, fieldAppearance, paddingX} = useFieldSize();
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
defineEmits(['update:modelValue']);
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: () => 'base',
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,9 +1,7 @@
|
|||
<template>
|
||||
<ui-popup v-if="visible === true" heading="Filtern" @close="visible = false">
|
||||
<div class="grid gap-3 md:grid-cols-2">
|
||||
<slot name="fields"></slot>
|
||||
</div>
|
||||
</ui-popup>
|
||||
<ui-filter-sidebar v-model="visible">
|
||||
<slot name="fields"></slot>
|
||||
</ui-filter-sidebar>
|
||||
<div class="px-6 py-2 border-b border-gray-600 items-center space-x-3">
|
||||
<div class="flex flex-col sm:flex-row items-stretch sm:items-end space-y-1 sm:space-y-0 sm:space-x-3">
|
||||
<slot name="buttons"></slot>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<ui-sidebar :max="0" v-if="modelValue === true" @close="$emit('update:modelValue', false)">
|
||||
<page-header title="Filter" @close="$emit('update:modelValue', false)"> </page-header>
|
||||
<div class="grid gap-3 p-6">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</ui-sidebar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineEmits(['update:modelValue']);
|
||||
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div
|
||||
class="fixed w-full w-[80vw] max-w-[40rem] shadow-2xl bg-gray-600 right-0 top-0 h-full flex flex-col group is-bright">
|
||||
<div class="fixed shadow-2xl bg-gray-600 right-0 top-0 h-full flex flex-col group is-bright" :class="widths[max]">
|
||||
<suspense>
|
||||
<slot></slot>
|
||||
|
||||
|
@ -18,4 +17,20 @@
|
|||
|
||||
<script setup>
|
||||
defineEmits(['close']);
|
||||
|
||||
const widths = {
|
||||
40: 'w-full w-[80vw] max-w-[40rem]',
|
||||
30: 'w-full w-[80vw] max-w-[30rem]',
|
||||
20: 'w-full w-[80vw] max-w-[20rem]',
|
||||
10: 'w-full w-[80vw] max-w-[10rem]',
|
||||
0: '',
|
||||
};
|
||||
|
||||
defineProps({
|
||||
max: {
|
||||
default: () => 40,
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -56,6 +56,11 @@ export function useIndex(props, siteName) {
|
|||
reload(true);
|
||||
}
|
||||
|
||||
function setFilterObject(o) {
|
||||
inner.filter.value = o;
|
||||
reload(true);
|
||||
}
|
||||
|
||||
startListener();
|
||||
onBeforeUnmount(() => stopListener());
|
||||
|
||||
|
@ -70,6 +75,8 @@ export function useIndex(props, siteName) {
|
|||
toFilterString,
|
||||
reloadPage,
|
||||
axios,
|
||||
filter: inner.filter,
|
||||
setFilterObject,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<f-switch v-show="hasModule('bill')" id="ausstand" name="ausstand" label="Nur Ausstände" size="sm" v-model="filter.ausstand"></f-switch>
|
||||
<f-select id="has_vk" name="has_vk" label="Verhaltenskodex unterschrieben" size="sm" :options="meta.boolean_filter" v-model="filter.has_vk"></f-select>
|
||||
<f-select id="has_svk" name="has_svk" label="SVK unterschrieben" size="sm" :options="meta.boolean_filter" v-model="filter.has_svk"></f-select>
|
||||
<f-multipleselect id="group_ids" :options="meta.groups" label="Gruppierungen" size="sm" v-model="filter.group_ids"></f-multipleselect>
|
||||
<f-select v-show="hasModule('bill')" id="billKinds" name="billKinds" :options="meta.billKinds" label="Rechnung" size="sm" v-model="filter.bill_kind"></f-select>
|
||||
|
||||
<div class="mt-5">
|
||||
<f-checkboxes-label>nach Mitgliedschaften</f-checkboxes-label>
|
||||
<button class="btn btn-primary label mt-2" @click.prevent="filter.memberships = [...filter.memberships, {...meta.default_membership_filter}]">
|
||||
<ui-sprite class="w-3 h-3 xl:mr-2" src="plus"></ui-sprite>
|
||||
<span class="hidden xl:inline">Hinzufügen</span>
|
||||
</button>
|
||||
<template v-for="(filter, index) in filter.memberships" :key="index">
|
||||
<f-multipleselect :id="`group_ids-multiple-${index}`" class="mt-4" v-model="filter.group_ids" :options="meta.groups" label="Gruppierung" size="sm"></f-multipleselect>
|
||||
<f-multipleselect :id="`activity_ids-multiple-${index}`" v-model="filter.activity_ids" :options="meta.filterActivities" label="Tätigkeiten" size="sm"></f-multipleselect>
|
||||
<f-multipleselect :id="`subactivity_ids-multiple-${index}`" v-model="filter.subactivity_ids" :options="meta.filterSubactivities" label="Untertätigkeiten" size="sm"></f-multipleselect>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {inject, ref, watch} from 'vue';
|
||||
|
||||
const axios = inject('axios');
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const metaResponse = await axios.post('/api/member/search', {});
|
||||
const meta = ref(metaResponse.data.meta);
|
||||
|
||||
const filter = ref({...props.modelValue});
|
||||
|
||||
watch(
|
||||
filter,
|
||||
function (newValue) {
|
||||
emit('update:modelValue', newValue);
|
||||
},
|
||||
{deep: true}
|
||||
);
|
||||
</script>
|
|
@ -18,7 +18,14 @@
|
|||
label="Untertätigkeit"
|
||||
@update:modelValue="setSubactivityId(single, $event)"
|
||||
></f-select>
|
||||
<f-switch v-if="displayPromisedAt" id="has_promise" :model-value="single.promised_at !== null" label="Hat Versprechen" @update:modelValue="setPromisedAtSwitch(single, $event)"></f-switch>
|
||||
<f-switch
|
||||
v-if="displayPromisedAt"
|
||||
id="has_promise"
|
||||
name="has_promise"
|
||||
:model-value="single.promised_at !== null"
|
||||
label="Hat Versprechen"
|
||||
@update:modelValue="setPromisedAtSwitch(single, $event)"
|
||||
></f-switch>
|
||||
<f-text v-show="displayPromisedAt && single.promised_at !== null" id="promised_at" v-model="single.promised_at" type="date" label="Versprechensdatum" size="sm"></f-text>
|
||||
<button type="submit" class="btn btn-primary">Absenden</button>
|
||||
</form>
|
||||
|
|
|
@ -17,80 +17,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</ui-popup>
|
||||
<ui-popup v-if="membershipFilters !== null" heading="Nach Mitgliedschaften filtern" full @close="membershipFilters = null">
|
||||
<button class="btn btn-primary label mt-2" @click.prevent="membershipFilters.push({...meta.default_membership_filter})">
|
||||
<ui-sprite class="w-3 h-3 xl:mr-2" src="plus"></ui-sprite>
|
||||
<span class="hidden xl:inline">Hinzufügen</span>
|
||||
</button>
|
||||
<div v-for="(filter, index) in membershipFilters" :key="index" class="flex space-x-2 mt-2">
|
||||
<f-multipleselect :id="`group_ids-multiple-${index}`" v-model="filter.group_ids" :options="meta.groups" label="Gruppierung" size="sm"></f-multipleselect>
|
||||
<f-multipleselect :id="`activity_ids-multiple-${index}`" v-model="filter.activity_ids" :options="meta.filterActivities" label="Tätigkeiten" size="sm"></f-multipleselect>
|
||||
<f-multipleselect :id="`subactivity_ids-multiple-${index}`" v-model="filter.subactivity_ids" :options="meta.filterSubactivities" label="Untertätigkeiten" size="sm"></f-multipleselect>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary label mt-3"
|
||||
@click.prevent="
|
||||
setFilter('memberships', membershipFilters);
|
||||
membershipFilters = null;
|
||||
"
|
||||
>
|
||||
<span class="hidden xl:inline">Anwenden</span>
|
||||
</button>
|
||||
</ui-popup>
|
||||
<page-filter>
|
||||
<template #fields>
|
||||
<f-switch
|
||||
v-show="hasModule('bill')"
|
||||
id="ausstand"
|
||||
name="ausstand"
|
||||
:model-value="getFilter('ausstand')"
|
||||
label="Nur Ausstände"
|
||||
size="sm"
|
||||
@update:model-value="setFilter('ausstand', $event)"
|
||||
></f-switch>
|
||||
<f-select
|
||||
id="has_vk"
|
||||
name="has_vk"
|
||||
:model-value="getFilter('has_vk')"
|
||||
label="Verhaltenskodex unterschrieben"
|
||||
size="sm"
|
||||
:options="meta.boolean_filter"
|
||||
@update:model-value="setFilter('has_vk', $event)"
|
||||
></f-select>
|
||||
<f-select
|
||||
id="has_svk"
|
||||
name="has_svk"
|
||||
:model-value="getFilter('has_svk')"
|
||||
label="SVK unterschrieben"
|
||||
size="sm"
|
||||
:options="meta.boolean_filter"
|
||||
@update:model-value="setFilter('has_svk', $event)"
|
||||
></f-select>
|
||||
<f-multipleselect
|
||||
id="group_ids"
|
||||
:options="meta.groups"
|
||||
:model-value="getFilter('group_ids')"
|
||||
label="Gruppierungen"
|
||||
size="sm"
|
||||
@update:model-value="setFilter('group_ids', $event)"
|
||||
></f-multipleselect>
|
||||
<f-select
|
||||
v-show="hasModule('bill')"
|
||||
id="billKinds"
|
||||
name="billKinds"
|
||||
:options="meta.billKinds"
|
||||
:model-value="getFilter('bill_kind')"
|
||||
label="Rechnung"
|
||||
size="sm"
|
||||
@update:model-value="setFilter('bill_kind', $event)"
|
||||
></f-select>
|
||||
<button class="btn btn-primary label mr-2" @click.prevent="membershipFilters = getFilter('memberships')">
|
||||
<ui-sprite class="w-3 h-3 xl:mr-2" src="filter"></ui-sprite>
|
||||
<span class="hidden xl:inline">Mitgliedschaften</span>
|
||||
</button>
|
||||
<member-filter-fields :model-value="filter" @update:model-value="setFilterObject($event)" />
|
||||
</template>
|
||||
<template #buttons>
|
||||
<f-text id="search" :model-value="getFilter('search')" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text>
|
||||
<f-text id="search" :model-value="filter.search" label="Suchen …" size="sm" @update:model-value="setFilterObject({...filter, search: $event})"></f-text>
|
||||
<button class="btn btn-primary label mr-2" @click.prevent="exportMembers">
|
||||
<ui-sprite class="w-3 h-3 xl:mr-2" src="save"></ui-sprite>
|
||||
<span class="hidden xl:inline">Exportieren</span>
|
||||
|
@ -161,21 +93,21 @@
|
|||
</page-layout>
|
||||
</template>
|
||||
|
||||
<script lang="js" setup>
|
||||
<script setup>
|
||||
import MemberInvoicePositions from './MemberInvoicePositions.vue';
|
||||
import MemberMemberships from './MemberMemberships.vue';
|
||||
import MemberCourses from './MemberCourses.vue';
|
||||
import Tags from './Tags.vue';
|
||||
import Actions from './index/Actions.vue';
|
||||
import { indexProps, useIndex } from '../../composables/useIndex.js';
|
||||
import { ref, defineProps } from 'vue';
|
||||
import {indexProps, useIndex} from '../../composables/useIndex.js';
|
||||
import {ref, defineProps} from 'vue';
|
||||
import MemberFilterFields from './MemberFilterFields.vue';
|
||||
|
||||
const single = ref(null);
|
||||
const deleting = ref(null);
|
||||
const membershipFilters = ref(null);
|
||||
|
||||
const props = defineProps(indexProps);
|
||||
var { router, data, meta, getFilter, setFilter, filterString, reloadPage } = useIndex(props.data, 'member');
|
||||
var {router, data, meta, filter, setFilterObject, filterString, reloadPage} = useIndex(props.data, 'member');
|
||||
|
||||
function exportMembers() {
|
||||
window.open(`/member-export?filter=${filterString.value}`);
|
||||
|
@ -183,7 +115,7 @@ function exportMembers() {
|
|||
|
||||
async function remove(member) {
|
||||
new Promise((resolve, reject) => {
|
||||
deleting.value = { resolve, reject, member };
|
||||
deleting.value = {resolve, reject, member};
|
||||
})
|
||||
.then(() => {
|
||||
router.delete(`/member/${member.id}`);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<f-text id="fresh_remember_interval" v-model="data.freshRememberInterval" label="Bei Ablauf alle X Wochen erinnern" type="number" />
|
||||
</div>
|
||||
<f-editor v-if="active === 1" id="yearlymail" v-model="data.yearlymail" label="Jährliche Präventions-Erinnerung"></f-editor>
|
||||
<f-member-filter id="yearly_member_filter" v-model="data.yearlyMemberFilter" label="nur für folgende Mitglieder erlauben" />
|
||||
</div>
|
||||
</form>
|
||||
</setting-layout>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Member;
|
||||
namespace Tests\EndToEnd\Member;
|
||||
|
||||
use App\Prevention\Enums\Prevention;
|
||||
use App\Form\Actions\PreventionRememberAction;
|
||||
|
@ -10,6 +10,7 @@ use App\Form\Models\Form;
|
|||
use App\Form\Models\Participant;
|
||||
use App\Invoice\InvoiceSettings;
|
||||
use App\Lib\Editor\Condition;
|
||||
use App\Member\FilterScope;
|
||||
use App\Prevention\Mails\PreventionRememberMail;
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
|
@ -18,11 +19,13 @@ use App\Prevention\Mails\YearlyMail;
|
|||
use App\Prevention\PreventionSettings;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Tests\EndToEndTestCase;
|
||||
use Tests\Lib\CreatesFormFields;
|
||||
use Tests\RequestFactories\EditorRequestFactory;
|
||||
|
||||
uses(DatabaseTransactions::class);
|
||||
uses(CreatesFormFields::class);
|
||||
uses(EndToEndTestCase::class);
|
||||
|
||||
function createForm(): Form
|
||||
{
|
||||
|
@ -229,6 +232,7 @@ it('notices a few weeks before', function ($date, bool $shouldSend) {
|
|||
app(PreventionSettings::class)->fill(['weeks' => 2])->save();
|
||||
createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]);
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
|
||||
$shouldSend
|
||||
|
@ -244,6 +248,7 @@ it('remembers members yearly', function ($date, $shouldSend) {
|
|||
Mail::fake();
|
||||
createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]);
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
|
||||
$shouldSend
|
||||
|
@ -259,6 +264,7 @@ it('remembers yearly only once', function () {
|
|||
Mail::fake();
|
||||
createMember(['efz' => now()->subYears(5), 'ps_at' => now(), 'has_vk' => true]);
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
YearlyRememberAction::run();
|
||||
YearlyRememberAction::run();
|
||||
|
@ -278,6 +284,29 @@ it('testItDoesntRememberParticipantThatHasNoMail', function () {
|
|||
Mail::assertNotSent(PreventionRememberMail::class);
|
||||
});
|
||||
|
||||
it('doesnt send yearly mail when member has no mail', function () {
|
||||
Mail::fake();
|
||||
createMember(['efz' => now()->subYears(5), 'ps_at' => now(), 'has_vk' => true, 'email' => '', 'email_parents' => '']);
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
|
||||
Mail::assertNotSent(YearlyMail::class);
|
||||
});
|
||||
|
||||
it('doesnt send yearly mail when member doesnt match', function () {
|
||||
Mail::fake();
|
||||
app(PreventionSettings::class)->fill([
|
||||
'yearlyMemberFilter' => FilterScope::from(['search' => 'Lorem Ipsum']),
|
||||
])->save();
|
||||
createMember(['efz' => now()->subYears(5), 'ps_at' => now(), 'has_vk' => true, 'firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
|
||||
Mail::assertNotSent(YearlyMail::class);
|
||||
});
|
||||
|
||||
it('testItRendersSetttingMail', function () {
|
||||
Mail::fake();
|
||||
app(PreventionSettings::class)->fill([
|
||||
|
@ -362,6 +391,7 @@ it('renders setting of yearly mail', function () {
|
|||
])->save();
|
||||
$member = createMember((['efz' => now()->subYears(5), 'ps_at' => now(), 'has_vk' => true]));
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
|
||||
Mail::assertSent(
|
||||
|
@ -379,6 +409,7 @@ it('renders expires at date for preventions', function () {
|
|||
])->save();
|
||||
createMember((['efz' => now()->subYears(5)->addWeeks(4), 'ps_at' => now(), 'has_vk' => true]));
|
||||
|
||||
sleep(2);
|
||||
YearlyRememberAction::run();
|
||||
|
||||
Mail::assertSent(YearlyMail::class, fn($mail) => $mail->bodyText->hasAll([
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\Feature\Prevention;
|
||||
|
||||
use App\Member\FilterScope;
|
||||
use App\Prevention\PreventionSettings;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\RequestFactories\EditorRequestFactory;
|
||||
|
@ -20,25 +21,47 @@ it('receives settings', function () {
|
|||
|
||||
$text = EditorRequestFactory::new()->text(50, 'lorem ipsum')->toData();
|
||||
$yearlyMail = EditorRequestFactory::new()->text(50, 'lala dd')->toData();
|
||||
app(PreventionSettings::class)->fill(['formmail' => $text, 'yearlymail' => $yearlyMail, 'weeks' => 9, 'freshRememberInterval' => 11, 'active' => true])->save();
|
||||
app(PreventionSettings::class)->fill([
|
||||
'formmail' => $text,
|
||||
'yearlymail' => $yearlyMail,
|
||||
'weeks' => 9,
|
||||
'freshRememberInterval' => 11,
|
||||
'active' => true,
|
||||
'yearlyMemberFilter' => FilterScope::from([
|
||||
'memberships' => [['group_ids' => [33]]],
|
||||
'search' => 'searchstring',
|
||||
]),
|
||||
])->save();
|
||||
|
||||
test()->get('/api/prevention')
|
||||
->assertJsonPath('data.formmail.blocks.0.data.text', 'lorem ipsum')
|
||||
->assertJsonPath('data.yearlymail.blocks.0.data.text', 'lala dd')
|
||||
->assertJsonPath('data.weeks', '9')
|
||||
->assertJsonPath('data.active', true)
|
||||
->assertJsonPath('data.freshRememberInterval', '11');
|
||||
->assertJsonPath('data.freshRememberInterval', '11')
|
||||
->assertJsonPath('data.yearlyMemberFilter.search', 'searchstring')
|
||||
->assertJsonPath('data.yearlyMemberFilter.memberships.0.group_ids.0', 33);
|
||||
});
|
||||
|
||||
it('testItStoresSettings', function () {
|
||||
test()->login()->loginNami();
|
||||
|
||||
$formmail = EditorRequestFactory::new()->text(50, 'new lorem')->create();
|
||||
$yearlyMail = EditorRequestFactory::new()->text(50, 'lala dd')->create();
|
||||
test()->post('/api/prevention', ['formmail' => $formmail, 'yearlymail' => $yearlyMail, 'weeks' => 9, 'freshRememberInterval' => 11, 'active' => true])->assertOk();
|
||||
test()->post('/api/prevention', [
|
||||
'formmail' => EditorRequestFactory::new()->text(50, 'new lorem')->create(),
|
||||
'yearlymail' => EditorRequestFactory::new()->text(50, 'lala dd')->create(),
|
||||
'weeks' => 9,
|
||||
'freshRememberInterval' => 11,
|
||||
'active' => true,
|
||||
'yearlyMemberFilter' => [
|
||||
'memberships' => [['group_ids' => 33]],
|
||||
'search' => 'searchstring',
|
||||
],
|
||||
])->assertOk();
|
||||
test()->assertTrue(app(PreventionSettings::class)->formmail->hasAll(['new lorem']));
|
||||
test()->assertTrue(app(PreventionSettings::class)->yearlymail->hasAll(['lala dd']));
|
||||
test()->assertEquals(9, app(PreventionSettings::class)->weeks);
|
||||
test()->assertEquals(11, app(PreventionSettings::class)->freshRememberInterval);
|
||||
test()->assertTrue(app(PreventionSettings::class)->active);
|
||||
test()->assertEquals([['group_ids' => 33]], app(PreventionSettings::class)->yearlyMemberFilter->memberships);
|
||||
test()->assertEquals('searchstring', app(PreventionSettings::class)->yearlyMemberFilter->search);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue