From 65a6614831e361a10573a3964013e158a2fc328a Mon Sep 17 00:00:00 2001 From: philipp lang Date: Mon, 26 May 2025 14:26:24 +0200 Subject: [PATCH] --wip-- [skip ci] --- app/Member/Member.php | 33 ++++++++-- app/Prevention/Actions/SettingStoreAction.php | 2 + .../Contracts/YearlyPreventable.php | 20 ++++++ app/Prevention/Mails/YearlyMail.php | 65 +++++++++++++++++++ app/Prevention/PreventionSettings.php | 1 + ...create_prevention_yearly_mail_settings.php | 11 ++++ docker-compose.yml | 4 ++ resources/js/components/ui/Tabs.vue | 14 ++-- resources/js/views/setting/Prevention.vue | 15 +++-- tests/Feature/Member/PreventionTest.php | 12 ++++ tests/Feature/Prevention/SettingTest.php | 47 +++++++------- 11 files changed, 184 insertions(+), 40 deletions(-) create mode 100644 app/Prevention/Contracts/YearlyPreventable.php create mode 100644 app/Prevention/Mails/YearlyMail.php create mode 100644 database/settings/2025_05_24_202013_create_prevention_yearly_mail_settings.php diff --git a/app/Member/Member.php b/app/Member/Member.php index 78f993fe..efc74294 100644 --- a/app/Member/Member.php +++ b/app/Member/Member.php @@ -2,6 +2,7 @@ namespace App\Member; +use stdClass; use App\Confession; use App\Country; use App\Course\Models\CourseMember; @@ -13,6 +14,7 @@ use App\Nami\HasNamiField; use App\Nationality; use App\Payment\Subscription; use App\Pdf\Sender; +use App\Prevention\Contracts\YearlyPreventable; use App\Region; use App\Setting\NamiSettings; use Carbon\Carbon; @@ -40,7 +42,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne; * @property string $subscription_name * @property int $pending_payment */ -class Member extends Model implements Geolocatable +class Member extends Model implements Geolocatable, YearlyPreventable { use Notifiable; use HasNamiField; @@ -191,7 +193,7 @@ class Member extends Model implements Geolocatable protected function getAusstand(): int { - return (int) $this->invoicePositions()->whereHas('invoice', fn ($query) => $query->whereNeedsPayment())->sum('price'); + return (int) $this->invoicePositions()->whereHas('invoice', fn($query) => $query->whereNeedsPayment())->sum('price'); } // ---------------------------------- Relations ---------------------------------- @@ -339,7 +341,7 @@ class Member extends Model implements Geolocatable return $query->addSelect([ 'pending_payment' => InvoicePosition::selectRaw('SUM(price)') ->whereColumn('invoice_positions.member_id', 'members.id') - ->whereHas('invoice', fn ($query) => $query->whereNeedsPayment()), + ->whereHas('invoice', fn($query) => $query->whereNeedsPayment()), ]); } @@ -350,7 +352,7 @@ class Member extends Model implements Geolocatable */ public function scopeWhereHasPendingPayment(Builder $query): Builder { - return $query->whereHas('invoicePositions', fn ($q) => $q->whereHas('invoice', fn ($q) => $q->whereNeedsPayment())); + return $query->whereHas('invoicePositions', fn($q) => $q->whereHas('invoice', fn($q) => $q->whereNeedsPayment())); } /** @@ -499,7 +501,7 @@ class Member extends Model implements Geolocatable 'name' => $this->fullname, 'address' => $this->address, 'zipLocation' => $this->zip . ' ' . $this->location, - 'mglnr' => Lazy::create(fn () => 'Mglnr.: ' . $this->nami_id), + 'mglnr' => Lazy::create(fn() => 'Mglnr.: ' . $this->nami_id), ]); } @@ -508,7 +510,7 @@ class Member extends Model implements Geolocatable */ public static function forSelect(): array { - return static::select(['id', 'firstname', 'lastname'])->get()->map(fn ($member) => ['id' => $member->id, 'name' => $member->fullname])->toArray(); + return static::select(['id', 'firstname', 'lastname'])->get()->map(fn($member) => ['id' => $member->id, 'name' => $member->fullname])->toArray(); } // -------------------------------- Geolocation -------------------------------- @@ -567,7 +569,24 @@ class Member extends Model implements Geolocatable 'age_group_icon' => $this->ageGroupMemberships->first()?->subactivity->slug, 'is_leader' => $this->leaderMemberships()->count() > 0, 'memberships' => $this->memberships()->active()->get() - ->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]), + ->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]), ]; } + + // -------------------------------- Prevention --------------------------------- + // ***************************************************************************** + public function preventableSubject(): string + { + return 'Nachweise erforderlich'; + } + + public function preventableLayout(): string + { + return 'mail.prevention.prevention-remember-participant'; + } + + public function getMailRecipient(): stdClass + { + return (object) ['name' => $this->fullname, 'email' => $this->email]; + } } diff --git a/app/Prevention/Actions/SettingStoreAction.php b/app/Prevention/Actions/SettingStoreAction.php index cf72952c..18586524 100644 --- a/app/Prevention/Actions/SettingStoreAction.php +++ b/app/Prevention/Actions/SettingStoreAction.php @@ -19,6 +19,7 @@ class SettingStoreAction { return [ 'formmail' => 'array', + 'yearlymail' => 'array', ]; } @@ -26,6 +27,7 @@ class SettingStoreAction { $settings = app(PreventionSettings::class); $settings->formmail = EditorData::from($request->formmail); + $settings->yearlymail = EditorData::from($request->yearlymail); $settings->save(); Succeeded::message('Einstellungen gespeichert.')->dispatch(); diff --git a/app/Prevention/Contracts/YearlyPreventable.php b/app/Prevention/Contracts/YearlyPreventable.php new file mode 100644 index 00000000..f13201b5 --- /dev/null +++ b/app/Prevention/Contracts/YearlyPreventable.php @@ -0,0 +1,20 @@ + + */ + public function preventions(): array; + + public function getMailRecipient(): stdClass; +} diff --git a/app/Prevention/Mails/YearlyMail.php b/app/Prevention/Mails/YearlyMail.php new file mode 100644 index 00000000..ba5094de --- /dev/null +++ b/app/Prevention/Mails/YearlyMail.php @@ -0,0 +1,65 @@ +settings = app(InvoiceSettings::class); + $this->bodyText = $this->bodyText + ->replaceWithList('wanted', collect($preventable->preventions())->map(fn($prevention) => $prevention->text())->toArray()); + } + + /** + * Get the message envelope. + * + * @return \Illuminate\Mail\Mailables\Envelope + */ + public function envelope() + { + return (new Envelope( + subject: $this->preventable->preventableSubject(), + ))->to($this->preventable->getMailRecipient()->email, $this->preventable->getMailRecipient()->name); + } + + /** + * Get the message content definition. + * + * @return \Illuminate\Mail\Mailables\Content + */ + public function content() + { + return new Content( + markdown: $this->preventable->preventableLayout(), + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Prevention/PreventionSettings.php b/app/Prevention/PreventionSettings.php index 01bfb1f2..d1832a0a 100644 --- a/app/Prevention/PreventionSettings.php +++ b/app/Prevention/PreventionSettings.php @@ -9,6 +9,7 @@ class PreventionSettings extends LocalSettings { public EditorData $formmail; + public EditorData $yearlymail; public static function group(): string { diff --git a/database/settings/2025_05_24_202013_create_prevention_yearly_mail_settings.php b/database/settings/2025_05_24_202013_create_prevention_yearly_mail_settings.php new file mode 100644 index 00000000..97b3b2f5 --- /dev/null +++ b/database/settings/2025_05_24_202013_create_prevention_yearly_mail_settings.php @@ -0,0 +1,11 @@ +migrator->add('prevention.yearlymail', ['time' => 1, 'blocks' => [], 'version' => '1.0']); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index aa27c717..d90c7653 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -91,6 +91,8 @@ services: - ./data/db:/var/lib/mysql socketi: + ports: + - '6001:6001' image: quay.io/soketi/soketi:89604f268623cf799573178a7ba56b7491416bde-16-debian environment: SOKETI_DEFAULT_APP_ID: adremaid @@ -103,6 +105,8 @@ services: - ./data/redis:/data meilisearch: + ports: + - '7700:7700' image: getmeili/meilisearch:v1.6 volumes: - ./data/meilisearch:/meili_data diff --git a/resources/js/components/ui/Tabs.vue b/resources/js/components/ui/Tabs.vue index ed696c37..a636d739 100644 --- a/resources/js/components/ui/Tabs.vue +++ b/resources/js/components/ui/Tabs.vue @@ -1,9 +1,15 @@ diff --git a/resources/js/views/setting/Prevention.vue b/resources/js/views/setting/Prevention.vue index 9085ea2d..36d23f6a 100644 --- a/resources/js/views/setting/Prevention.vue +++ b/resources/js/views/setting/Prevention.vue @@ -3,24 +3,31 @@ +

Hier kannst du Einstellungen zu Prävention setzen.

-
- -
+ + +