Add api for fetching memberships
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
85f0d6c515
commit
3d154c4154
|
@ -23,7 +23,7 @@ class MemberController extends Controller
|
|||
'data' => MemberResource::collection(Member::search($filter->search)->query(
|
||||
fn ($q) => $q->select('*')
|
||||
->withFilter($filter)
|
||||
->with(['payments.subscription', 'memberships', 'courses', 'subscription', 'leaderMemberships', 'ageGroupMemberships'])
|
||||
->with(['payments.subscription', 'courses', 'subscription', 'leaderMemberships', 'ageGroupMemberships'])
|
||||
->withPendingPayment()
|
||||
->ordered()
|
||||
)->paginate(15)),
|
||||
|
|
|
@ -73,10 +73,10 @@ class MemberResource extends JsonResource
|
|||
'has_nami' => null !== $this->nami_id,
|
||||
'children_phone' => $this->children_phone,
|
||||
'payments' => PaymentResource::collection($this->whenLoaded('payments')),
|
||||
'memberships' => MembershipResource::collection($this->whenLoaded('memberships')),
|
||||
'pending_payment' => $this->pending_payment ? number_format($this->pending_payment / 100, 2, ',', '.') . ' €' : null,
|
||||
'age_group_icon' => $this->ageGroupMemberships->first()?->subactivity->slug,
|
||||
'courses' => CourseMemberResource::collection($this->whenLoaded('courses')),
|
||||
'memberships' => MembershipResource::collection($this->whenLoaded('memberships')),
|
||||
'nationality' => new NationalityResource($this->whenLoaded('nationality')),
|
||||
'region' => new RegionResource($this->whenLoaded('region')),
|
||||
'full_address' => $this->fullAddress,
|
||||
|
@ -105,6 +105,7 @@ class MemberResource extends JsonResource
|
|||
'lat' => $this->lat,
|
||||
'lon' => $this->lon,
|
||||
'links' => [
|
||||
'membership_index' => route('member.membership.index', ['member' => $this->getModel()]),
|
||||
'show' => route('member.show', ['member' => $this->getModel()]),
|
||||
'edit' => route('member.edit', ['member' => $this->getModel()]),
|
||||
],
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Membership\Actions;
|
||||
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use App\Membership\MembershipResource;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class ApiIndexAction
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
/**
|
||||
* @return Collection<int, Membership>
|
||||
*/
|
||||
public function handle(Member $member): Collection
|
||||
{
|
||||
return $member->memberships;
|
||||
}
|
||||
|
||||
public function asController(Member $member): AnonymousResourceCollection
|
||||
{
|
||||
return MembershipResource::collection($this->handle($member))
|
||||
->additional([
|
||||
'meta' => MembershipResource::memberMeta($member)
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use App\Maildispatcher\Actions\ResyncAction;
|
|||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
@ -32,16 +32,15 @@ class MembershipDestroyAction
|
|||
}
|
||||
}
|
||||
|
||||
public function asController(Member $member, Membership $membership, ActionRequest $request, NamiSettings $settings): RedirectResponse
|
||||
public function asController(Membership $membership, NamiSettings $settings): JsonResponse
|
||||
{
|
||||
$this->handle(
|
||||
$member,
|
||||
$membership->member,
|
||||
$membership,
|
||||
$settings,
|
||||
);
|
||||
|
||||
ResyncAction::dispatch();
|
||||
|
||||
return redirect()->back();
|
||||
return response()->json([]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@ namespace App\Membership\Actions;
|
|||
|
||||
use App\Activity;
|
||||
use App\Maildispatcher\Actions\ResyncAction;
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use App\Subactivity;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\In;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
|
@ -20,8 +19,6 @@ class MembershipUpdateAction
|
|||
|
||||
public function handle(Membership $membership, Activity $activity, ?Subactivity $subactivity, ?Carbon $promisedAt): Membership
|
||||
{
|
||||
$from = now()->startOfDay();
|
||||
|
||||
$membership->update([
|
||||
'activity_id' => $activity->id,
|
||||
'subactivity_id' => $subactivity ? $subactivity->id : null,
|
||||
|
@ -56,7 +53,7 @@ class MembershipUpdateAction
|
|||
];
|
||||
}
|
||||
|
||||
public function asController(Member $member, Membership $membership, ActionRequest $request): RedirectResponse
|
||||
public function asController(Membership $membership, ActionRequest $request): JsonResponse
|
||||
{
|
||||
$this->handle(
|
||||
$membership,
|
||||
|
@ -67,6 +64,6 @@ class MembershipUpdateAction
|
|||
|
||||
ResyncAction::dispatch();
|
||||
|
||||
return redirect()->back();
|
||||
return response()->json([]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
namespace App\Membership;
|
||||
|
||||
use App\Activity;
|
||||
use App\Lib\HasMeta;
|
||||
use App\Member\Data\NestedGroup;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +32,33 @@ class MembershipResource extends JsonResource
|
|||
'human_date' => $this->from->format('d.m.Y'),
|
||||
'promised_at' => $this->promised_at?->format('Y-m-d'),
|
||||
'is_active' => $this->isActive(),
|
||||
'links' => [
|
||||
'update' => route('membership.update', ['membership' => $this->getModel()]),
|
||||
'destroy' => route('membership.destroy', ['membership' => $this->getModel()]),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function memberMeta(Member $member): array
|
||||
{
|
||||
$activities = Activity::with('subactivities')->get();
|
||||
|
||||
return [
|
||||
'links' => [
|
||||
'store' => route('member.membership.store', ['member' => $member]),
|
||||
],
|
||||
'groups' => NestedGroup::cacheForSelect(),
|
||||
'activities' => $activities->map(fn ($activity) => ['id' => $activity->id, 'name' => $activity->name]),
|
||||
'subactivities' => $activities->mapWithKeys(fn ($activity) => [$activity->id => $activity->subactivities->map(fn ($subactivity) => ['id' => $subactivity->id, 'name' => $subactivity->name])]),
|
||||
'default' => [
|
||||
'group_id' => $member->group_id,
|
||||
'activity_id' => null,
|
||||
'subactivity_id' => null,
|
||||
'promised_at' => null,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,10 +63,11 @@ class MembershipFactory extends Factory
|
|||
|
||||
public function in(string $activity, int $activityNamiId, ?string $subactivity = null, ?int $subactivityNamiId = null): self
|
||||
{
|
||||
$instance = $this->for(Activity::factory()->name($activity)->inNami($activityNamiId));
|
||||
$activityModel = Activity::factory()->name($activity)->inNami($activityNamiId)->create();
|
||||
$instance = $this->for($activityModel);
|
||||
|
||||
if ($subactivity) {
|
||||
$instance = $instance->for(Subactivity::factory()->name($subactivity)->inNami($subactivityNamiId));
|
||||
$instance = $instance->for(Subactivity::factory()->name($subactivity)->inNami($subactivityNamiId)->hasAttached($activityModel));
|
||||
}
|
||||
|
||||
return $instance;
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
<template>
|
||||
<div v-tooltip="longLabel" class="flex space-x-2 items-center">
|
||||
<div class="border-2 rounded-full w-5 h-5 flex items-center justify-center"
|
||||
:class="value ? (dark ? 'border-green-500' : 'border-green-700') : dark ? 'border-red-500' : 'border-red-700'">
|
||||
<ui-sprite :src="value ? 'check' : 'close'"
|
||||
:class="value ? (dark ? 'text-green-600' : 'text-green-800') : dark ? 'text-red-600' : 'text-red-800'"
|
||||
class="w-3 h-3 flex-none"></ui-sprite>
|
||||
<div class="border-2 rounded-full w-5 h-5 flex items-center justify-center" :class="value ? (dark ? 'border-green-500' : 'border-green-700') : dark ? 'border-red-500' : 'border-red-700'">
|
||||
<ui-sprite :src="value ? 'check' : 'close'" :class="value ? (dark ? 'text-green-600' : 'text-green-800') : dark ? 'text-red-600' : 'text-red-800'" class="w-3 h-3 flex-none"></ui-sprite>
|
||||
</div>
|
||||
<div class="text-gray-400 text-xs" v-text="label"></div>
|
||||
</div>
|
||||
|
@ -18,8 +15,8 @@ export default {
|
|||
type: Boolean,
|
||||
},
|
||||
label: {
|
||||
required: true,
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
longLabel: {
|
||||
default: function () {
|
||||
|
|
|
@ -2,24 +2,24 @@
|
|||
<div class="sidebar flex flex-col group is-bright">
|
||||
<page-header title="Mitgliedschaften" @close="$emit('close')">
|
||||
<template #toolbar>
|
||||
<page-toolbar-button v-if="single === null" color="primary" icon="plus" @click.prevent="create">Neue
|
||||
Mitgliedschaft</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single !== null" color="primary" icon="undo"
|
||||
@click.prevent="cancel">Zurück</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single === null" color="primary" icon="plus" @click.prevent="create">Neue Mitgliedschaft</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single !== null" color="primary" icon="undo" @click.prevent="single = null">Zurück</page-toolbar-button>
|
||||
</template>
|
||||
</page-header>
|
||||
|
||||
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
||||
<f-select id="group_id" v-model="single.group_id" name="group_id" :options="groups" label="Gruppierung"
|
||||
required></f-select>
|
||||
<f-select id="activity_id" v-model="single.activity_id" name="activity_id" :options="activities"
|
||||
label="Tätigkeit" required></f-select>
|
||||
<f-select v-if="single.activity_id" id="subactivity_id" v-model="single.subactivity_id" name="subactivity_id"
|
||||
:options="subactivities[single.activity_id]" label="Untertätigkeit" size="sm"></f-select>
|
||||
<f-switch id="has_promise" :model-value="single.promised_at !== null" label="Hat Versprechen"
|
||||
@update:modelValue="single.promised_at = $event ? '2000-02-02' : null"></f-switch>
|
||||
<f-text v-show="single.promised_at !== null" id="promised_at" v-model="single.promised_at" type="date"
|
||||
label="Versprechensdatum" size="sm"></f-text>
|
||||
<f-select id="group_id" v-model="single.group_id" name="group_id" :options="data.meta.groups" label="Gruppierung" required></f-select>
|
||||
<f-select id="activity_id" v-model="single.activity_id" name="activity_id" :options="data.meta.activities" label="Tätigkeit" required></f-select>
|
||||
<f-select
|
||||
v-if="single.activity_id"
|
||||
id="subactivity_id"
|
||||
v-model="single.subactivity_id"
|
||||
name="subactivity_id"
|
||||
:options="data.meta.subactivities[single.activity_id]"
|
||||
label="Untertätigkeit"
|
||||
></f-select>
|
||||
<f-switch id="has_promise" :model-value="single.promised_at !== null" label="Hat Versprechen" @update:modelValue="single.promised_at = $event ? '2000-02-02' : null"></f-switch>
|
||||
<f-text v-show="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>
|
||||
|
||||
|
@ -33,18 +33,14 @@
|
|||
<th></th>
|
||||
</thead>
|
||||
|
||||
<tr v-for="(membership, index) in value.memberships" :key="index">
|
||||
<tr v-for="(membership, index) in data.data" :key="index">
|
||||
<td v-text="membership.activity_name"></td>
|
||||
<td v-text="membership.subactivity_name"></td>
|
||||
<td v-text="membership.human_date"></td>
|
||||
<td><ui-boolean-display :value="membership.is_active" dark></ui-boolean-display></td>
|
||||
<td class="flex">
|
||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="
|
||||
single = membership;
|
||||
mode = 'edit';
|
||||
"><ui-sprite src="pencil"></ui-sprite></a>
|
||||
<i-link href="#" class="inline-flex btn btn-danger btn-sm"
|
||||
@click.prevent="remove(membership)"><ui-sprite src="trash"></ui-sprite></i-link>
|
||||
<td class="flex space-x-1">
|
||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(membership)"><ui-sprite src="pencil"></ui-sprite></a>
|
||||
<a href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(membership)"><ui-sprite src="trash"></ui-sprite></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -52,70 +48,47 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {},
|
||||
activities: {},
|
||||
subactivities: {},
|
||||
groups: {},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
mode: null,
|
||||
single: null,
|
||||
};
|
||||
<script setup>
|
||||
import {ref, inject, onBeforeMount} from 'vue';
|
||||
const axios = inject('axios');
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
computed: {
|
||||
def() {
|
||||
return {
|
||||
group_id: this.value.group_id,
|
||||
activity_id: null,
|
||||
subactivity_id: null,
|
||||
promised_at: null,
|
||||
};
|
||||
},
|
||||
},
|
||||
const single = ref(null);
|
||||
const data = ref({
|
||||
meta: {},
|
||||
data: [],
|
||||
});
|
||||
|
||||
methods: {
|
||||
create() {
|
||||
this.mode = 'create';
|
||||
this.single = { ...this.def };
|
||||
},
|
||||
cancel() {
|
||||
this.mode = this.single = null;
|
||||
},
|
||||
remove(membership) {
|
||||
this.$inertia.delete(`/member/${this.value.id}/membership/${membership.id}`);
|
||||
},
|
||||
async function reload() {
|
||||
data.value = (await axios.post(props.value.links.membership_index)).data;
|
||||
}
|
||||
|
||||
accept(payment) {
|
||||
this.$inertia.patch(`/member/${this.value.id}/payment/${payment.id}`, { ...payment, status_id: 3 });
|
||||
},
|
||||
onBeforeMount(async () => {
|
||||
await reload();
|
||||
});
|
||||
|
||||
openLink(link) {
|
||||
if (link.disabled) {
|
||||
return;
|
||||
}
|
||||
function create() {
|
||||
single.value = JSON.parse(JSON.stringify(data.value.meta.default));
|
||||
}
|
||||
|
||||
window.open(link.href);
|
||||
},
|
||||
function edit(membership) {
|
||||
single.value = JSON.parse(JSON.stringify(membership));
|
||||
}
|
||||
|
||||
submit() {
|
||||
var _self = this;
|
||||
async function submit() {
|
||||
single.value.id ? await axios.patch(single.value.links.update, single.value) : await axios.post(data.value.meta.links.store, single.value);
|
||||
await reload();
|
||||
single.value = null;
|
||||
}
|
||||
|
||||
var options = {
|
||||
onSuccess() {
|
||||
_self.single = null;
|
||||
_self.mode = null;
|
||||
},
|
||||
};
|
||||
|
||||
this.mode === 'create'
|
||||
? this.$inertia.post(`/member/${this.value.id}/membership`, this.single, options)
|
||||
: this.$inertia.patch(`/member/${this.value.id}/membership/${this.single.id}`, this.single, options);
|
||||
},
|
||||
},
|
||||
};
|
||||
async function remove(membership) {
|
||||
await axios.delete(membership.links.destroy);
|
||||
await reload();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<template>
|
||||
<div class="sidebar flex flex-col group is-bright">
|
||||
<page-header @close="$emit('close')" title="Zahlungen">
|
||||
<page-header title="Zahlungen" @close="$emit('close')">
|
||||
<template #toolbar>
|
||||
<page-toolbar-button @click.prevent="create" color="primary" icon="plus" v-if="single === null">Neue Zahlung</page-toolbar-button>
|
||||
<page-toolbar-button @click.prevent="cancel" color="primary" icon="undo" v-if="single !== null">Zurück</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single === null" color="primary" icon="plus" @click.prevent="create">Neue Zahlung</page-toolbar-button>
|
||||
<page-toolbar-button v-if="single !== null" color="primary" icon="undo" @click.prevent="cancel">Zurück</page-toolbar-button>
|
||||
</template>
|
||||
</page-header>
|
||||
|
||||
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
||||
<f-text id="nr" v-model="single.nr" label="Jahr" required></f-text>
|
||||
<f-select id="subscription_id" name="subscription_id" :options="subscriptions" v-model="single.subscription_id" label="Beitrag" required></f-select>
|
||||
<f-select id="status_id" name="status_id" :options="statuses" v-model="single.status_id" label="Status" required></f-select>
|
||||
<f-select id="subscription_id" v-model="single.subscription_id" name="subscription_id" :options="subscriptions" label="Beitrag" required></f-select>
|
||||
<f-select id="status_id" v-model="single.status_id" name="status_id" :options="statuses" label="Status" required></f-select>
|
||||
<button type="submit" class="btn btn-primary">Absenden</button>
|
||||
</form>
|
||||
|
||||
<div class="grow" v-else>
|
||||
<div v-else class="grow">
|
||||
<table class="custom-table custom-table-light custom-table-sm text-sm">
|
||||
<thead>
|
||||
<th>Nr</th>
|
||||
|
@ -30,28 +30,28 @@
|
|||
<td class="flex">
|
||||
<a
|
||||
href="#"
|
||||
class="inline-flex btn btn-warning btn-sm"
|
||||
@click.prevent="
|
||||
single = payment;
|
||||
mode = 'edit';
|
||||
"
|
||||
class="inline-flex btn btn-warning btn-sm"
|
||||
><ui-sprite src="pencil"></ui-sprite
|
||||
></a>
|
||||
<i-link v-show="!payment.is_accepted" href="#" @click.prevent="accept(payment)" class="inline-flex btn btn-success btn-sm"><ui-sprite src="check"></ui-sprite></i-link>
|
||||
<i-link href="#" @click.prevent="remove(payment)" class="inline-flex btn btn-danger btn-sm"><ui-sprite src="trash"></ui-sprite></i-link>
|
||||
<i-link v-show="!payment.is_accepted" href="#" class="inline-flex btn btn-success btn-sm" @click.prevent="accept(payment)"><ui-sprite src="check"></ui-sprite></i-link>
|
||||
<i-link href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(payment)"><ui-sprite src="trash"></ui-sprite></i-link>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flex flex-col pb-6 px-6">
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="openLink(link)"
|
||||
:class="{disabled: link.disabled}"
|
||||
target="_BLANK"
|
||||
v-for="(link, index) in value.payment_links"
|
||||
:key="index"
|
||||
href="#"
|
||||
:class="{disabled: link.disabled}"
|
||||
target="_BLANK"
|
||||
class="mt-1 text-center btn btn-primary"
|
||||
@click.prevent="openLink(link)"
|
||||
v-text="link.label"
|
||||
></a>
|
||||
</div>
|
||||
|
@ -60,6 +60,12 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
value: {},
|
||||
subscriptions: {},
|
||||
statuses: {},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
mode: null,
|
||||
|
@ -107,11 +113,5 @@ export default {
|
|||
});
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {},
|
||||
subscriptions: {},
|
||||
statuses: {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<ui-label :value="member.pending_payment" fallback="---"></ui-label>
|
||||
</td>
|
||||
<td>
|
||||
<actions :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)"></actions>
|
||||
<actions :member="member" @sidebar="openSidebar($event, member)" @remove="remove(member)"></actions>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -93,7 +93,7 @@
|
|||
<ui-label v-show="hasModule('bill')" class="text-gray-100 block" :value="member.pending_payment"
|
||||
fallback=""></ui-label>
|
||||
</div>
|
||||
<actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)">
|
||||
<actions class="mt-2" :member="member" @sidebar="openSidebar($event, member)" @remove="remove(member)">
|
||||
</actions>
|
||||
<div class="absolute right-0 top-0 h-full flex items-center mr-2">
|
||||
<i-link v-tooltip="`Details`" :href="member.links.show"><ui-sprite src="chevron"
|
||||
|
@ -106,12 +106,11 @@
|
|||
<ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
|
||||
</div>
|
||||
|
||||
<member-payments v-if="single !== null && sidebar === 'payment.index'" :subscriptions="meta.subscriptions"
|
||||
:statuses="meta.statuses" :value="data[single]" @close="closeSidebar"></member-payments>
|
||||
<member-memberships v-if="single !== null && sidebar === 'membership.index'" :groups="meta.groups"
|
||||
:activities="meta.formActivities" :subactivities="meta.formSubactivities" :value="data[single]"
|
||||
@close="closeSidebar"></member-memberships>
|
||||
<member-courses v-if="single !== null && sidebar === 'courses.index'" :courses="meta.courses" :value="data[single]"
|
||||
<member-payments v-if="single !== null && single.type === 'payment'" :subscriptions="meta.subscriptions"
|
||||
:statuses="meta.statuses" :value="single.model" @close="closeSidebar"></member-payments>
|
||||
<member-memberships v-if="single !== null && single.type === 'membership'" :activities="meta.formActivities"
|
||||
:subactivities="meta.formSubactivities" :value="single.model" @close="closeSidebar"></member-memberships>
|
||||
<member-courses v-if="single !== null && single.type === 'courses'" :courses="meta.courses" :value="single.model"
|
||||
@close="closeSidebar"></member-courses>
|
||||
</page-layout>
|
||||
</template>
|
||||
|
@ -125,7 +124,6 @@ import Actions from './index/Actions.vue';
|
|||
import { indexProps, useIndex } from '../../composables/useIndex.js';
|
||||
import { ref, defineProps } from 'vue';
|
||||
|
||||
const sidebar = ref(null);
|
||||
const single = ref(null);
|
||||
const deleting = ref(null);
|
||||
|
||||
|
@ -147,12 +145,13 @@ async function remove(member) {
|
|||
.catch(() => (deleting.value = null));
|
||||
}
|
||||
|
||||
function openSidebar(index, name) {
|
||||
single.value = index;
|
||||
sidebar.value = name;
|
||||
function openSidebar(type, model) {
|
||||
single.value = {
|
||||
type: type,
|
||||
model: model,
|
||||
};
|
||||
}
|
||||
function closeSidebar() {
|
||||
single.value = null;
|
||||
sidebar.value = null;
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
<template>
|
||||
<div class="flex space-x-1">
|
||||
<i-link :href="member.links.show" class="inline-flex btn btn-primary btn-sm" v-tooltip="`Details`"><ui-sprite src="eye"></ui-sprite></i-link>
|
||||
<i-link :href="`/member/${member.id}/edit`" class="inline-flex btn btn-warning btn-sm" v-tooltip="`Bearbeiten`"><ui-sprite src="pencil"></ui-sprite></i-link>
|
||||
<a href="#" v-tooltip="`Zahlungen`" v-show="hasModule('bill')" @click.prevent="$emit('sidebar', 'payment.index')" class="inline-flex btn btn-info btn-sm"
|
||||
><ui-sprite src="money"></ui-sprite
|
||||
></a>
|
||||
<a href="#" v-tooltip="`Ausbildungen`" v-show="hasModule('courses')" @click.prevent="$emit('sidebar', 'courses.index')" class="inline-flex btn btn-info btn-sm"
|
||||
<i-link v-tooltip="`Details`" :href="member.links.show" class="inline-flex btn btn-primary btn-sm"><ui-sprite src="eye"></ui-sprite></i-link>
|
||||
<i-link v-tooltip="`Bearbeiten`" :href="`/member/${member.id}/edit`" class="inline-flex btn btn-warning btn-sm"><ui-sprite src="pencil"></ui-sprite></i-link>
|
||||
<a v-show="hasModule('bill')" v-tooltip="`Zahlungen`" href="#" class="inline-flex btn btn-info btn-sm" @click.prevent="$emit('sidebar', 'payment')"><ui-sprite src="money"></ui-sprite></a>
|
||||
<a v-show="hasModule('courses')" v-tooltip="`Ausbildungen`" href="#" class="inline-flex btn btn-info btn-sm" @click.prevent="$emit('sidebar', 'courses')"
|
||||
><ui-sprite src="course"></ui-sprite
|
||||
></a>
|
||||
<a href="#" v-tooltip="`Mitgliedschaften`" @click.prevent="$emit('sidebar', 'membership.index')" class="inline-flex btn btn-info btn-sm"><ui-sprite src="user"></ui-sprite></a>
|
||||
<a :href="member.efz_link" v-show="member.efz_link" class="inline-flex btn btn-info btn-sm" v-tooltip="`EFZ Formular`"><ui-sprite src="report"></ui-sprite></a>
|
||||
<a href="#" @click.prevent="$emit('remove')" class="inline-flex btn btn-danger btn-sm" v-tooltip="`Entfernen`"><ui-sprite src="trash"></ui-sprite></a>
|
||||
<a v-tooltip="`Mitgliedschaften`" href="#" class="inline-flex btn btn-info btn-sm" @click.prevent="$emit('sidebar', 'membership')"><ui-sprite src="user"></ui-sprite></a>
|
||||
<a v-show="member.efz_link" v-tooltip="`EFZ Formular`" :href="member.efz_link" class="inline-flex btn btn-info btn-sm"><ui-sprite src="report"></ui-sprite></a>
|
||||
<a v-tooltip="`Entfernen`" href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="$emit('remove')"><ui-sprite src="trash"></ui-sprite></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ use App\Member\Actions\MemberResyncAction;
|
|||
use App\Member\Actions\MemberShowAction;
|
||||
use App\Member\Actions\SearchAction;
|
||||
use App\Member\MemberController;
|
||||
use App\Membership\Actions\ApiIndexAction;
|
||||
use App\Membership\Actions\ApiListAction;
|
||||
use App\Membership\Actions\MembershipDestroyAction;
|
||||
use App\Membership\Actions\MembershipStoreAction;
|
||||
|
@ -59,6 +60,7 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
|||
Route::post('/api/member/search', SearchAction::class)->name('member.search');
|
||||
Route::post('/api/membership/member-list', ApiListAction::class)->name('membership.member-list');
|
||||
Route::post('/api/membership/sync', SyncAction::class)->name('membership.sync');
|
||||
Route::post('/api/member/{member}/membership', ApiIndexAction::class)->name('member.membership.index');
|
||||
Route::get('/initialize', InitializeFormAction::class)->name('initialize.form');
|
||||
Route::post('/initialize', InitializeAction::class)->name('initialize.store');
|
||||
Route::resource('member', MemberController::class)->except('show', 'destroy');
|
||||
|
@ -72,9 +74,9 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
|||
->name('member.singlepdf');
|
||||
Route::get('/sendpayment', [SendpaymentController::class, 'create'])->name('sendpayment.create');
|
||||
Route::get('/sendpayment/pdf', [SendpaymentController::class, 'send'])->name('sendpayment.pdf');
|
||||
Route::post('/member/{member}/membership', MembershipStoreAction::class)->name('membership.store');
|
||||
Route::patch('/member/{member}/membership/{membership}', MembershipUpdateAction::class)->name('membership.store');
|
||||
Route::delete('/member/{member}/membership/{membership}', MembershipDestroyAction::class)->name('membership.destroy');
|
||||
Route::post('/member/{member}/membership', MembershipStoreAction::class)->name('member.membership.store');
|
||||
Route::patch('/membership/{membership}', MembershipUpdateAction::class)->name('membership.update');
|
||||
Route::delete('/membership/{membership}', MembershipDestroyAction::class)->name('membership.destroy');
|
||||
Route::resource('member.course', CourseController::class);
|
||||
Route::get('/member/{member}/efz', ShowEfzDocumentAction::class)->name('efz');
|
||||
Route::get('/member/{member}/resync', MemberResyncAction::class)->name('member.resync');
|
||||
|
|
|
@ -8,8 +8,6 @@ use App\Member\Member;
|
|||
use App\Member\Membership;
|
||||
use App\Payment\Payment;
|
||||
use App\Subactivity;
|
||||
use Carbon\Carbon;
|
||||
use Generator;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\RequestFactories\Child;
|
||||
use Tests\TestCase;
|
||||
|
@ -22,7 +20,7 @@ class IndexTest extends TestCase
|
|||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$group = Group::factory()->create();
|
||||
Member::factory()->defaults()->for($group)->create([
|
||||
$member = Member::factory()->defaults()->for($group)->create([
|
||||
'firstname' => '::firstname::',
|
||||
'address' => 'Kölner Str 3',
|
||||
'zip' => 33333,
|
||||
|
@ -36,6 +34,8 @@ class IndexTest extends TestCase
|
|||
$this->assertInertiaHas(false, $response, 'data.data.0.has_nami');
|
||||
$this->assertInertiaHas('Kölner Str 3, 33333 Hilden', $response, 'data.data.0.full_address');
|
||||
$this->assertInertiaHas($group->id, $response, 'data.data.0.group_id');
|
||||
$this->assertInertiaHas(null, $response, 'data.data.0.memberships');
|
||||
$this->assertInertiaHas(route('member.membership.index', ['member' => $member]), $response, 'data.data.0.links.membership_index');
|
||||
}
|
||||
|
||||
public function testFieldsCanBeNull(): void
|
||||
|
@ -82,29 +82,6 @@ class IndexTest extends TestCase
|
|||
$this->assertInertiaHas(false, $response, 'data.data.2.is_leader');
|
||||
}
|
||||
|
||||
public function membershipDataProvider(): Generator
|
||||
{
|
||||
yield [now()->subMonths(2), null, true];
|
||||
yield [now()->subMonths(2), now()->subDay(), false];
|
||||
yield [now()->addDays(2), null, false];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider membershipDataProvider
|
||||
*/
|
||||
public function testItShowsIfMembershipIsActive(Carbon $from, ?Carbon $to, bool $isActive): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->in('€ LeiterIn', 455, 'Pfadfinder', 15)->state(['from' => $from, 'to' => $to]))
|
||||
->create();
|
||||
|
||||
$response = $this->get('/member');
|
||||
|
||||
$this->assertInertiaHas($isActive, $response, 'data.data.0.memberships.0.is_active');
|
||||
}
|
||||
|
||||
public function testItHasNoEfzLinkWhenAddressIsMissing(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
|
@ -155,28 +132,6 @@ class IndexTest extends TestCase
|
|||
$this->assertInertiaHas('€ Mitglied', $response, "data.meta.formActivities.{$activity->id}");
|
||||
}
|
||||
|
||||
public function testItShowsActivityAndSubactivityNamesOfMember(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$group = Group::factory()->create();
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->for($group)->in('€ Mitglied', 122, 'Wölfling', 234)->from('2022-11-02'))
|
||||
->create();
|
||||
|
||||
$response = $this->get('/member');
|
||||
|
||||
$this->assertInertiaHas([
|
||||
'activity_id' => $member->memberships->first()->activity_id,
|
||||
'subactivity_id' => $member->memberships->first()->subactivity_id,
|
||||
'activity_name' => '€ Mitglied',
|
||||
'subactivity_name' => 'Wölfling',
|
||||
'human_date' => '02.11.2022',
|
||||
'group_id' => $group->id,
|
||||
'id' => $member->memberships->first()->id,
|
||||
], $response, 'data.data.0.memberships.0');
|
||||
}
|
||||
|
||||
public function testItReturnsPayments(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
|
|
|
@ -44,9 +44,9 @@ class DestroyTest extends TestCase
|
|||
->inNami(6)
|
||||
->create();
|
||||
|
||||
$response = $this->from('/member')->delete("/member/{$member->id}/membership/{$member->memberships->first()->id}");
|
||||
$response = $this->delete("/membership/{$member->memberships->first()->id}");
|
||||
|
||||
$response->assertRedirect('/member');
|
||||
$response->assertOk();
|
||||
$this->assertEquals(1506, $member->fresh()->version);
|
||||
$this->assertDatabaseMissing('memberships', [
|
||||
'member_id' => $member->id,
|
||||
|
@ -65,9 +65,8 @@ class DestroyTest extends TestCase
|
|||
->inNami(6)
|
||||
->create();
|
||||
|
||||
$response = $this->from('/member')->delete("/member/{$member->id}/membership/{$member->memberships->first()->id}");
|
||||
$response = $this->delete("/membership/{$member->memberships->first()->id}");
|
||||
|
||||
$response->assertRedirect('/member');
|
||||
$this->assertDatabaseMissing('memberships', [
|
||||
'member_id' => $member->id,
|
||||
]);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Membership;
|
||||
|
||||
use App\Group;
|
||||
use App\Member\Member;
|
||||
use App\Member\Membership;
|
||||
use Generator;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IndexTest extends TestCase
|
||||
{
|
||||
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function testItShowsActivityAndSubactivityNamesOfMember(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$group = Group::factory()->create(['name' => 'aaaaaaaa']);
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->for($group)
|
||||
->has(Membership::factory()->for($group)->in('€ Mitglied', 122, 'Wölfling', 234)->from('2022-11-02'))
|
||||
->create();
|
||||
$membership = $member->memberships->first();
|
||||
|
||||
$this->postJson("/api/member/{$member->id}/membership")
|
||||
->assertJsonPath('data.0.activity_id', $membership->activity_id)
|
||||
->assertJsonPath('data.0.subactivity_id', $membership->subactivity_id)
|
||||
->assertJsonPath('data.0.activity_name', '€ Mitglied')
|
||||
->assertJsonPath('data.0.subactivity_name', 'Wölfling')
|
||||
->assertJsonPath('data.0.human_date', '02.11.2022')
|
||||
->assertJsonPath('data.0.group_id', $group->id)
|
||||
->assertJsonPath('data.0.id', $membership->id)
|
||||
->assertJsonPath('data.0.links.update', route('membership.update', ['membership' => $membership]))
|
||||
->assertJsonPath('data.0.links.destroy', route('membership.destroy', ['membership' => $membership]))
|
||||
->assertJsonPath('meta.default.activity_id', null)
|
||||
->assertJsonPath('meta.default.group_id', $group->id)
|
||||
->assertJsonPath('meta.groups.0.id', $group->id)
|
||||
->assertJsonPath('meta.activities.0.id', $membership->activity_id)
|
||||
->assertJsonPath("meta.subactivities.{$membership->activity_id}.0.id", $membership->subactivity_id)
|
||||
->assertJsonPath('meta.links.store', route('member.membership.store', ['member' => $member]));
|
||||
}
|
||||
|
||||
public function membershipDataProvider(): Generator
|
||||
{
|
||||
yield [now()->subMonths(2), null, true];
|
||||
yield [now()->subMonths(2), now()->subDay(), false];
|
||||
yield [now()->addDays(2), null, false];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider membershipDataProvider
|
||||
*/
|
||||
public function testItShowsIfMembershipIsActive(Carbon $from, ?Carbon $to, bool $isActive): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->has(Membership::factory()->in('€ LeiterIn', 455, 'Pfadfinder', 15)->state(['from' => $from, 'to' => $to]))
|
||||
->create();
|
||||
|
||||
$this->postJson("/api/member/{$member->id}/membership")
|
||||
->assertJsonPath('data.0.is_active', $isActive);
|
||||
}
|
||||
}
|
|
@ -37,11 +37,11 @@ class UpdateTest extends TestCase
|
|||
$membership = $member->memberships->first();
|
||||
|
||||
$response = $this->from('/member')->patch(
|
||||
"/member/{$member->id}/membership/{$membership->id}",
|
||||
"/membership/{$membership->id}",
|
||||
MembershipRequestFactory::new()->promise(now())->in($membership->activity, $membership->subactivity)->create()
|
||||
);
|
||||
|
||||
$response->assertRedirect('/member');
|
||||
$response->assertOk();
|
||||
$this->assertDatabaseHas('memberships', [
|
||||
'member_id' => $member->id,
|
||||
'activity_id' => $activity->id,
|
||||
|
|
Loading…
Reference in New Issue