diff --git a/app/Member/FilterScope.php b/app/Member/FilterScope.php
index c5a2d219..2bed86a9 100644
--- a/app/Member/FilterScope.php
+++ b/app/Member/FilterScope.php
@@ -32,6 +32,8 @@ class FilterScope extends Filter
public array $groupIds = [],
public array $include = [],
public array $exclude = [],
+ public ?bool $hasFullAddress = null,
+ public ?bool $hasBirthday = null,
) {
}
@@ -60,6 +62,22 @@ class FilterScope extends Filter
$query->where('bill_kind', BillKind::fromValue($this->billKind));
}
+ if (true === $this->hasFullAddress) {
+ $query->whereNotNull('address')->whereNotNull('zip')->whereNotNull('location')->where('address', '!=', '')->where('zip', '!=', '')->where('location', '!=', '');
+ }
+
+ if (false === $this->hasFullAddress) {
+ $query->where(fn ($q) => $q
+ ->orWhere('address', '')->orWhereNull('address')
+ ->orWhere('zip', '')->orWhereNull('zip')
+ ->orWhere('location', '')->orWhereNull('location')
+ );
+ }
+
+ if (true === $this->hasBirthday) {
+ $query->whereNotNull('birthday');
+ }
+
if (count($this->groupIds)) {
$query->whereIn('group_id', $this->groupIds);
}
diff --git a/app/Member/Member.php b/app/Member/Member.php
index 13998e20..0d74a23d 100644
--- a/app/Member/Member.php
+++ b/app/Member/Member.php
@@ -161,7 +161,7 @@ class Member extends Model implements Geolocatable
public function getEfzLink(): ?string
{
- return $this->isLeader()
+ return $this->isLeader() && $this->address && $this->zip && $this->location && $this->birthday
? route('efz', ['member' => $this])
: null;
}
@@ -436,11 +436,14 @@ class Member extends Model implements Geolocatable
'VERSION' => '3.0',
'FN' => $this->fullname,
'N' => [$this->lastname, $this->firstname, '', '', ''],
- 'BDAY' => $this->birthday->format('Ymd'),
'CATEGORIES' => 'Scoutrobot',
'UID' => $this->slug,
]);
+ if ($this->birthday) {
+ $card->add('BDAY', $this->birthday->format('Ymd'));
+ }
+
if ($this->main_phone) {
$card->add('TEL', $this->main_phone, ['type' => 'voice']);
}
diff --git a/app/Member/MemberRequest.php b/app/Member/MemberRequest.php
index 12cb8397..4db6adda 100644
--- a/app/Member/MemberRequest.php
+++ b/app/Member/MemberRequest.php
@@ -12,6 +12,7 @@ use App\Subactivity;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
+use Illuminate\Validation\Validator;
use Zoomyboy\Phone\ValidPhoneRule;
class MemberRequest extends FormRequest
@@ -51,15 +52,10 @@ class MemberRequest extends FormRequest
}),
'firstname' => 'required',
'lastname' => 'required',
- 'address' => 'required',
- 'zip' => 'required|numeric',
- 'location' => 'required',
- 'birthday' => 'date|required',
'country_id' => 'required|exists:countries,id',
'email' => 'nullable|email',
'email_parents' => 'nullable|email',
'bill_kind' => ['nullable', Rule::in(BillKind::values())],
- 'joined_at' => 'date|required',
'confession_id' => 'nullable|exists:confessions,id',
'ps_at' => 'nullable|date_format:Y-m-d',
'more_ps_at' => 'nullable|date_format:Y-m-d',
@@ -76,7 +72,6 @@ class MemberRequest extends FormRequest
'invoice_address' => '',
'gender_id' => 'nullable|exists:genders,id',
'region_id' => 'nullable|exists:regions,id',
- 'nationality_id' => 'required|exists:nationalities,id',
'children_phone' => ['nullable', new ValidPhoneRule('Telefon (Kind)')],
'fax' => ['nullable', new ValidPhoneRule('Fax')],
'other_country' => '',
@@ -122,4 +117,23 @@ class MemberRequest extends FormRequest
}
ResyncAction::dispatch();
}
+
+ public function withValidator(Validator $validator): void
+ {
+ $this->namiIfElse($validator, 'birthday', 'date|required');
+ $this->namiIfElse($validator, 'nationality_id', 'required|exists:nationalities,id');
+ $this->namiIfElse($validator, 'address', 'required|max:255');
+ $this->namiIfElse($validator, 'zip', 'required|numeric');
+ $this->namiIfElse($validator, 'location', 'required|max:255');
+ $this->namiIfElse($validator, 'joined_at', 'date|required');
+ }
+
+ public function namiIfElse(Validator $validator, string $attribute, string $rules): void
+ {
+ $request = request();
+ $when = fn () => true === $request->input('has_nami');
+ $notWhen = fn () => true !== $request->input('has_nami');
+ $validator->sometimes($attribute, $rules, $when);
+ $validator->sometimes($attribute, 'present', $notWhen);
+ }
}
diff --git a/database/migrations/2023_07_23_203851_edit_members_table.php b/database/migrations/2023_07_23_203851_edit_members_table.php
new file mode 100644
index 00000000..7df859f3
--- /dev/null
+++ b/database/migrations/2023_07_23_203851_edit_members_table.php
@@ -0,0 +1,41 @@
+unsignedBigInteger('nationality_id')->nullable(true)->change();
+ $table->date('birthday')->nullable(true)->change();
+ $table->string('address')->nullable(true)->change();
+ $table->string('zip')->nullable(true)->change();
+ $table->string('location')->nullable(true)->change();
+ $table->date('joined_at')->nullable(true)->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('members', function (Blueprint $table) {
+ $table->unsignedBigInteger('nationality_id')->nullable(false)->change();
+ $table->date('birthday')->nullable(false)->change();
+ $table->string('address')->nullable(false)->change();
+ $table->string('zip')->nullable(false)->change();
+ $table->string('location')->nullable(false)->change();
+ $table->date('joined_at')->nullable(false)->change();
+ });
+ }
+};
diff --git a/resources/js/views/contribution/VIndex.vue b/resources/js/views/contribution/VIndex.vue
index 99995c7c..e5a34c60 100644
--- a/resources/js/views/contribution/VIndex.vue
+++ b/resources/js/views/contribution/VIndex.vue
@@ -86,6 +86,8 @@ export default {
var response = await this.axios.post('/api/member/search', {
filter: {
search: event,
+ hasBirthday: true,
+ hasFullAddress: true,
},
});
diff --git a/resources/js/views/member/VForm.vue b/resources/js/views/member/VForm.vue
index c2a916c4..ba27c954 100644
--- a/resources/js/views/member/VForm.vue
+++ b/resources/js/views/member/VForm.vue
@@ -27,14 +27,14 @@
-
+
-
+
-
-
-
+
+
+
diff --git a/resources/js/views/member/boxes/Stamm.vue b/resources/js/views/member/boxes/Stamm.vue
index f4ceb428..e35c2d7c 100644
--- a/resources/js/views/member/boxes/Stamm.vue
+++ b/resources/js/views/member/boxes/Stamm.vue
@@ -5,7 +5,7 @@
-
+
diff --git a/tests/EndToEnd/MemberIndexTest.php b/tests/EndToEnd/MemberIndexTest.php
index 8e1a716c..24953fc7 100644
--- a/tests/EndToEnd/MemberIndexTest.php
+++ b/tests/EndToEnd/MemberIndexTest.php
@@ -25,6 +25,33 @@ class MemberIndexTest extends TestCase
$this->assertCount(1, $this->inertia($response, 'data.data'));
}
+ public function testItHandlesAddress(): void
+ {
+ $this->withoutExceptionHandling()->login()->loginNami();
+ $group = Group::factory()->create();
+ Member::factory()->defaults()->for(Group::factory())->create(['address' => '']);
+ Member::factory()->defaults()->for(Group::factory())->create(['zip' => '']);
+ Member::factory()->defaults()->for(Group::factory())->create(['location' => '']);
+
+ $response = $this->callFilter('member.index', ['has_full_address' => true]);
+ $noResponse = $this->callFilter('member.index', ['has_full_address' => false]);
+
+ $this->assertCount(0, $this->inertia($response, 'data.data'));
+ $this->assertCount(3, $this->inertia($noResponse, 'data.data'));
+ }
+
+ public function testItHandlesBirthday(): void
+ {
+ $this->withoutExceptionHandling()->login()->loginNami();
+ $group = Group::factory()->create();
+ $member = Member::factory()->defaults()->for(Group::factory())->create(['birthday' => null]);
+
+ $response = $this->callFilter('member.index', ['has_birthday' => true]);
+
+ $this->assertCount(0, $this->inertia($response, 'data.data'));
+ $member->delete();
+ }
+
public function testItFiltersForSearchButNotForPayments(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
diff --git a/tests/Feature/Member/DavTest.php b/tests/Feature/Member/DavTest.php
index 73cc1b25..45e85e6e 100644
--- a/tests/Feature/Member/DavTest.php
+++ b/tests/Feature/Member/DavTest.php
@@ -76,6 +76,15 @@ VCARD;
$this->assertEquals('Max Muster', $card->FN->getValue());
}
+ public function testItDoesntNeedBirthday(): void
+ {
+ $member = Member::factory()->defaults()->create(['birthday' => null]);
+
+ $card = $member->toVcard();
+
+ $this->assertNull($card->BDAY);
+ }
+
public function testItSetsTheBirthday(): void
{
$member = Member::factory()->defaults()->create(['birthday' => '1993-05-06']);
diff --git a/tests/Feature/Member/IndexTest.php b/tests/Feature/Member/IndexTest.php
index 17c38130..4f02ae6c 100644
--- a/tests/Feature/Member/IndexTest.php
+++ b/tests/Feature/Member/IndexTest.php
@@ -80,6 +80,19 @@ class IndexTest extends TestCase
$this->assertInertiaHas(false, $response, 'data.data.2.is_leader');
}
+ public function testItHasNoEfzLinkWhenAddressIsMissing(): void
+ {
+ $this->withoutExceptionHandling()->login()->loginNami();
+ $member = Member::factory()
+ ->defaults()
+ ->has(Membership::factory()->in('€ LeiterIn', 455, 'Pfadfinder', 15))
+ ->create(['address' => null]);
+
+ $response = $this->get('/member');
+
+ $this->assertInertiaHas(null, $response, 'data.data.0.efz_link');
+ }
+
public function testItShowsAgeGroupIcon(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
@@ -93,6 +106,17 @@ class IndexTest extends TestCase
$this->assertInertiaHas('woelfling', $response, 'data.data.0.age_group_icon');
}
+ public function testAgeIsNullWhenBirthdayIsNull(): void
+ {
+ $this->withoutExceptionHandling()->login()->loginNami();
+ $member = Member::factory()->defaults()->create(['birthday' => null]);
+
+ $response = $this->get('/member');
+
+ $this->assertInertiaHas(null, $response, 'data.data.0.age');
+ $this->assertInertiaHas(null, $response, 'data.data.0.birthday');
+ }
+
public function testItShowsActivitiesAndSubactivities(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
diff --git a/tests/Feature/Member/SearchTest.php b/tests/Feature/Member/SearchTest.php
index db17f8e1..2be536ea 100644
--- a/tests/Feature/Member/SearchTest.php
+++ b/tests/Feature/Member/SearchTest.php
@@ -24,5 +24,4 @@ class SearchTest extends TestCase
$this->assertEquals('::firstname:: ::lastname:: Kölner Str 3, 33333 Hilden', $member->search_text);
}
-
}
diff --git a/tests/Feature/Member/StoreTest.php b/tests/Feature/Member/StoreTest.php
index 8deaf70e..268cfd8f 100644
--- a/tests/Feature/Member/StoreTest.php
+++ b/tests/Feature/Member/StoreTest.php
@@ -47,7 +47,7 @@ class StoreTest extends TestCase
'bill_kind' => 'Post',
'salutation' => 'Doktor',
'comment' => 'Lorem bla',
- ]));
+ ]))->assertSessionHasNoErrors();
$response->assertRedirect('/member')->assertSessionHasNoErrors();
$member = Member::firstWhere('firstname', 'Joe');
@@ -140,6 +140,30 @@ class StoreTest extends TestCase
]);
}
+ public function testItDoesntRequireBirthdayWhenNotInNami(): void
+ {
+ $this->login()->loginNami();
+
+ $this
+ ->post('/member', $this->attributes([
+ 'nationality_id' => null,
+ 'birthday' => null,
+ 'has_nami' => false,
+ 'address' => null,
+ 'zip' => null,
+ 'location' => null,
+ 'joined_at' => null,
+ ]));
+ $this->assertDatabaseHas('members', [
+ 'nationality_id' => null,
+ 'birthday' => null,
+ 'address' => null,
+ 'zip' => null,
+ 'location' => null,
+ 'joined_at' => null,
+ ]);
+ }
+
public function testItRequiresFields(): void
{
$this->login()->loginNami();
@@ -147,8 +171,13 @@ class StoreTest extends TestCase
$this
->post('/member', $this->attributes([
'nationality_id' => null,
+ 'birthday' => '',
+ 'address' => '',
+ 'zip' => '',
+ 'location' => '',
+ 'joined_at' => '',
]))
- ->assertSessionHasErrors(['nationality_id']);
+ ->assertSessionHasErrors(['nationality_id', 'birthday', 'address', 'zip', 'location', 'joined_at']);
}
public function testSubscriptionIsRequiredIfFirstActivityIsPaid(): void
diff --git a/tests/Feature/Member/UpdateTest.php b/tests/Feature/Member/UpdateTest.php
index 854e8fb9..e63286a5 100644
--- a/tests/Feature/Member/UpdateTest.php
+++ b/tests/Feature/Member/UpdateTest.php
@@ -73,6 +73,22 @@ class UpdateTest extends TestCase
]);
}
+ public function testItSetsLocationToNull(): void
+ {
+ $this->withoutExceptionHandling()->login()->loginNami();
+ $member = $this->member(['location' => 'Hilden', 'nami_id' => null]);
+ $this->fakeRequest();
+ NamiPutMemberAction::allowToRun();
+
+ $this->patch("/member/{$member->id}", array_merge($member->getAttributes(), [
+ 'location' => null,
+ ]));
+
+ $this->assertDatabaseHas('members', [
+ 'location' => null,
+ ]);
+ }
+
public function testItUpdatesContact(): void
{
$this->withoutExceptionHandling()->login()->loginNami();