Add membership filter
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
cc8428e6b8
commit
9985ed9e44
|
@ -23,10 +23,12 @@ class FilterScope extends Filter
|
|||
* @param array<int, int> $groupIds
|
||||
* @param array<int, int> $include
|
||||
* @param array<int, int> $exclude
|
||||
* @param array<int, array{group_ids: array<int, int>, subactivity_ids: array<int, int>, activity_ids: array<int, int>}> $memberships
|
||||
*/
|
||||
public function __construct(
|
||||
public bool $ausstand = false,
|
||||
public ?string $billKind = null,
|
||||
public array $memberships = [],
|
||||
public array $activityIds = [],
|
||||
public array $subactivityIds = [],
|
||||
public ?string $search = '',
|
||||
|
@ -73,13 +75,16 @@ class FilterScope extends Filter
|
|||
$filter->push($this->inExpression('memberships.subactivity_id', $this->subactivityIds));
|
||||
}
|
||||
if ($this->subactivityIds && $this->activityIds) {
|
||||
$combinations = collect($this->activityIds)
|
||||
->map(fn ($activityId) => collect($this->subactivityIds)->map(fn ($subactivityId) => $activityId . '|' . $subactivityId))
|
||||
->flatten()
|
||||
$combinations = $this->combinations($this->activityIds, $this->subactivityIds)
|
||||
->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('"'))));
|
||||
}
|
||||
|
||||
if (count($this->exclude)) {
|
||||
$filter->push($this->notInExpression('id', $this->exclude));
|
||||
}
|
||||
|
@ -127,4 +132,33 @@ class FilterScope extends Filter
|
|||
|
||||
return "$key NOT IN [{$valueString}]";
|
||||
}
|
||||
|
||||
protected function possibleValuesForMembership(array $membership): Collection
|
||||
{
|
||||
return $this->combinations($membership['group_ids'], $membership['activity_ids'], $membership['subactivity_ids'])
|
||||
->map(fn ($combination) => collect($combination)->implode('|'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, mixed>
|
||||
*/
|
||||
protected function combinations(...$parts): Collection
|
||||
{
|
||||
$firstPart = array_shift($parts);
|
||||
$otherParts = $parts;
|
||||
|
||||
if (!count($otherParts)) {
|
||||
return collect(array_map(fn ($p) => [$p], $firstPart));
|
||||
}
|
||||
|
||||
/** @var Collection<int, mixed> */
|
||||
$results = collect([]);
|
||||
foreach ($firstPart as $firstPartSegment) {
|
||||
foreach ($this->combinations(...$otherParts) as $combination) {
|
||||
$results->push([$firstPartSegment, ...$combination]);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -502,7 +502,7 @@ class Member extends Model implements Geolocatable
|
|||
'bill_kind' => $this->bill_kind?->value,
|
||||
'group_id' => $this->group->id,
|
||||
'memberships' => $this->memberships()->active()->get()
|
||||
->map(fn ($membership) => [...$membership->only('activity_id', 'subactivity_id'), 'both' => $membership->activity_id . '|' . $membership->subactivity_id]),
|
||||
->map(fn ($membership) => [...$membership->only('activity_id', 'subactivity_id'), 'both' => $membership->activity_id . '|' . $membership->subactivity_id, 'with_group' => $membership->group_id . '|' . $membership->activity_id . '|' . $membership->subactivity_id]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,6 +157,11 @@ class MemberResource extends JsonResource
|
|||
'index' => route('member.index'),
|
||||
'create' => route('member.create'),
|
||||
],
|
||||
'default_membership_filter' => [
|
||||
'group_ids' => [],
|
||||
'activity_ids' => [],
|
||||
'subactivity_ids' => []
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,28 @@
|
|||
</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_id" v-model="filter.group_ids" :options="meta.groups" label="Gruppierung"
|
||||
size="sm" name="group_id"></f-multipleselect>
|
||||
<f-multipleselect id="activity_ids" v-model="filter.activity_ids" :options="meta.filterActivities"
|
||||
label="Tätigkeiten" size="sm" name="activity_ids"></f-multipleselect>
|
||||
<f-multipleselect id="subactivity_ids" v-model="filter.subactivity_ids" :options="meta.filterSubactivities"
|
||||
label="Untertätigkeiten" size="sm" name="subactivity_ids"></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 breakpoint="xl">
|
||||
<f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm"
|
||||
@update:model-value="setFilter('search', $event)"></f-text>
|
||||
|
@ -34,12 +56,10 @@
|
|||
<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>
|
||||
<f-multipleselect id="activity_ids" :options="meta.filterActivities" :model-value="getFilter('activity_ids')"
|
||||
label="Tätigkeiten" size="sm" name="activity_ids"
|
||||
@update:model-value="setFilter('activity_ids', $event)"></f-multipleselect>
|
||||
<f-multipleselect id="subactivity_ids" :options="meta.filterSubactivities"
|
||||
:model-value="getFilter('subactivity_ids')" label="Untertätigkeiten" size="sm" name="subactivity_ids"
|
||||
@update:model-value="setFilter('subactivity_ids', $event)"></f-multipleselect>
|
||||
<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>
|
||||
<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>
|
||||
|
@ -126,6 +146,7 @@ import { ref, defineProps } from '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');
|
||||
|
|
|
@ -169,6 +169,36 @@ class MemberIndexTest extends EndToEndTestCase
|
|||
->assertInertiaCount('data.data', 0);
|
||||
}
|
||||
|
||||
public function testItFiltersForMemberships(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$mitglied = Activity::factory()->create();
|
||||
$woelfling = Subactivity::factory()->create();
|
||||
$juffi = Subactivity::factory()->create();
|
||||
$group = Group::factory()->create();
|
||||
Member::factory()->defaults()->has(Membership::factory()->for($mitglied)->for($woelfling)->for($group))->create();
|
||||
Member::factory()->defaults()->has(Membership::factory()->for($mitglied)->for($juffi)->for($group))->create();
|
||||
Member::factory()->defaults()
|
||||
->has(Membership::factory()->for($mitglied)->for($woelfling)->for($group))
|
||||
->has(Membership::factory()->for($mitglied)->for($juffi)->for($group))
|
||||
->create();
|
||||
|
||||
sleep(1);
|
||||
$this->callFilter('member.index', ['memberships' => [
|
||||
['group_ids' => [$group->id], 'activity_ids' => [$mitglied->id], 'subactivity_ids' => [$woelfling->id]]
|
||||
]])->assertInertiaCount('data.data', 2);
|
||||
$this->callFilter('member.index', ['memberships' => [
|
||||
['group_ids' => [$group->id], 'activity_ids' => [$mitglied->id], 'subactivity_ids' => [$juffi->id]]
|
||||
]])->assertInertiaCount('data.data', 2);
|
||||
$this->callFilter('member.index', ['memberships' => [
|
||||
['group_ids' => [$group->id], 'activity_ids' => [$mitglied->id], 'subactivity_ids' => [$juffi->id, $woelfling->id]],
|
||||
]])->assertInertiaCount('data.data', 3);
|
||||
$this->callFilter('member.index', ['memberships' => [
|
||||
['group_ids' => [$group->id], 'activity_ids' => [$mitglied->id], 'subactivity_ids' => [$woelfling->id]],
|
||||
['group_ids' => [$group->id], 'activity_ids' => [$mitglied->id], 'subactivity_ids' => [$juffi->id]],
|
||||
]])->assertInertiaCount('data.data', 1);
|
||||
}
|
||||
|
||||
public function testItFiltersForSearchButNotForPayments(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
|
|
Loading…
Reference in New Issue