Compare commits
	
		
			79 Commits
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 4644f3832b | |
|  | d68b166e15 | |
|  | 5d103b1908 | |
|  | 195fcc97b2 | |
|  | ef4f4e68fb | |
|  | 7c73b489ce | |
|  | 74db2f7c6b | |
|  | 458d68c271 | |
|  | 471f766638 | |
|  | dc3eb68861 | |
|  | 003e30d423 | |
|  | 5d40c0b2bb | |
|  | e60105e5af | |
|  | 2fadba3193 | |
|  | 357a696a53 | |
|  | 79fed8c002 | |
|  | 9b02bddea9 | |
|  | 1b6057673a | |
|  | 5c9c59718c | |
|  | c1e0842457 | |
|  | a61fd55b29 | |
|  | 4156a8744a | |
|  | 84b17b312f | |
|  | 6906b7c097 | |
|  | 30c84f0cfd | |
|  | d765bad0ed | |
|  | 89ce931f9d | |
|  | 2fac4d5a73 | |
|  | 56090b0104 | |
|  | 131061a3ee | |
|  | 9733c260b2 | |
|  | e2e9ddb9fd | |
|  | 511a58ded3 | |
|  | b937a8f4fa | |
|  | cb01496028 | |
|  | 92284b053c | |
|  | 39f0d476fb | |
|  | e6aa5e2c61 | |
|  | d225be6137 | |
|  | 6094a16c36 | |
|  | 34355d9b32 | |
|  | 7a39798e8b | |
|  | b07ad8d739 | |
|  | 5cd164f0d8 | |
|  | 652b83a140 | |
|  | 4d0923a01f | |
|  | 1de148eecc | |
|  | 12f053428c | |
|  | 0ce797c874 | |
|  | 8e2487c719 | |
|  | 2b1fe6ddcd | |
|  | fe8a2d69e6 | |
|  | 76d6c4ef90 | |
|  | 8bd66010dd | |
|  | bb9c219d81 | |
|  | b52eedc254 | |
|  | a06fd01194 | |
|  | 4a731f1104 | |
|  | 278a598078 | |
|  | 3c72b890fb | |
|  | 7de12a92dd | |
|  | 643959ff04 | |
|  | 5d16d65f8a | |
|  | 985b7942c4 | |
|  | 6582f49321 | |
|  | 5998230569 | |
|  | 0dbac1da4a | |
|  | 4b507aa297 | |
|  | af45560fe1 | |
|  | cecc90f07b | |
|  | 5b116a54c4 | |
|  | 0070976e34 | |
|  | dd07f6dbd7 | |
|  | ab74abdfdf | |
|  | 8f5448bbbc | |
|  | 2fd6598ef3 | |
|  | 0c90f66a66 | |
|  | b541d246e0 | |
|  | 858ae7f8d1 | 
|  | @ -1,4 +1,4 @@ | ||||||
| FROM php:8.3.11-fpm AS php | FROM php:8.3.11-fpm as php | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| RUN ls /app | RUN ls /app | ||||||
| RUN apt-get update | RUN apt-get update | ||||||
|  |  | ||||||
|  | @ -1,16 +1,16 @@ | ||||||
| FROM composer:2.7.9 AS composer | FROM composer:2.7.9 as composer | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| COPY . /app | COPY . /app | ||||||
| RUN composer install --ignore-platform-reqs --no-dev | RUN composer install --ignore-platform-reqs --no-dev | ||||||
| RUN php artisan telescope:publish | RUN php artisan telescope:publish | ||||||
| RUN php artisan horizon:publish | RUN php artisan horizon:publish | ||||||
| 
 | 
 | ||||||
| FROM node:20.15.0-slim AS node | FROM node:20.15.0-slim as node | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| COPY . /app | COPY . /app | ||||||
| RUN npm install && npm run prod && npm run img && rm -R node_modules | RUN npm install && npm run prod && npm run img && rm -R node_modules | ||||||
| 
 | 
 | ||||||
| FROM nginx:1.21.6-alpine AS nginx | FROM nginx:1.21.6-alpine as nginx | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| COPY --from=node /app /app | COPY --from=node /app /app | ||||||
| COPY --from=composer /app/public/vendor /app/public/vendor | COPY --from=composer /app/public/vendor /app/public/vendor | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| FROM composer:2.7.9 AS composer | FROM composer:2.7.9 as composer | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| COPY . /app | COPY . /app | ||||||
| RUN composer install --ignore-platform-reqs --no-dev | RUN composer install --ignore-platform-reqs --no-dev | ||||||
| 
 | 
 | ||||||
| FROM node:20.15.0-slim AS node | FROM node:20.15.0-slim as node | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| COPY . /app | COPY . /app | ||||||
| RUN npm install && npm run prod && npm run img && rm -R node_modules | RUN npm install && npm run prod && npm run img && rm -R node_modules | ||||||
| 
 | 
 | ||||||
| FROM zoomyboy/adrema-base:latest AS php | FROM zoomyboy/adrema-base:latest as php | ||||||
| COPY --chown=www-data:www-data . /app | COPY --chown=www-data:www-data . /app | ||||||
| COPY --chown=www-data:www-data --from=node /app/public /app/public | COPY --chown=www-data:www-data --from=node /app/public /app/public | ||||||
| COPY --chown=www-data:www-data --from=composer /app/vendor /app/vendor | COPY --chown=www-data:www-data --from=composer /app/vendor /app/vendor | ||||||
|  |  | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | { | ||||||
|  |     "env": { | ||||||
|  |         "browser": true, | ||||||
|  |         "es2021": true | ||||||
|  |     }, | ||||||
|  |     "extends": [ | ||||||
|  |         "eslint:recommended", | ||||||
|  |         "plugin:vue/vue3-recommended", | ||||||
|  |         "prettier" | ||||||
|  |     ], | ||||||
|  |     "parserOptions": { | ||||||
|  |         "ecmaVersion": "latest", | ||||||
|  |         "sourceType": "module" | ||||||
|  |     }, | ||||||
|  |     "plugins": [ | ||||||
|  |         "vue" | ||||||
|  |     ], | ||||||
|  |     "overrides": [ | ||||||
|  |         { | ||||||
|  |             "files": [ | ||||||
|  |                 "*.vue" | ||||||
|  |             ], | ||||||
|  |             "rules": { | ||||||
|  |                 "vue/multi-word-component-names": "off" | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										38
									
								
								CHANGELOG.md
								
								
								
								
							|  | @ -1,43 +1,5 @@ | ||||||
| # Letzte Änderungen | # Letzte Änderungen | ||||||
| 
 | 
 | ||||||
| ### 1.12.20 |  | ||||||
| 
 |  | ||||||
| -   Nachnelde-Link kann nun erstellt werden für Veranstaltungen |  | ||||||
| 
 |  | ||||||
| ### 1.12.19 |  | ||||||
| 
 |  | ||||||
| -   Zuschusslisten können nun aus Veranstaltungs-Daten erstellt werden |  | ||||||
| -   Veranstaltungs-Übersicht zeigt nun Tags an |  | ||||||
| 
 |  | ||||||
| ### 1.12.18 |  | ||||||
| 
 |  | ||||||
| -   Fix: Initialisierung klappt nun auch, wenn Mitgliedsnummer mit einer 0 beginnt |  | ||||||
| 
 |  | ||||||
| ### 1.12.17 |  | ||||||
| 
 |  | ||||||
| -   Fix: Mitgliedschaften werden beim Sammel-Speichern nicht mehr doppelt angelegt |  | ||||||
| 
 |  | ||||||
| ### 1.12.16 |  | ||||||
| 
 |  | ||||||
| -   Mitgliedschaften können nun bei Mitgliedschaften-Übersicht gelöscht werden |  | ||||||
| 
 |  | ||||||
| ### 1.12.15 |  | ||||||
| 
 |  | ||||||
| -   Bestätigung wird eingeblendet beim Kopieren eines Events |  | ||||||
| 
 |  | ||||||
| ### 1.12.14 |  | ||||||
| 
 |  | ||||||
| -   Bilder werden nun mitkopiert beim Kopieren eines Events |  | ||||||
| 
 |  | ||||||
| ### 1.12.13 |  | ||||||
| 
 |  | ||||||
| -   Kopieren von bestehenden Veranstaltungen |  | ||||||
| -   Präventions-Erinnerung automatisch versenden |  | ||||||
| 
 |  | ||||||
| ### 1.12.11 |  | ||||||
| 
 |  | ||||||
| -   Fix: Bank Account mit abrufen wenn Mitglied editiert wird |  | ||||||
| 
 |  | ||||||
| ### 1.12.7 | ### 1.12.7 | ||||||
| 
 | 
 | ||||||
| -   Fix: Synchronisation von allen Mitgliedern bei Mail-Verteilern - nicht nur den ersten 20 | -   Fix: Synchronisation von allen Mitgliedern bei Mail-Verteilern - nicht nur den ersten 20 | ||||||
|  |  | ||||||
							
								
								
									
										25
									
								
								README.md
								
								
								
								
							
							
						
						
									
										25
									
								
								README.md
								
								
								
								
							|  | @ -32,16 +32,16 @@ Außerdem ist AdReMa auch problemlos auf Handys und Tablets bedienbar ("mobiles | ||||||
| 
 | 
 | ||||||
| ## Installation des Produktivsystems | ## Installation des Produktivsystems | ||||||
| 
 | 
 | ||||||
| 1. Verschieben der Docker-Compose | 1. Herunterladen der Beispiel Docker-Compose | ||||||
| 
 | 
 | ||||||
|     ```cmd |     ```cmd | ||||||
|     mv docker-compose.prod.yml docker-compose.yml |     curl https://git.zoomyboy.de/silva/adrema/raw/branch/master/docker-compose.prod.yml -o docker-compose.yml | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 2. Anwenden der Beispiel Environmentvariablen-Datei | 2. Herunterladen der Beispiel Environmentvariablen-Datei | ||||||
| 
 | 
 | ||||||
|     ```cmd |     ```cmd | ||||||
|     mv .app.env.example .app.env |     curl https://git.zoomyboy.de/silva/adrema/raw/branch/master/.app.env.example -o .app.env | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 3. In der `.app.env` notwendige Einstellungen vornehmen: | 3. In der `.app.env` notwendige Einstellungen vornehmen: | ||||||
|  | @ -50,8 +50,7 @@ Außerdem ist AdReMa auch problemlos auf Handys und Tablets bedienbar ("mobiles | ||||||
|     - Mail-Server Einstellungen `MAIL_PORT`, `MAIL_HOST`, `MAIL_USERNAME`, `MAIL_PASSWORD` und `MAIL_ENCRYPTION` anpassen |     - Mail-Server Einstellungen `MAIL_PORT`, `MAIL_HOST`, `MAIL_USERNAME`, `MAIL_PASSWORD` und `MAIL_ENCRYPTION` anpassen | ||||||
|     - `MAIL_FROM_NAME`: Der Name, der als Absender von E-Mails gesetzt wird (z.B. `Stamm Bipi Service`) |     - `MAIL_FROM_NAME`: Der Name, der als Absender von E-Mails gesetzt wird (z.B. `Stamm Bipi Service`) | ||||||
|     - `MAIL_FROM_ADDRESS`: Die dazu gehörige E-Mail-Adresse, die natürlich für antworten erreichbar sein sollte (z.B. `vorstand@stamm-bipi.de`) |     - `MAIL_FROM_ADDRESS`: Die dazu gehörige E-Mail-Adresse, die natürlich für antworten erreichbar sein sollte (z.B. `vorstand@stamm-bipi.de`) | ||||||
|     - `DB_PASSWORD` und `MYSQL_PASSWORD`: Mit dem selben neu erstellten, sicheren Passwort für die Datenbank versehen |     - `DB_PASSWORD` und `MYSQL_PASSWORD`: Mit dem selben sicheren Passwort für die Datenbank versehen | ||||||
|     - `MEILI_MASTER_KEY` Mit einem neu erstellten, sicheren Passwort versehen |  | ||||||
|     - `USER_EMAIL` und `USER_PASSWORD`: Einstellen des standard Adrema Logins |     - `USER_EMAIL` und `USER_PASSWORD`: Einstellen des standard Adrema Logins | ||||||
| 
 | 
 | ||||||
| 4. Container zur Gennerierung des App-Key starten | 4. Container zur Gennerierung des App-Key starten | ||||||
|  | @ -100,22 +99,28 @@ Bei dem Setup wird im Daten-Verzeichniss ein Ordner `./data/setup` angelegt. Hie | ||||||
|     git clone https://git.zoomyboy.de/silva/adrema.git |     git clone https://git.zoomyboy.de/silva/adrema.git | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 2. Kopieren der Beispiel Environmentvariablen-Datei | 2. Kopieren der Beispiel Docker-Compose für das Entwickeln und nach Wünschen anpassen | ||||||
|  | 
 | ||||||
|  |     ```cmd | ||||||
|  |     cp docker-compose.dev.yml docker-compose.yml | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | 3. Kopieren der Beispiel Environmentvariablen-Datei | ||||||
| 
 | 
 | ||||||
|     ```cmd |     ```cmd | ||||||
|     cp .app.env.example .app.env |     cp .app.env.example .app.env | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 3. Submodule aktuallisieren | 4. Submodule aktuallisieren | ||||||
| 
 | 
 | ||||||
|     ```cmd |     ```cmd | ||||||
|     git submodule update --init |     git submodule update --init | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 4. Container erstellen | 5. Container erstellen | ||||||
| 
 | 
 | ||||||
|     ```cmd |     ```cmd | ||||||
|     docker compose build |     docker compose build | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 5. Mit Schritt 3 und den folgenden der [Installation des Produktivsystems](#installation-des-produktivsystems) fortfahren | 6. Mit Schritt 3 und den folgenden der [Installation des Produktivsystems](#installation-des-produktivsystems) fortfahren | ||||||
|  |  | ||||||
|  | @ -54,7 +54,6 @@ class ActivityResource extends JsonResource | ||||||
|                 'index' => route('activity.index'), |                 'index' => route('activity.index'), | ||||||
|                 'create' => route('activity.create'), |                 'create' => route('activity.create'), | ||||||
|                 'membership_masslist' => route('membership.masslist.index'), |                 'membership_masslist' => route('membership.masslist.index'), | ||||||
|                 'membership_index' => route('membership.index'), |  | ||||||
|             ], |             ], | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ use App\Actions\DbMaintainAction; | ||||||
| use App\Form\Actions\PreventionRememberAction; | use App\Form\Actions\PreventionRememberAction; | ||||||
| use App\Initialize\InitializeMembers; | use App\Initialize\InitializeMembers; | ||||||
| use App\Invoice\Actions\InvoiceSendAction; | use App\Invoice\Actions\InvoiceSendAction; | ||||||
| use App\Prevention\Actions\YearlyRememberAction; |  | ||||||
| use Illuminate\Console\Scheduling\Schedule; | use Illuminate\Console\Scheduling\Schedule; | ||||||
| use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||||
| 
 | 
 | ||||||
|  | @ -22,7 +21,6 @@ class Kernel extends ConsoleKernel | ||||||
|         InitializeMembers::class, |         InitializeMembers::class, | ||||||
|         DbMaintainAction::class, |         DbMaintainAction::class, | ||||||
|         PreventionRememberAction::class, |         PreventionRememberAction::class, | ||||||
|         YearlyRememberAction::class, |  | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -36,7 +34,6 @@ class Kernel extends ConsoleKernel | ||||||
|         $schedule->command(InitializeMembers::class)->dailyAt('03:00'); |         $schedule->command(InitializeMembers::class)->dailyAt('03:00'); | ||||||
|         $schedule->command(PreventionRememberAction::class)->dailyAt('11:00'); |         $schedule->command(PreventionRememberAction::class)->dailyAt('11:00'); | ||||||
|         $schedule->command(InvoiceSendAction::class)->dailyAt('10:00'); |         $schedule->command(InvoiceSendAction::class)->dailyAt('10:00'); | ||||||
|         $schedule->command(YearlyRememberAction::class)->dailyAt('09:00'); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -2,11 +2,11 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Actions; | namespace App\Contribution\Actions; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\ContributionFactory; | use App\Contribution\ContributionFactory; | ||||||
| use App\Contribution\Requests\GenerateRequest; | use App\Contribution\Documents\ContributionDocument; | ||||||
| use App\Rules\JsonBase64Rule; | use App\Rules\JsonBase64Rule; | ||||||
| use Illuminate\Http\JsonResponse; | use Illuminate\Support\Facades\Validator; | ||||||
|  | use Lorisleiva\Actions\ActionRequest; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
| use Zoomyboy\Tex\BaseCompiler; | use Zoomyboy\Tex\BaseCompiler; | ||||||
| use Zoomyboy\Tex\Tex; | use Zoomyboy\Tex\Tex; | ||||||
|  | @ -15,19 +15,23 @@ class GenerateAction | ||||||
| { | { | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     public function handle(HasContributionData $request): BaseCompiler |     /** | ||||||
|  |      * @param class-string<ContributionDocument> $document | ||||||
|  |      * @param array<string, mixed>               $payload | ||||||
|  |      */ | ||||||
|  |     public function handle(string $document, array $payload): BaseCompiler | ||||||
|     { |     { | ||||||
|         return Tex::compile($request->type()::fromPayload($request)); |         return Tex::compile($document::fromRequest($payload)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function asController(GenerateRequest $request): BaseCompiler|JsonResponse |     public function asController(ActionRequest $request): BaseCompiler | ||||||
|     { |     { | ||||||
|         app(ContributionFactory::class)->validateType($request); |         $payload = $this->payload($request); | ||||||
|         $request->validateContribution(); |         $type = data_get($payload, 'type'); | ||||||
|  |         ValidateAction::validateType($type); | ||||||
|  |         Validator::make($payload, app(ContributionFactory::class)->rules($type))->validate(); | ||||||
| 
 | 
 | ||||||
|         return $request->input('validate') |         return $this->handle($type, $payload); | ||||||
|             ? response()->json([]) |  | ||||||
|             : $this->handle($request); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -39,4 +43,12 @@ class GenerateAction | ||||||
|             'payload' => [new JsonBase64Rule()], |             'payload' => [new JsonBase64Rule()], | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return array<string, string> | ||||||
|  |      */ | ||||||
|  |     private function payload(ActionRequest $request): array | ||||||
|  |     { | ||||||
|  |         return json_decode(rawurldecode(base64_decode($request->input('payload', ''))), true); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,9 +2,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Actions; | namespace App\Contribution\Actions; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; | use App\Contribution\Documents\ContributionDocument; | ||||||
| use App\Contribution\ContributionFactory; | use Lorisleiva\Actions\ActionRequest; | ||||||
| use App\Contribution\Requests\GenerateApiRequest; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
| use Zoomyboy\Tex\BaseCompiler; | use Zoomyboy\Tex\BaseCompiler; | ||||||
| use Zoomyboy\Tex\Tex; | use Zoomyboy\Tex\Tex; | ||||||
|  | @ -14,17 +13,26 @@ class GenerateApiAction | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @todo merge this with GenerateAction |      * @param class-string<ContributionDocument> $document | ||||||
|  |      * @param array<string, mixed>               $payload | ||||||
|      */ |      */ | ||||||
|     public function handle(HasContributionData $request): BaseCompiler |     public function handle(string $document, array $payload): BaseCompiler | ||||||
|     { |     { | ||||||
|         return Tex::compile($request->type()::fromPayload($request)); |         return Tex::compile($document::fromApiRequest($payload)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function asController(GenerateApiRequest $request): BaseCompiler |     public function asController(ActionRequest $request): BaseCompiler | ||||||
|     { |     { | ||||||
|         app(ContributionFactory::class)->validateType($request); |         ValidateAction::validateType($request->input('type')); | ||||||
| 
 | 
 | ||||||
|         return $this->handle($request); |         return $this->handle($request->input('type'), $request->input()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return array<string, mixed> | ||||||
|  |      */ | ||||||
|  |     public function rules(): array | ||||||
|  |     { | ||||||
|  |         return []; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Contribution\Actions; | ||||||
|  | 
 | ||||||
|  | use App\Contribution\ContributionFactory; | ||||||
|  | use Illuminate\Http\JsonResponse; | ||||||
|  | use Illuminate\Support\Facades\Validator; | ||||||
|  | use Lorisleiva\Actions\ActionRequest; | ||||||
|  | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
|  | 
 | ||||||
|  | class ValidateAction | ||||||
|  | { | ||||||
|  |     use AsAction; | ||||||
|  | 
 | ||||||
|  |     public function asController(): JsonResponse | ||||||
|  |     { | ||||||
|  |         return response()->json(['valid' => true]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return array<string, mixed> | ||||||
|  |      */ | ||||||
|  |     public function rules(): array | ||||||
|  |     { | ||||||
|  |         return app(ContributionFactory::class)->rules(request()->type); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function prepareForValidation(ActionRequest $request): void | ||||||
|  |     { | ||||||
|  |         static::validateType($request->input('type')); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static function validateType(?string $type = null): void | ||||||
|  |     { | ||||||
|  |         Validator::make(['type' => $type], app(ContributionFactory::class)->typeRule())->validate(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Contribution\Contracts; |  | ||||||
| 
 |  | ||||||
| use App\Contribution\Data\MemberData; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| use App\Contribution\Documents\ContributionDocument; |  | ||||||
| use App\Country; |  | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| 
 |  | ||||||
| interface HasContributionData { |  | ||||||
| 
 |  | ||||||
|     public function dateFrom(): Carbon; |  | ||||||
|     public function dateUntil(): Carbon; |  | ||||||
|     public function zipLocation(): string; |  | ||||||
|     public function eventName(): string; |  | ||||||
|     /** |  | ||||||
|      * @return class-string<ContributionDocument> |  | ||||||
|      */ |  | ||||||
|     public function type(): string; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return Collection<int, MemberData> |  | ||||||
|      */ |  | ||||||
|     public function members(): Collection; |  | ||||||
| 
 |  | ||||||
|     public function country(): ?Country; |  | ||||||
| 
 |  | ||||||
|     public function validateContribution(): void; |  | ||||||
| } |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution; | namespace App\Contribution; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Documents\BdkjHesse; | use App\Contribution\Documents\BdkjHesse; | ||||||
| use App\Contribution\Documents\ContributionDocument; | use App\Contribution\Documents\ContributionDocument; | ||||||
| use App\Contribution\Documents\RdpNrwDocument; | use App\Contribution\Documents\RdpNrwDocument; | ||||||
|  | @ -11,7 +10,6 @@ use App\Contribution\Documents\CitySolingenDocument; | ||||||
| use App\Contribution\Documents\CityFrankfurtMainDocument; | use App\Contribution\Documents\CityFrankfurtMainDocument; | ||||||
| use App\Contribution\Documents\WuppertalDocument; | use App\Contribution\Documents\WuppertalDocument; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Illuminate\Support\Facades\Validator; |  | ||||||
| use Illuminate\Validation\Rule; | use Illuminate\Validation\Rule; | ||||||
| 
 | 
 | ||||||
| class ContributionFactory | class ContributionFactory | ||||||
|  | @ -29,13 +27,13 @@ class ContributionFactory | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return Collection<int, array{name: string, id: class-string<ContributionDocument>}> |      * @return Collection<int, array{title: string, class: class-string<ContributionDocument>}> | ||||||
|      */ |      */ | ||||||
|     public function compilerSelect(): Collection |     public function compilerSelect(): Collection | ||||||
|     { |     { | ||||||
|         return collect($this->documents)->map(fn ($document) => [ |         return collect($this->documents)->map(fn ($document) => [ | ||||||
|             'name' => $document::getName(), |             'title' => $document::buttonName(), | ||||||
|             'id' => $document, |             'class' => $document, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -61,9 +59,4 @@ class ContributionFactory | ||||||
|             ...$type::rules(), |             ...$type::rules(), | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public function validateType(HasContributionData $request): void { |  | ||||||
|         Validator::make(['type' => $request->type()], $this->typeRule())->validate(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -47,7 +47,7 @@ class MemberData extends Data | ||||||
|         return collect($data)->map(fn ($member) => self::factory()->withoutMagicalCreation()->from([ |         return collect($data)->map(fn ($member) => self::factory()->withoutMagicalCreation()->from([ | ||||||
|             ...$member, |             ...$member, | ||||||
|             'birthday' => Carbon::parse($member['birthday'])->toAtomString(), |             'birthday' => Carbon::parse($member['birthday'])->toAtomString(), | ||||||
|             'gender' => $member['gender'] ? Gender::fromString($member['gender']) : null, |             'gender' => Gender::fromString($member['gender']), | ||||||
|             'isLeader' => $member['is_leader'], |             'isLeader' => $member['is_leader'], | ||||||
|         ])); |         ])); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -2,11 +2,9 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Data\MemberData; | use App\Contribution\Data\MemberData; | ||||||
| use App\Contribution\Traits\HasPdfBackground; | use App\Contribution\Traits\HasPdfBackground; | ||||||
| use App\Country; | use App\Country; | ||||||
| use App\Form\Enums\SpecialType; |  | ||||||
| use Carbon\Carbon; | use Carbon\Carbon; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| 
 | 
 | ||||||
|  | @ -19,8 +17,8 @@ class BdkjHesse extends ContributionDocument | ||||||
|      * @param Collection<int, Collection<int, MemberData>> $members |      * @param Collection<int, Collection<int, MemberData>> $members | ||||||
|      */ |      */ | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         public Carbon $dateFrom, |         public string $dateFrom, | ||||||
|         public Carbon $dateUntil, |         public string $dateUntil, | ||||||
|         public string $zipLocation, |         public string $zipLocation, | ||||||
|         public ?Country $country, |         public ?Country $country, | ||||||
|         public Collection $members, |         public Collection $members, | ||||||
|  | @ -41,15 +39,33 @@ class BdkjHesse extends ContributionDocument | ||||||
|         return Carbon::parse($this->dateUntil)->format('d.m.Y'); |         return Carbon::parse($this->dateUntil)->format('d.m.Y'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function fromPayload(HasContributionData $request): self |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromRequest(array $request): self | ||||||
|     { |     { | ||||||
|         return new self( |         return new self( | ||||||
|             dateFrom: $request->dateFrom(), |             dateFrom: $request['dateFrom'], | ||||||
|             dateUntil: $request->dateUntil(), |             dateUntil: $request['dateUntil'], | ||||||
|             zipLocation: $request->zipLocation(), |             zipLocation: $request['zipLocation'], | ||||||
|             country: $request->country(), |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|             members: $request->members()->chunk(20), |             members: MemberData::fromModels($request['members'])->chunk(20), | ||||||
|             eventName: $request->eventName(), |             eventName: $request['eventName'], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromApiRequest(array $request): self | ||||||
|  |     { | ||||||
|  |         return new self( | ||||||
|  |             dateFrom: $request['dateFrom'], | ||||||
|  |             dateUntil: $request['dateUntil'], | ||||||
|  |             zipLocation: $request['zipLocation'], | ||||||
|  |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|  |             members: MemberData::fromApi($request['member_data'])->chunk(20), | ||||||
|  |             eventName: $request['eventName'], | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -117,15 +133,4 @@ class BdkjHesse extends ContributionDocument | ||||||
|             'zipLocation' => 'required|string', |             'zipLocation' => 'required|string', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public static function requiredFormSpecialTypes(): array { |  | ||||||
|         return [ |  | ||||||
|             SpecialType::FIRSTNAME, |  | ||||||
|             SpecialType::LASTNAME, |  | ||||||
|             SpecialType::BIRTHDAY, |  | ||||||
|             SpecialType::ZIP, |  | ||||||
|             SpecialType::LOCATION, |  | ||||||
|             SpecialType::GENDER, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,15 +2,12 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Data\MemberData; | use App\Contribution\Data\MemberData; | ||||||
| use App\Contribution\Traits\FormatsDates; | use App\Contribution\Traits\FormatsDates; | ||||||
| use App\Contribution\Traits\HasPdfBackground; | use App\Contribution\Traits\HasPdfBackground; | ||||||
| use App\Country; | use App\Country; | ||||||
| use App\Form\Enums\SpecialType; | use Modules\Invoice\InvoiceSettings; | ||||||
| use App\Invoice\InvoiceSettings; |  | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 | 
 | ||||||
| class CityFrankfurtMainDocument extends ContributionDocument | class CityFrankfurtMainDocument extends ContributionDocument | ||||||
| { | { | ||||||
|  | @ -23,8 +20,8 @@ class CityFrankfurtMainDocument extends ContributionDocument | ||||||
|      * @param Collection<int, Collection<int, MemberData>> $members |      * @param Collection<int, Collection<int, MemberData>> $members | ||||||
|      */ |      */ | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         public Carbon $dateFrom, |         public string $dateFrom, | ||||||
|         public Carbon $dateUntil, |         public string $dateUntil, | ||||||
|         public string $zipLocation, |         public string $zipLocation, | ||||||
|         public ?Country $country, |         public ?Country $country, | ||||||
|         public Collection $members, |         public Collection $members, | ||||||
|  | @ -36,15 +33,33 @@ class CityFrankfurtMainDocument extends ContributionDocument | ||||||
|         $this->fromName = app(InvoiceSettings::class)->from_long; |         $this->fromName = app(InvoiceSettings::class)->from_long; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function fromPayload(HasContributionData $request): self |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromRequest(array $request): self | ||||||
|     { |     { | ||||||
|         return new self( |         return new self( | ||||||
|             dateFrom: $request->dateFrom(), |             dateFrom: $request['dateFrom'], | ||||||
|             dateUntil: $request->dateUntil(), |             dateUntil: $request['dateUntil'], | ||||||
|             zipLocation: $request->zipLocation(), |             zipLocation: $request['zipLocation'], | ||||||
|             country: $request->country(), |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|             members: $request->members()->chunk(15), |             members: MemberData::fromModels($request['members'])->chunk(15), | ||||||
|             eventName: $request->eventName(), |             eventName: $request['eventName'], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromApiRequest(array $request): self | ||||||
|  |     { | ||||||
|  |         return new self( | ||||||
|  |             dateFrom: $request['dateFrom'], | ||||||
|  |             dateUntil: $request['dateUntil'], | ||||||
|  |             zipLocation: $request['zipLocation'], | ||||||
|  |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|  |             members: MemberData::fromApi($request['member_data'])->chunk(15), | ||||||
|  |             eventName: $request['eventName'], | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -75,15 +90,4 @@ class CityFrankfurtMainDocument extends ContributionDocument | ||||||
|             'zipLocation' => 'required|string', |             'zipLocation' => 'required|string', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public static function requiredFormSpecialTypes(): array { |  | ||||||
|         return [ |  | ||||||
|             SpecialType::FIRSTNAME, |  | ||||||
|             SpecialType::LASTNAME, |  | ||||||
|             SpecialType::BIRTHDAY, |  | ||||||
|             SpecialType::ZIP, |  | ||||||
|             SpecialType::LOCATION, |  | ||||||
|             SpecialType::ADDRESS, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,14 +2,12 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; | use App\Contribution\Data\MemberData; | ||||||
| use App\Contribution\Traits\FormatsDates; | use App\Contribution\Traits\FormatsDates; | ||||||
| use App\Contribution\Traits\HasPdfBackground; | use App\Contribution\Traits\HasPdfBackground; | ||||||
| use App\Country; | use App\Country; | ||||||
| use App\Form\Enums\SpecialType; |  | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 | 
 | ||||||
| class CityRemscheidDocument extends ContributionDocument | class CityRemscheidDocument extends ContributionDocument | ||||||
| { | { | ||||||
|  | @ -21,8 +19,8 @@ class CityRemscheidDocument extends ContributionDocument | ||||||
|      * @param Collection<int, Collection<int, Member>> $children |      * @param Collection<int, Collection<int, Member>> $children | ||||||
|      */ |      */ | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         public Carbon $dateFrom, |         public string $dateFrom, | ||||||
|         public Carbon $dateUntil, |         public string $dateUntil, | ||||||
|         public string $zipLocation, |         public string $zipLocation, | ||||||
|         public ?Country $country, |         public ?Country $country, | ||||||
|         public Collection $leaders, |         public Collection $leaders, | ||||||
|  | @ -34,18 +32,40 @@ class CityRemscheidDocument extends ContributionDocument | ||||||
|         $this->setEventName($eventName); |         $this->setEventName($eventName); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function fromPayload(HasContributionData $request): self |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromRequest(array $request): self | ||||||
|     { |     { | ||||||
|         [$leaders, $children] = $request->members()->partition(fn ($member) => $member->isLeader); |         [$leaders, $children] = MemberData::fromModels($request['members'])->partition(fn ($member) => $member->isLeader); | ||||||
| 
 | 
 | ||||||
|         return new self( |         return new self( | ||||||
|             dateFrom: $request->dateFrom(), |             dateFrom: $request['dateFrom'], | ||||||
|             dateUntil: $request->dateUntil(), |             dateUntil: $request['dateUntil'], | ||||||
|             zipLocation: $request->zipLocation(), |             zipLocation: $request['zipLocation'], | ||||||
|             country: $request->country(), |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|             leaders: $leaders->values()->toBase()->chunk(6), |             leaders: $leaders->values()->toBase()->chunk(6), | ||||||
|             children: $children->values()->toBase()->chunk(20), |             children: $children->values()->toBase()->chunk(20), | ||||||
|             eventName: $request->eventName(), |             eventName: $request['eventName'], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromApiRequest(array $request): self | ||||||
|  |     { | ||||||
|  |         $members = MemberData::fromApi($request['member_data']); | ||||||
|  |         [$leaders, $children] = $members->partition(fn ($member) => $member->isLeader); | ||||||
|  | 
 | ||||||
|  |         return new self( | ||||||
|  |             dateFrom: $request['dateFrom'], | ||||||
|  |             dateUntil: $request['dateUntil'], | ||||||
|  |             zipLocation: $request['zipLocation'], | ||||||
|  |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|  |             leaders: $leaders->values()->toBase()->chunk(6), | ||||||
|  |             children: $children->values()->toBase()->chunk(20), | ||||||
|  |             eventName: $request['eventName'], | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -66,15 +86,4 @@ class CityRemscheidDocument extends ContributionDocument | ||||||
|             'country' => 'required|integer|exists:countries,id', |             'country' => 'required|integer|exists:countries,id', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public static function requiredFormSpecialTypes(): array { |  | ||||||
|         return [ |  | ||||||
|             SpecialType::FIRSTNAME, |  | ||||||
|             SpecialType::LASTNAME, |  | ||||||
|             SpecialType::ADDRESS, |  | ||||||
|             SpecialType::BIRTHDAY, |  | ||||||
|             SpecialType::ZIP, |  | ||||||
|             SpecialType::LOCATION, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,10 +2,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Data\MemberData; | use App\Contribution\Data\MemberData; | ||||||
| use App\Form\Enums\SpecialType; | use Modules\Invoice\InvoiceSettings; | ||||||
| use App\Invoice\InvoiceSettings; |  | ||||||
| use Carbon\Carbon; | use Carbon\Carbon; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Zoomyboy\Tex\Engine; | use Zoomyboy\Tex\Engine; | ||||||
|  | @ -18,8 +16,8 @@ class CitySolingenDocument extends ContributionDocument | ||||||
|      * @param Collection<int, MemberData> $members |      * @param Collection<int, MemberData> $members | ||||||
|      */ |      */ | ||||||
|     final private function __construct( |     final private function __construct( | ||||||
|         public Carbon $dateFrom, |         public string $dateFrom, | ||||||
|         public Carbon $dateUntil, |         public string $dateUntil, | ||||||
|         public string $zipLocation, |         public string $zipLocation, | ||||||
|         public Collection $members, |         public Collection $members, | ||||||
|         public string $eventName, |         public string $eventName, | ||||||
|  | @ -32,14 +30,28 @@ class CitySolingenDocument extends ContributionDocument | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     public static function fromPayload(HasContributionData $request): static |     public static function fromRequest(array $request): static | ||||||
|     { |     { | ||||||
|         return new static( |         return new static( | ||||||
|             dateFrom: $request->dateFrom(), |             dateFrom: $request['dateFrom'], | ||||||
|             dateUntil: $request->dateUntil(), |             dateUntil: $request['dateUntil'], | ||||||
|             zipLocation: $request->zipLocation(), |             zipLocation: $request['zipLocation'], | ||||||
|             members: $request->members(), |             members: MemberData::fromModels($request['members']), | ||||||
|             eventName: $request->eventName(), |             eventName: $request['eventName'], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromApiRequest(array $request): static | ||||||
|  |     { | ||||||
|  |         return new static( | ||||||
|  |             dateFrom: $request['dateFrom'], | ||||||
|  |             dateUntil: $request['dateUntil'], | ||||||
|  |             zipLocation: $request['zipLocation'], | ||||||
|  |             members: MemberData::fromApi($request['member_data']), | ||||||
|  |             eventName: $request['eventName'], | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -63,6 +75,8 @@ class CitySolingenDocument extends ContributionDocument | ||||||
| 
 | 
 | ||||||
|     public function checkboxes(): string |     public function checkboxes(): string | ||||||
|     { |     { | ||||||
|  |         $output = ''; | ||||||
|  | 
 | ||||||
|         $firstRow = collect(['B' => 'Jugendbildungsmaßnahme', 'G' => 'Gruppenleiter/innenschulung', 'FK' => 'Ferienkolonie', 'F' => 'Freizeitnaßnahme'])->map(function ($item, $key) { |         $firstRow = collect(['B' => 'Jugendbildungsmaßnahme', 'G' => 'Gruppenleiter/innenschulung', 'FK' => 'Ferienkolonie', 'F' => 'Freizeitnaßnahme'])->map(function ($item, $key) { | ||||||
|             return ($this->type === $key ? '\\checkedcheckbox' : '\\checkbox') . '{' . $item . '}'; |             return ($this->type === $key ? '\\checkedcheckbox' : '\\checkbox') . '{' . $item . '}'; | ||||||
|         })->implode(' & ') . ' \\\\'; |         })->implode(' & ') . ' \\\\'; | ||||||
|  | @ -95,15 +109,4 @@ class CitySolingenDocument extends ContributionDocument | ||||||
|             'zipLocation' => 'required|string', |             'zipLocation' => 'required|string', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public static function requiredFormSpecialTypes(): array { |  | ||||||
|         return [ |  | ||||||
|             SpecialType::FIRSTNAME, |  | ||||||
|             SpecialType::LASTNAME, |  | ||||||
|             SpecialType::BIRTHDAY, |  | ||||||
|             SpecialType::ZIP, |  | ||||||
|             SpecialType::LOCATION, |  | ||||||
|             SpecialType::ADDRESS, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,8 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Form\Enums\SpecialType; |  | ||||||
| use Zoomyboy\Tex\Document; | use Zoomyboy\Tex\Document; | ||||||
| use Zoomyboy\Tex\Template; | use Zoomyboy\Tex\Template; | ||||||
| 
 | 
 | ||||||
|  | @ -13,12 +11,15 @@ abstract class ContributionDocument extends Document | ||||||
| 
 | 
 | ||||||
|     abstract public static function getName(): string; |     abstract public static function getName(): string; | ||||||
| 
 | 
 | ||||||
|     abstract public static function fromPayload(HasContributionData $request): self; |     /** | ||||||
|  |      * @param ContributionRequestArray $request | ||||||
|  |      */ | ||||||
|  |     abstract public static function fromRequest(array $request): self; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return array<int, SpecialType> |      * @param ContributionApiRequestArray $request | ||||||
|      */ |      */ | ||||||
|     abstract public static function requiredFormSpecialTypes(): array; |     abstract public static function fromApiRequest(array $request): self; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return array<string, mixed> |      * @return array<string, mixed> | ||||||
|  | @ -37,6 +38,11 @@ abstract class ContributionDocument extends Document | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static function buttonName(): string | ||||||
|  |     { | ||||||
|  |         return 'Für ' . static::getName() . ' erstellen';; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function setEventName(string $eventName): void |     public function setEventName(string $eventName): void | ||||||
|     { |     { | ||||||
|         $this->eventName = $eventName; |         $this->eventName = $eventName; | ||||||
|  |  | ||||||
|  | @ -2,14 +2,11 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Data\MemberData; | use App\Contribution\Data\MemberData; | ||||||
| use App\Contribution\Traits\FormatsDates; | use App\Contribution\Traits\FormatsDates; | ||||||
| use App\Contribution\Traits\HasPdfBackground; | use App\Contribution\Traits\HasPdfBackground; | ||||||
| use App\Country; | use App\Country; | ||||||
| use App\Form\Enums\SpecialType; |  | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 | 
 | ||||||
| class RdpNrwDocument extends ContributionDocument | class RdpNrwDocument extends ContributionDocument | ||||||
| { | { | ||||||
|  | @ -20,8 +17,8 @@ class RdpNrwDocument extends ContributionDocument | ||||||
|      * @param Collection<int, Collection<int, MemberData>> $members |      * @param Collection<int, Collection<int, MemberData>> $members | ||||||
|      */ |      */ | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         public Carbon $dateFrom, |         public string $dateFrom, | ||||||
|         public Carbon $dateUntil, |         public string $dateUntil, | ||||||
|         public string $zipLocation, |         public string $zipLocation, | ||||||
|         public ?Country $country, |         public ?Country $country, | ||||||
|         public Collection $members, |         public Collection $members, | ||||||
|  | @ -32,15 +29,33 @@ class RdpNrwDocument extends ContributionDocument | ||||||
|         $this->setEventName($eventName); |         $this->setEventName($eventName); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function fromPayload(HasContributionData $request): self |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromRequest(array $request): self | ||||||
|     { |     { | ||||||
|         return new self( |         return new self( | ||||||
|             dateFrom: $request->dateFrom(), |             dateFrom: $request['dateFrom'], | ||||||
|             dateUntil: $request->dateUntil(), |             dateUntil: $request['dateUntil'], | ||||||
|             zipLocation: $request->zipLocation(), |             zipLocation: $request['zipLocation'], | ||||||
|             country: $request->country(), |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|             members: $request->members()->chunk(17), |             members: MemberData::fromModels($request['members'])->chunk(17), | ||||||
|             eventName: $request->eventName(), |             eventName: $request['eventName'], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromApiRequest(array $request): self | ||||||
|  |     { | ||||||
|  |         return new self( | ||||||
|  |             dateFrom: $request['dateFrom'], | ||||||
|  |             dateUntil: $request['dateUntil'], | ||||||
|  |             zipLocation: $request['zipLocation'], | ||||||
|  |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|  |             members: MemberData::fromApi($request['member_data'])->chunk(17), | ||||||
|  |             eventName: $request['eventName'], | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -66,15 +81,4 @@ class RdpNrwDocument extends ContributionDocument | ||||||
|             'zipLocation' => 'required|string', |             'zipLocation' => 'required|string', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public static function requiredFormSpecialTypes(): array { |  | ||||||
|         return [ |  | ||||||
|             SpecialType::FIRSTNAME, |  | ||||||
|             SpecialType::LASTNAME, |  | ||||||
|             SpecialType::BIRTHDAY, |  | ||||||
|             SpecialType::ZIP, |  | ||||||
|             SpecialType::LOCATION, |  | ||||||
|             SpecialType::GENDER, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,14 +2,11 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Contribution\Documents; | namespace App\Contribution\Documents; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Data\MemberData; | use App\Contribution\Data\MemberData; | ||||||
| use App\Contribution\Traits\FormatsDates; | use App\Contribution\Traits\FormatsDates; | ||||||
| use App\Contribution\Traits\HasPdfBackground; | use App\Contribution\Traits\HasPdfBackground; | ||||||
| use App\Country; | use App\Country; | ||||||
| use App\Form\Enums\SpecialType; |  | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 | 
 | ||||||
| class WuppertalDocument extends ContributionDocument | class WuppertalDocument extends ContributionDocument | ||||||
| { | { | ||||||
|  | @ -21,8 +18,8 @@ class WuppertalDocument extends ContributionDocument | ||||||
|      * @param Collection<int, Collection<int, MemberData>> $members |      * @param Collection<int, Collection<int, MemberData>> $members | ||||||
|      */ |      */ | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         public Carbon $dateFrom, |         public string $dateFrom, | ||||||
|         public Carbon $dateUntil, |         public string $dateUntil, | ||||||
|         public string $zipLocation, |         public string $zipLocation, | ||||||
|         public ?Country $country, |         public ?Country $country, | ||||||
|         public Collection $members, |         public Collection $members, | ||||||
|  | @ -33,15 +30,33 @@ class WuppertalDocument extends ContributionDocument | ||||||
|         $this->setEventName($eventName); |         $this->setEventName($eventName); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function fromPayload(HasContributionData $request): self |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromRequest(array $request): self | ||||||
|     { |     { | ||||||
|         return new self( |         return new self( | ||||||
|             dateFrom: $request->dateFrom(), |             dateFrom: $request['dateFrom'], | ||||||
|             dateUntil: $request->dateUntil(), |             dateUntil: $request['dateUntil'], | ||||||
|             zipLocation: $request->zipLocation(), |             zipLocation: $request['zipLocation'], | ||||||
|             country: $request->country(), |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|             members: $request->members()->chunk(14), |             members: MemberData::fromModels($request['members'])->chunk(14), | ||||||
|             eventName: $request->eventName(), |             eventName: $request['eventName'], | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public static function fromApiRequest(array $request): self | ||||||
|  |     { | ||||||
|  |         return new self( | ||||||
|  |             dateFrom: $request['dateFrom'], | ||||||
|  |             dateUntil: $request['dateUntil'], | ||||||
|  |             zipLocation: $request['zipLocation'], | ||||||
|  |             country: Country::where('id', $request['country'])->firstOrFail(), | ||||||
|  |             members: MemberData::fromApi($request['member_data'])->chunk(14), | ||||||
|  |             eventName: $request['eventName'], | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -61,16 +76,4 @@ class WuppertalDocument extends ContributionDocument | ||||||
|             'zipLocation' => 'required|string', |             'zipLocation' => 'required|string', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public static function requiredFormSpecialTypes(): array { |  | ||||||
|         return [ |  | ||||||
|             SpecialType::FIRSTNAME, |  | ||||||
|             SpecialType::LASTNAME, |  | ||||||
|             SpecialType::ADDRESS, |  | ||||||
|             SpecialType::BIRTHDAY, |  | ||||||
|             SpecialType::ZIP, |  | ||||||
|             SpecialType::LOCATION, |  | ||||||
|             SpecialType::GENDER, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,256 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Contribution\Enums; |  | ||||||
| 
 |  | ||||||
| enum Country: string { |  | ||||||
|     case AD = 'Andorra'; |  | ||||||
|     case AE = 'Vereinigte Arabische Emirate'; |  | ||||||
|     case AF = 'Afghanistan'; |  | ||||||
|     case AG = 'Antigua und Barbuda'; |  | ||||||
|     case AI = 'Anguilla'; |  | ||||||
|     case AL = 'Albanien'; |  | ||||||
|     case AM = 'Armenien'; |  | ||||||
|     case AN = 'Niederländische Antillen'; |  | ||||||
|     case AO = 'Angola'; |  | ||||||
|     case AQ = 'Antarktis'; |  | ||||||
|     case AR = 'Argentinien'; |  | ||||||
|     case AS = 'Amerikanisch-Samoa'; |  | ||||||
|     case AT = 'Österreich (Austria)'; |  | ||||||
|     case AU = 'Australien'; |  | ||||||
|     case AW = 'Aruba'; |  | ||||||
|     case AZ = 'Azerbaijan'; |  | ||||||
|     case BA = 'Bosnien-Herzegovina'; |  | ||||||
|     case BB = 'Barbados'; |  | ||||||
|     case BD = 'Bangladesh'; |  | ||||||
|     case BE = 'Belgien'; |  | ||||||
|     case BF = 'Burkina Faso'; |  | ||||||
|     case BG = 'Bulgarien'; |  | ||||||
|     case BH = 'Bahrain'; |  | ||||||
|     case BI = 'Burundi'; |  | ||||||
|     case BJ = 'Benin'; |  | ||||||
|     case BM = 'Bermudas'; |  | ||||||
|     case BN = 'Brunei Darussalam'; |  | ||||||
|     case BO = 'Bolivien'; |  | ||||||
|     case BR = 'Brasilien'; |  | ||||||
|     case BS = 'Bahamas'; |  | ||||||
|     case BT = 'Bhutan'; |  | ||||||
|     case BV = 'Bouvet Island'; |  | ||||||
|     case BW = 'Botswana'; |  | ||||||
|     case BY = 'Weißrußland (Belarus)'; |  | ||||||
|     case BZ = 'Belize'; |  | ||||||
|     case CA = 'Canada'; |  | ||||||
|     case CC = 'Cocos (Keeling) Islands'; |  | ||||||
|     case CD = 'Demokratische Republik Kongo'; |  | ||||||
|     case CF = 'Zentralafrikanische Republik'; |  | ||||||
|     case CG = 'Kongo'; |  | ||||||
|     case CH = 'Schweiz'; |  | ||||||
|     case CI = 'Elfenbeinküste (Cote D’Ivoire)'; |  | ||||||
|     case CK = 'Cook Islands'; |  | ||||||
|     case CL = 'Chile'; |  | ||||||
|     case CM = 'Kamerun'; |  | ||||||
|     case CN = 'China'; |  | ||||||
|     case CO = 'Kolumbien'; |  | ||||||
|     case CR = 'Costa Rica'; |  | ||||||
|     case CS = 'Tschechoslowakei (ehemalige)'; |  | ||||||
|     case CU = 'Kuba'; |  | ||||||
|     case CV = 'Kap Verde'; |  | ||||||
|     case CX = 'Christmas Island'; |  | ||||||
|     case CY = 'Zypern'; |  | ||||||
|     case CZ = 'Tschechische Republik'; |  | ||||||
|     case DE = 'Deutschland'; |  | ||||||
|     case DJ = 'Djibouti'; |  | ||||||
|     case DK = 'Dänemark'; |  | ||||||
|     case DM = 'Dominica'; |  | ||||||
|     case DO = 'Dominikanische Republik'; |  | ||||||
|     case DZ = 'Algerien'; |  | ||||||
|     case EC = 'Ecuador'; |  | ||||||
|     case EE = 'Estland'; |  | ||||||
|     case EG = 'Ägypten'; |  | ||||||
|     case EH = 'Westsahara'; |  | ||||||
|     case ER = 'Eritrea'; |  | ||||||
|     case ES = 'Spanien'; |  | ||||||
|     case ET = 'Äthiopien'; |  | ||||||
|     case FI = 'Finnland'; |  | ||||||
|     case FJ = 'Fiji'; |  | ||||||
|     case FK = 'Falkland-Inseln (Malvinas)'; |  | ||||||
|     case FM = 'Micronesien'; |  | ||||||
|     case FO = 'Faröer-Inseln'; |  | ||||||
|     case FR = 'Frankreich'; |  | ||||||
|     case FX = 'France, Metropolitan'; |  | ||||||
|     case GA = 'Gabon'; |  | ||||||
|     case GD = 'Grenada'; |  | ||||||
|     case GE = 'Georgien'; |  | ||||||
|     case GF = 'Französisch Guiana'; |  | ||||||
|     case GH = 'Ghana'; |  | ||||||
|     case GI = 'Gibraltar'; |  | ||||||
|     case GL = 'Grönland'; |  | ||||||
|     case GM = 'Gambia'; |  | ||||||
|     case GN = 'Guinea'; |  | ||||||
|     case GP = 'Guadeloupe'; |  | ||||||
|     case GQ = 'Äquatorialguinea'; |  | ||||||
|     case GR = 'Griechenland'; |  | ||||||
|     case GS = 'Südgeorgien und Südliche Sandwich-Inseln'; |  | ||||||
|     case GT = 'Guatemala'; |  | ||||||
|     case GU = 'Guam'; |  | ||||||
|     case GW = 'Guinea-Bissau'; |  | ||||||
|     case GY = 'Guyana'; |  | ||||||
|     case HK = 'Kong Hong'; |  | ||||||
|     case HM = 'Heard und Mc Donald Islands'; |  | ||||||
|     case HN = 'Honduras'; |  | ||||||
|     case HT = 'Haiti'; |  | ||||||
|     case HU = 'Ungarn'; |  | ||||||
|     case ID = 'Indonesien'; |  | ||||||
|     case IE = 'Irland'; |  | ||||||
|     case IL = 'Israel'; |  | ||||||
|     case IN = 'Indien'; |  | ||||||
|     case IO = 'British Indian Ocean Territory'; |  | ||||||
|     case IQ = 'Irak'; |  | ||||||
|     case IR = 'Iran (Islamische Republik)'; |  | ||||||
|     case IS = 'Island'; |  | ||||||
|     case IT = 'Italien'; |  | ||||||
|     case JM = 'Jamaica'; |  | ||||||
|     case JO = 'Jordanien'; |  | ||||||
|     case JP = 'Japan'; |  | ||||||
|     case KE = 'Kenya'; |  | ||||||
|     case KG = 'Kirgisien'; |  | ||||||
|     case KH = 'Königreich Kambodscha'; |  | ||||||
|     case KI = 'Kiribati'; |  | ||||||
|     case KM = 'Komoren'; |  | ||||||
|     case KN = 'Saint Kitts und Nevis'; |  | ||||||
|     case KP = 'Korea, Volksrepublik'; |  | ||||||
|     case KR = 'Korea'; |  | ||||||
|     case KW = 'Kuwait'; |  | ||||||
|     case KY = 'Kayman Islands'; |  | ||||||
|     case KZ = 'Kasachstan'; |  | ||||||
|     case LA = 'Laos'; |  | ||||||
|     case LB = 'Libanon'; |  | ||||||
|     case LC = 'Saint Lucia'; |  | ||||||
|     case LI = 'Liechtenstein'; |  | ||||||
|     case LK = 'Sri Lanka'; |  | ||||||
|     case LR = 'Liberia'; |  | ||||||
|     case LS = 'Lesotho'; |  | ||||||
|     case LT = 'Littauen'; |  | ||||||
|     case LU = 'Luxemburg'; |  | ||||||
|     case LV = 'Lettland'; |  | ||||||
|     case LY = 'Libyen'; |  | ||||||
|     case MA = 'Marokko'; |  | ||||||
|     case MC = 'Monaco'; |  | ||||||
|     case MD = 'Moldavien'; |  | ||||||
|     case MG = 'Madagaskar'; |  | ||||||
|     case MH = 'Marshall-Inseln'; |  | ||||||
|     case MK = 'Mazedonien, ehem. Jugoslawische Republik'; |  | ||||||
|     case ML = 'Mali'; |  | ||||||
|     case MM = 'Myanmar'; |  | ||||||
|     case MN = 'Mongolei'; |  | ||||||
|     case MO = 'Macao'; |  | ||||||
|     case MP = 'Nördliche Marianneninseln'; |  | ||||||
|     case MQ = 'Martinique'; |  | ||||||
|     case MR = 'Mauretanien'; |  | ||||||
|     case MS = 'Montserrat'; |  | ||||||
|     case MT = 'Malta'; |  | ||||||
|     case MU = 'Mauritius'; |  | ||||||
|     case MV = 'Malediven'; |  | ||||||
|     case MW = 'Malawi'; |  | ||||||
|     case MX = 'Mexico'; |  | ||||||
|     case MY = 'Malaysien'; |  | ||||||
|     case MZ = 'Mozambique'; |  | ||||||
|     case NA = 'Namibia'; |  | ||||||
|     case NC = 'Neu Kaledonien'; |  | ||||||
|     case NE = 'Niger'; |  | ||||||
|     case NF = 'Norfolk Island'; |  | ||||||
|     case NG = 'Nigeria'; |  | ||||||
|     case NI = 'Nicaragua'; |  | ||||||
|     case NL = 'Niederlande'; |  | ||||||
|     case NO = 'Norwegen'; |  | ||||||
|     case NP = 'Nepal'; |  | ||||||
|     case NR = 'Nauru'; |  | ||||||
|     case NU = 'Niue'; |  | ||||||
|     case NZ = 'Neuseeland'; |  | ||||||
|     case OM = 'Oman'; |  | ||||||
|     case PA = 'Panama'; |  | ||||||
|     case PE = 'Peru'; |  | ||||||
|     case PF = 'Französisch Polynesien'; |  | ||||||
|     case PG = 'Papua Neuguinea'; |  | ||||||
|     case PH = 'Philippinen'; |  | ||||||
|     case PK = 'Pakistan'; |  | ||||||
|     case PL = 'Polen'; |  | ||||||
|     case PM = 'St. Pierre und Miquelon'; |  | ||||||
|     case PN = 'Pitcairn'; |  | ||||||
|     case PR = 'Puerto Rico'; |  | ||||||
|     case PT = 'Portugal'; |  | ||||||
|     case PW = 'Palau'; |  | ||||||
|     case PY = 'Paraguay'; |  | ||||||
|     case QA = 'Katar'; |  | ||||||
|     case RE = 'Reunion'; |  | ||||||
|     case RO = 'Rumänien'; |  | ||||||
|     case RU = 'Russische Föderation'; |  | ||||||
|     case RW = 'Ruanda'; |  | ||||||
|     case SA = 'Saudi Arabien'; |  | ||||||
|     case SB = 'Salomonen'; |  | ||||||
|     case SC = 'Seychellen'; |  | ||||||
|     case SD = 'Sudan'; |  | ||||||
|     case SE = 'Schweden'; |  | ||||||
|     case SG = 'Singapur'; |  | ||||||
|     case SH = 'St. Helena'; |  | ||||||
|     case SI = 'Slovenien'; |  | ||||||
|     case SJ = 'Svalbard und Jan Mayen Islands'; |  | ||||||
|     case SK = 'Slowakei'; |  | ||||||
|     case SL = 'Sierra Leone'; |  | ||||||
|     case SM = 'San Marino'; |  | ||||||
|     case SN = 'Senegal'; |  | ||||||
|     case SO = 'Somalia'; |  | ||||||
|     case SR = 'Surinam'; |  | ||||||
|     case ST = 'Sao Tome und Principe'; |  | ||||||
|     case SV = 'El Salvador'; |  | ||||||
|     case SY = 'Syrien, Arabische Republik'; |  | ||||||
|     case SZ = 'Swaziland'; |  | ||||||
|     case TC = 'Turk und Caicos-Inseln'; |  | ||||||
|     case TD = 'Tschad'; |  | ||||||
|     case TF = 'Französisches Südl.Territorium'; |  | ||||||
|     case TG = 'Togo'; |  | ||||||
|     case TH = 'Thailand'; |  | ||||||
|     case TJ = 'Tadschikistan'; |  | ||||||
|     case TK = 'Tokelau'; |  | ||||||
|     case TM = 'Turkmenistan'; |  | ||||||
|     case TN = 'Tunesien'; |  | ||||||
|     case TO = 'Tonga'; |  | ||||||
|     case TP = 'Ost-Timor'; |  | ||||||
|     case TR = 'Türkei'; |  | ||||||
|     case TT = 'Trinidad und Tobago'; |  | ||||||
|     case TV = 'Tuvalu'; |  | ||||||
|     case TW = 'Taiwan'; |  | ||||||
|     case TZ = 'Tansania, United Republic of'; |  | ||||||
|     case UA = 'Ukraine'; |  | ||||||
|     case UG = 'Uganda'; |  | ||||||
|     case GB = 'Großbritannien'; |  | ||||||
|     case US = 'Vereinigte Staaten'; |  | ||||||
|     case UM = 'Vereinigte Staaten, Minor Outlying Islands'; |  | ||||||
|     case UY = 'Uruguay'; |  | ||||||
|     case UZ = 'Usbekistan'; |  | ||||||
|     case VA = 'Vatikanstaat'; |  | ||||||
|     case VC = 'Saint Vincent und Grenadines'; |  | ||||||
|     case VE = 'Venezuela'; |  | ||||||
|     case VG = 'Virgin Islands (Britisch)'; |  | ||||||
|     case VI = 'Virgin Islands (U.S.)'; |  | ||||||
|     case VN = 'Vietnam'; |  | ||||||
|     case VU = 'Vanuatu'; |  | ||||||
|     case WF = 'Wallis und Futuna Islands'; |  | ||||||
|     case WS = 'Samoa'; |  | ||||||
|     case YE = 'Jemen'; |  | ||||||
|     case YT = 'Mayotte'; |  | ||||||
|     case YU = 'Jugoslawien'; |  | ||||||
|     case ZA = 'Südafrika'; |  | ||||||
|     case ZM = 'Sambia'; |  | ||||||
|     case ZW = 'Zimbabw'; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<int, array{name: string, id: string}> |  | ||||||
|      */ |  | ||||||
|     public static function forSelect(): array |  | ||||||
|     { |  | ||||||
|         return collect(static::cases()) |  | ||||||
|             ->map(fn ($case) => ['id' => $case->value, 'name' => $case->value]) |  | ||||||
|             ->toArray(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  | @ -1,24 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Contribution\Requests; |  | ||||||
| 
 |  | ||||||
| use App\Contribution\Data\MemberData; |  | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| 
 |  | ||||||
| class GenerateApiRequest extends GenerateRequest { |  | ||||||
|     /** |  | ||||||
|      * @return array<string, string> |  | ||||||
|      */ |  | ||||||
|     public function payload(): array |  | ||||||
|     { |  | ||||||
|         return $this->input(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function validateContribution(): void { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function members(): Collection { |  | ||||||
|         return MemberData::fromApi($this->value('members')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,68 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Contribution\Requests; |  | ||||||
| 
 |  | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\ContributionFactory; |  | ||||||
| use App\Contribution\Data\MemberData; |  | ||||||
| use App\Contribution\Documents\ContributionDocument; |  | ||||||
| use App\Country; |  | ||||||
| use Lorisleiva\Actions\ActionRequest; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| use Illuminate\Support\Facades\Validator; |  | ||||||
| 
 |  | ||||||
| class GenerateRequest extends ActionRequest implements HasContributionData { |  | ||||||
|     /** |  | ||||||
|      * @return array<string, string> |  | ||||||
|      */ |  | ||||||
|     protected function payload(): array |  | ||||||
|     { |  | ||||||
|         return json_decode(rawurldecode(base64_decode($this->input('payload', ''))), true); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function validateContribution(): void { |  | ||||||
|         Validator::make($this->payload(), app(ContributionFactory::class)->rules($this->type()))->validate(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return string|array<array-key, mixed> |  | ||||||
|      */ |  | ||||||
|     public function value(string $key): string|array |  | ||||||
|     { |  | ||||||
|         return data_get($this->payload(), $key); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return class-string<ContributionDocument> |  | ||||||
|      */ |  | ||||||
|     public function type(): string |  | ||||||
|     { |  | ||||||
|         return $this->value('type'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function dateFrom(): Carbon { |  | ||||||
|         return Carbon::parse($this->value('dateFrom')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function dateUntil(): Carbon { |  | ||||||
|         return Carbon::parse($this->value('dateUntil')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function zipLocation(): string { |  | ||||||
|         return $this->value('zipLocation'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function eventName(): string { |  | ||||||
|         return $this->value('eventName'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function members(): Collection { |  | ||||||
|         return MemberData::fromModels($this->value('members')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function country(): ?Country { |  | ||||||
|         return Country::where('id', $this->value('country'))->first(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,44 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Efz; |  | ||||||
| 
 |  | ||||||
| use App\Dashboard\Blocks\Block; |  | ||||||
| use App\Member\Member; |  | ||||||
| use Illuminate\Database\Eloquent\Builder; |  | ||||||
| 
 |  | ||||||
| class EfzPendingBlock extends Block |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @return Builder<Member> |  | ||||||
|      */ |  | ||||||
|     public function query(): Builder |  | ||||||
|     { |  | ||||||
|         return Member::where(function ($query) { |  | ||||||
|             return $query->where('efz', '<=', now()->subYears(5)->endOfYear()) |  | ||||||
|                 ->orWhereNull('efz'); |  | ||||||
|         }) |  | ||||||
|             ->whereCurrentGroup() |  | ||||||
|             ->orderByRaw('lastname, firstname') |  | ||||||
|             ->whereHas('memberships', fn ($builder) => $builder->isLeader()->active()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array{members: array<int, string>} |  | ||||||
|      */ |  | ||||||
|     public function data(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'members' => $this->query()->get()->map(fn ($member) => $member->fullname)->toArray(), |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function component(): string |  | ||||||
|     { |  | ||||||
|         return 'efz-pending'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function title(): string |  | ||||||
|     { |  | ||||||
|         return 'Ausstehende Führungszeugnisse'; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -16,7 +16,7 @@ class FormApiListAction | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param string $filter |      * @param string $filter | ||||||
|      * @return LengthAwarePaginator<int, Form> |      * @return LengthAwarePaginator<Form> | ||||||
|      */ |      */ | ||||||
|     public function handle(string $filter, int $perPage): LengthAwarePaginator |     public function handle(string $filter, int $perPage): LengthAwarePaginator | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -1,38 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Form\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Form\Models\Form; |  | ||||||
| use App\Lib\Events\Succeeded; |  | ||||||
| use Illuminate\Http\JsonResponse; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class FormCopyAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     public function handle(Form $form): Form |  | ||||||
|     { |  | ||||||
|         $newForm = $form->replicate(); |  | ||||||
|         $newForm->save(); |  | ||||||
|         $newForm->update(['name' => $form->name.' - Kopie', 'is_active' => false]); |  | ||||||
| 
 |  | ||||||
|         foreach ($form->getRegisteredMediaCollections() as $collection) { |  | ||||||
|             foreach ($form->getMedia($collection->name) as $media) { |  | ||||||
|                 $media->copy($newForm, $collection->name); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         ClearFrontendCacheAction::run(); |  | ||||||
| 
 |  | ||||||
|         return $form; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function asController(Form $form): JsonResponse |  | ||||||
|     { |  | ||||||
|         $this->handle($form); |  | ||||||
| 
 |  | ||||||
|         Succeeded::message('Veranstaltung kopiert.')->dispatch(); |  | ||||||
|         return response()->json([]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Form\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Form\FormSettings; |  | ||||||
| use App\Form\Models\Form; |  | ||||||
| use Illuminate\Http\JsonResponse; |  | ||||||
| use Illuminate\Support\Facades\Cache; |  | ||||||
| use Illuminate\Support\Facades\URL; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class FormGenerateLaterlinkAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     public function asController(Form $form): JsonResponse |  | ||||||
|     { |  | ||||||
|         $registerUrl = str(app(FormSettings::class)->registerUrl)->replace('{slug}', $form->slug)->toString(); |  | ||||||
|         $laterId = str()->uuid()->toString(); |  | ||||||
|         $laterUrl = URL::signedRoute('form.register', ['form' => $form, 'later' => '1', 'id' => $laterId]); |  | ||||||
|         $urlParts = parse_url($laterUrl); |  | ||||||
| 
 |  | ||||||
|         Cache::remember('later_'.$laterId, 2592000, fn () => $form->id);    // Link ist 40 Tage gültig
 |  | ||||||
| 
 |  | ||||||
|         return response()->json([ |  | ||||||
|             'url' => $registerUrl.'?'.data_get($urlParts, 'query') |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -16,7 +16,7 @@ class FormIndexAction | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return LengthAwarePaginator<int, Form> |      * @return LengthAwarePaginator<Form> | ||||||
|      */ |      */ | ||||||
|     public function handle(string $filter): LengthAwarePaginator |     public function handle(string $filter): LengthAwarePaginator | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -36,10 +36,6 @@ class FormStoreAction | ||||||
|             'needs_prevention' => 'present|boolean', |             'needs_prevention' => 'present|boolean', | ||||||
|             'prevention_text' => 'array', |             'prevention_text' => 'array', | ||||||
|             'prevention_conditions' => 'array', |             'prevention_conditions' => 'array', | ||||||
|             'leader_conditions' => 'array', |  | ||||||
|             'zip' => 'present|nullable|string', |  | ||||||
|             'location' => 'present|nullable|string', |  | ||||||
|             'country' => 'nullable|string|max:255', |  | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| namespace App\Form\Actions; | namespace App\Form\Actions; | ||||||
| 
 | 
 | ||||||
| use App\Form\Models\Form; | use App\Form\Models\Form; | ||||||
|  | use App\Lib\Editor\Condition; | ||||||
| use App\Lib\Events\Succeeded; | use App\Lib\Events\Succeeded; | ||||||
| use Illuminate\Http\JsonResponse; | use Illuminate\Http\JsonResponse; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
|  | @ -35,10 +36,6 @@ class FormUpdateAction | ||||||
|             'needs_prevention' => 'present|boolean', |             'needs_prevention' => 'present|boolean', | ||||||
|             'prevention_text' => 'array', |             'prevention_text' => 'array', | ||||||
|             'prevention_conditions' => 'array', |             'prevention_conditions' => 'array', | ||||||
|             'location' => 'present|nullable|string', |  | ||||||
|             'zip' => 'present|nullable|string', |  | ||||||
|             'country' => 'nullable|string|max:255', |  | ||||||
|             'leader_conditions' => 'array', |  | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ class FormtemplateIndexAction | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return LengthAwarePaginator<int, Formtemplate> |      * @return LengthAwarePaginator<Formtemplate> | ||||||
|      */ |      */ | ||||||
|     public function handle(): LengthAwarePaginator |     public function handle(): LengthAwarePaginator | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -1,45 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Form\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\ContributionFactory; |  | ||||||
| use App\Form\Models\Form; |  | ||||||
| use App\Form\Requests\FormCompileRequest; |  | ||||||
| use App\Rules\JsonBase64Rule; |  | ||||||
| use Illuminate\Http\JsonResponse; |  | ||||||
| use Lorisleiva\Actions\ActionRequest; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| use Zoomyboy\Tex\BaseCompiler; |  | ||||||
| use Zoomyboy\Tex\Tex; |  | ||||||
| 
 |  | ||||||
| class GenerateContributionAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     public function handle(HasContributionData $request): BaseCompiler |  | ||||||
|     { |  | ||||||
|         return Tex::compile($request->type()::fromPayload($request)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function asController(ActionRequest $request, Form $form): BaseCompiler|JsonResponse |  | ||||||
|     { |  | ||||||
|         $r = FormCompileRequest::from(['form' => $form]); |  | ||||||
|         app(ContributionFactory::class)->validateType($r); |  | ||||||
|         $r->validateContribution(); |  | ||||||
| 
 |  | ||||||
|         return $request->input('validate') |  | ||||||
|             ? response()->json([]) |  | ||||||
|             : $this->handle($r); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public function rules(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'payload' => [new JsonBase64Rule()], |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -13,18 +13,18 @@ class PreventionRememberAction | ||||||
| { | { | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     public string $commandSignature = 'prevention:remember-forms'; |     public string $commandSignature = 'prevention:remember'; | ||||||
| 
 | 
 | ||||||
|     public function handle(): void |     public function handle(): void | ||||||
|     { |     { | ||||||
|         $query = Participant::whereHas( |         $query = Participant::whereHas( | ||||||
|             'form', |             'form', | ||||||
|             fn($form) => $form |             fn ($form) => $form | ||||||
|                 ->where('needs_prevention', true) |                 ->where('needs_prevention', true) | ||||||
|                 ->where('from', '>=', now()) |                 ->where('from', '>=', now()) | ||||||
|         ) |         ) | ||||||
|             ->where( |             ->where( | ||||||
|                 fn($q) => $q |                 fn ($q) => $q | ||||||
|                     ->where('last_remembered_at', '<=', now()->subWeeks(2)) |                     ->where('last_remembered_at', '<=', now()->subWeeks(2)) | ||||||
|                     ->orWhereNull('last_remembered_at') |                     ->orWhereNull('last_remembered_at') | ||||||
|             ); |             ); | ||||||
|  | @ -33,7 +33,7 @@ class PreventionRememberAction | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if ($participant->getFields()->getMailRecipient() === null || $participant->preventions()->count() === 0) { |             if ($participant->getFields()->getMailRecipient() === null || count($participant->preventions()) === 0) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -41,7 +41,7 @@ class PreventionRememberAction | ||||||
|                 ->placeholder('formname', $participant->form->name) |                 ->placeholder('formname', $participant->form->name) | ||||||
|                 ->append($participant->form->prevention_text); |                 ->append($participant->form->prevention_text); | ||||||
| 
 | 
 | ||||||
|             Mail::send(new PreventionRememberMail($participant, $body, $participant->preventions())); |             Mail::send(new PreventionRememberMail($participant, $body)); | ||||||
| 
 | 
 | ||||||
|             $participant->update(['last_remembered_at' => now()]); |             $participant->update(['last_remembered_at' => now()]); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -7,9 +7,6 @@ use App\Form\Models\Form; | ||||||
| use App\Form\Models\Participant; | use App\Form\Models\Participant; | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
| use Illuminate\Http\JsonResponse; | use Illuminate\Http\JsonResponse; | ||||||
| use Illuminate\Support\Facades\Cache; |  | ||||||
| use Illuminate\Support\Facades\URL; |  | ||||||
| use Illuminate\Support\Facades\Validator; |  | ||||||
| use Illuminate\Validation\ValidationException; | use Illuminate\Validation\ValidationException; | ||||||
| use Lorisleiva\Actions\ActionRequest; | use Lorisleiva\Actions\ActionRequest; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
|  | @ -23,9 +20,13 @@ class RegisterAction | ||||||
|      */ |      */ | ||||||
|     public function handle(Form $form, array $input): Participant |     public function handle(Form $form, array $input): Participant | ||||||
|     { |     { | ||||||
|  |         if (!$form->canRegister()) { | ||||||
|  |             throw ValidationException::withMessages(['event' => 'Anmeldung zzt nicht möglich.']); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         $memberQuery = FieldCollection::fromRequest($form, $input) |         $memberQuery = FieldCollection::fromRequest($form, $input) | ||||||
|             ->withNamiType() |             ->withNamiType() | ||||||
|             ->reduce(fn($query, $field) => $field->namiType->performQuery($query, $field->value), (new Member())->newQuery()); |             ->reduce(fn ($query, $field) => $field->namiType->performQuery($query, $field->value), (new Member())->newQuery()); | ||||||
|         $member = $form->getFields()->withNamiType()->count() && $memberQuery->count() === 1 ? $memberQuery->first() : null; |         $member = $form->getFields()->withNamiType()->count() && $memberQuery->count() === 1 ? $memberQuery->first() : null; | ||||||
| 
 | 
 | ||||||
|         $participant = $form->participants()->create([ |         $participant = $form->participants()->create([ | ||||||
|  | @ -33,7 +34,7 @@ class RegisterAction | ||||||
|             'member_id' => $member?->id, |             'member_id' => $member?->id, | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         $form->getFields()->each(fn($field) => $field->afterRegistration($form, $participant, $input)); |         $form->getFields()->each(fn ($field) => $field->afterRegistration($form, $participant, $input)); | ||||||
| 
 | 
 | ||||||
|         $participant->sendConfirmationMail(); |         $participant->sendConfirmationMail(); | ||||||
|         ExportSyncAction::dispatch($form->id); |         ExportSyncAction::dispatch($form->id); | ||||||
|  | @ -76,30 +77,8 @@ class RegisterAction | ||||||
| 
 | 
 | ||||||
|     public function asController(ActionRequest $request, Form $form): JsonResponse |     public function asController(ActionRequest $request, Form $form): JsonResponse | ||||||
|     { |     { | ||||||
|         if (!$form->canRegister() && !$this->isRegisteringLater($request, $form)) { |  | ||||||
|             throw ValidationException::withMessages(['event' => 'Anmeldung zzt nicht möglich.']); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $participant = $this->handle($form, $request->validated()); |         $participant = $this->handle($form, $request->validated()); | ||||||
| 
 | 
 | ||||||
|         if ($this->isRegisteringLater($request, $form)) { |  | ||||||
|             Cache::forget('later_'.request('id')); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return response()->json($participant); |         return response()->json($participant); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public function isRegisteringLater(ActionRequest $request, Form $form): bool { |  | ||||||
|         $validator = Validator::make($request->query(), [ |  | ||||||
|             'later' => 'required|numeric|in:1', |  | ||||||
|             'id' => 'required|string|uuid:4', |  | ||||||
|             'signature' => 'required|string', |  | ||||||
|         ]); |  | ||||||
| 
 |  | ||||||
|         if (!URL::hasValidSignature($request) || $validator->fails()) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return Cache::get('later_'.data_get($validator->validated(), 'id')) === $form->id; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -114,12 +114,7 @@ class FieldCollection extends Collection | ||||||
|         return $this->map(fn ($field) => $field->presentRaw())->toArray(); |         return $this->map(fn ($field) => $field->presentRaw())->toArray(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function hasSpecialType(SpecialType $specialType): bool |     private function findBySpecialType(SpecialType $specialType): ?Field | ||||||
|     { |  | ||||||
|         return $this->findBySpecialType($specialType) !== null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function findBySpecialType(SpecialType $specialType): ?Field |  | ||||||
|     { |     { | ||||||
|         return $this->first(fn ($field) => $field->specialType === $specialType); |         return $this->first(fn ($field) => $field->specialType === $specialType); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -7,11 +7,6 @@ enum SpecialType: string | ||||||
|     case FIRSTNAME = 'Vorname'; |     case FIRSTNAME = 'Vorname'; | ||||||
|     case LASTNAME = 'Nachname'; |     case LASTNAME = 'Nachname'; | ||||||
|     case EMAIL = 'E-Mail-Adresse'; |     case EMAIL = 'E-Mail-Adresse'; | ||||||
|     case BIRTHDAY = 'Geburtsdatum'; |  | ||||||
|     case ZIP = 'PLZ'; |  | ||||||
|     case LOCATION = 'Ort'; |  | ||||||
|     case ADDRESS = 'Adresse'; |  | ||||||
|     case GENDER = 'Geschlecht'; |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return array<int, array{name: string, id: string}> |      * @return array<int, array{name: string, id: string}> | ||||||
|  |  | ||||||
|  | @ -69,8 +69,10 @@ class ConfirmRegistrationMail extends Mailable | ||||||
|      */ |      */ | ||||||
|     public function attachments() |     public function attachments() | ||||||
|     { |     { | ||||||
|  |         $conditionResolver = app(FormConditionResolver::class)->forParticipant($this->participant); | ||||||
|  | 
 | ||||||
|         return $this->participant->form->getMedia('mailattachments') |         return $this->participant->form->getMedia('mailattachments') | ||||||
|             ->filter(fn ($media) => $this->participant->matchesCondition(Condition::fromMedia($media))) |             ->filter(fn ($media) => $conditionResolver->filterCondition(Condition::fromMedia($media))) | ||||||
|             ->map(fn ($media) => Attachment::fromStorageDisk($media->disk, $media->getPathRelativeToRoot())) |             ->map(fn ($media) => Attachment::fromStorageDisk($media->disk, $media->getPathRelativeToRoot())) | ||||||
|             ->all(); |             ->all(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Form\Models; | namespace App\Form\Models; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Enums\Country; |  | ||||||
| use App\Form\Actions\UpdateParticipantSearchIndexAction; | use App\Form\Actions\UpdateParticipantSearchIndexAction; | ||||||
| use App\Form\Data\ExportData; | use App\Form\Data\ExportData; | ||||||
| use App\Form\Data\FieldCollection; | use App\Form\Data\FieldCollection; | ||||||
|  | @ -19,6 +18,7 @@ use Laravel\Scout\Searchable; | ||||||
| use Spatie\Image\Enums\Fit; | use Spatie\Image\Enums\Fit; | ||||||
| use Spatie\MediaLibrary\HasMedia; | use Spatie\MediaLibrary\HasMedia; | ||||||
| use Spatie\MediaLibrary\InteractsWithMedia; | use Spatie\MediaLibrary\InteractsWithMedia; | ||||||
|  | use Spatie\MediaLibrary\MediaCollections\Models\Media; | ||||||
| use Zoomyboy\MedialibraryHelper\DefersUploads; | use Zoomyboy\MedialibraryHelper\DefersUploads; | ||||||
| 
 | 
 | ||||||
| /** @todo replace editor content with EditorData cast */ | /** @todo replace editor content with EditorData cast */ | ||||||
|  | @ -49,8 +49,6 @@ class Form extends Model implements HasMedia | ||||||
|         'to' => 'datetime', |         'to' => 'datetime', | ||||||
|         'registration_from' => 'datetime', |         'registration_from' => 'datetime', | ||||||
|         'registration_until' => 'datetime', |         'registration_until' => 'datetime', | ||||||
|         'country' => Country::class, |  | ||||||
|         'leader_conditions' => Condition::class, |  | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -71,24 +69,25 @@ class Form extends Model implements HasMedia | ||||||
|         return $this->hasMany(Participant::class); |         return $this->hasMany(Participant::class); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     public function registerMediaCollections(): void |     public function registerMediaCollections(): void | ||||||
|     { |     { | ||||||
|         $this->addMediaCollection('headerImage') |         $this->addMediaCollection('headerImage') | ||||||
|             ->singleFile() |             ->singleFile() | ||||||
|             ->maxWidth(fn() => 500) |             ->maxWidth(fn () => 500) | ||||||
|             ->forceFileName(fn(Form $model) => $model->slug) |             ->forceFileName(fn (Form $model, string $name) => $model->slug) | ||||||
|             ->convert(fn() => 'jpg') |             ->convert(fn () => 'jpg') | ||||||
|             ->registerMediaConversions(function () { |             ->registerMediaConversions(function (Media $media) { | ||||||
|                 $this->addMediaConversion('square')->fit(Fit::Crop, 400, 400); |                 $this->addMediaConversion('square')->fit(Fit::Crop, 400, 400); | ||||||
|             }); |             }); | ||||||
|         $this->addMediaCollection('mailattachments') |         $this->addMediaCollection('mailattachments') | ||||||
|             ->withDefaultProperties(fn() => [ |             ->withDefaultProperties(fn () => [ | ||||||
|                 'conditions' => [ |                 'conditions' => [ | ||||||
|                     'mode' => 'all', |                     'mode' => 'all', | ||||||
|                     'ifs' => [] |                     'ifs' => [] | ||||||
|                 ], |                 ], | ||||||
|             ]) |             ]) | ||||||
|             ->withPropertyValidation(fn() => [ |             ->withPropertyValidation(fn () => [ | ||||||
|                 'conditions.mode' => 'required|string|in:all,any', |                 'conditions.mode' => 'required|string|in:all,any', | ||||||
|                 'conditions.ifs' => 'array', |                 'conditions.ifs' => 'array', | ||||||
|                 'conditions.ifs.*.field' => 'required', |                 'conditions.ifs.*.field' => 'required', | ||||||
|  | @ -102,7 +101,7 @@ class Form extends Model implements HasMedia | ||||||
|      */ |      */ | ||||||
|     public function getRegistrationRules(): array |     public function getRegistrationRules(): array | ||||||
|     { |     { | ||||||
|         return $this->getFields()->reduce(fn($carry, $field) => [ |         return $this->getFields()->reduce(fn ($carry, $field) => [ | ||||||
|             ...$carry, |             ...$carry, | ||||||
|             ...$field->getRegistrationRules($this), |             ...$field->getRegistrationRules($this), | ||||||
|         ], []); |         ], []); | ||||||
|  | @ -113,7 +112,7 @@ class Form extends Model implements HasMedia | ||||||
|      */ |      */ | ||||||
|     public function getRegistrationMessages(): array |     public function getRegistrationMessages(): array | ||||||
|     { |     { | ||||||
|         return $this->getFields()->reduce(fn($carry, $field) => [ |         return $this->getFields()->reduce(fn ($carry, $field) => [ | ||||||
|             ...$carry, |             ...$carry, | ||||||
|             ...$field->getRegistrationMessages($this), |             ...$field->getRegistrationMessages($this), | ||||||
|         ], []); |         ], []); | ||||||
|  | @ -124,7 +123,7 @@ class Form extends Model implements HasMedia | ||||||
|      */ |      */ | ||||||
|     public function getRegistrationAttributes(): array |     public function getRegistrationAttributes(): array | ||||||
|     { |     { | ||||||
|         return $this->getFields()->reduce(fn($carry, $field) => [ |         return $this->getFields()->reduce(fn ($carry, $field) => [ | ||||||
|             ...$carry, |             ...$carry, | ||||||
|             ...$field->getRegistrationAttributes($this), |             ...$field->getRegistrationAttributes($this), | ||||||
|         ], []); |         ], []); | ||||||
|  | @ -190,7 +189,8 @@ class Form extends Model implements HasMedia | ||||||
|         return Sorting::from($this->meta['sorting']); |         return Sorting::from($this->meta['sorting']); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function isInDates(): bool { |     public function canRegister(): bool | ||||||
|  |     { | ||||||
|         if ($this->registration_from && $this->registration_from->gt(now())) { |         if ($this->registration_from && $this->registration_from->gt(now())) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  | @ -201,9 +201,4 @@ class Form extends Model implements HasMedia | ||||||
| 
 | 
 | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public function canRegister(): bool |  | ||||||
|     { |  | ||||||
|         return $this->is_active && $this->isInDates(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,17 +4,16 @@ namespace App\Form\Models; | ||||||
| 
 | 
 | ||||||
| use App\Form\Data\FieldCollection; | use App\Form\Data\FieldCollection; | ||||||
| use App\Form\Data\FormConfigData; | use App\Form\Data\FormConfigData; | ||||||
| use App\Form\Editor\FormConditionResolver; |  | ||||||
| use App\Form\Mails\ConfirmRegistrationMail; | use App\Form\Mails\ConfirmRegistrationMail; | ||||||
| use App\Lib\Editor\Condition; | use App\Form\Scopes\ParticipantFilterScope; | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
| use App\Prevention\Contracts\Preventable; | use App\Prevention\Contracts\Preventable; | ||||||
| use Database\Factories\Form\Models\ParticipantFactory; | use Database\Factories\Form\Models\ParticipantFactory; | ||||||
|  | use Illuminate\Database\Eloquent\Builder; | ||||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||||
| use Illuminate\Database\Eloquent\Model; | use Illuminate\Database\Eloquent\Model; | ||||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | use Illuminate\Database\Eloquent\Relations\HasMany; | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| use Illuminate\Support\Facades\Mail; | use Illuminate\Support\Facades\Mail; | ||||||
| use Laravel\Scout\Searchable; | use Laravel\Scout\Searchable; | ||||||
| use stdClass; | use stdClass; | ||||||
|  | @ -82,15 +81,20 @@ class Participant extends Model implements Preventable | ||||||
|         Mail::to($this->getMailRecipient())->queue(new ConfirmRegistrationMail($this)); |         Mail::to($this->getMailRecipient())->queue(new ConfirmRegistrationMail($this)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function preventableLayout(): string | ||||||
|  |     { | ||||||
|  |         return 'mail.prevention.prevention-remember-participant'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * @inheritdoc | ||||||
|      */ |      */ | ||||||
|     public function preventions(): Collection |     public function preventions(): array | ||||||
|     { |     { | ||||||
|         return $this->member?->preventions($this->form->from) ?: collect([]); |         return $this->member?->preventions($this->form->from) ?: []; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function getMailRecipient(): ?stdClass |     public function getMailRecipient(): stdClass | ||||||
|     { |     { | ||||||
|         return $this->getFields()->getMailRecipient(); |         return $this->getFields()->getMailRecipient(); | ||||||
|     } |     } | ||||||
|  | @ -110,8 +114,4 @@ class Participant extends Model implements Preventable | ||||||
|     { |     { | ||||||
|         return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp]; |         return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     public function matchesCondition(Condition $condition): bool { |  | ||||||
|         return app(FormConditionResolver::class)->forParticipant($this)->filterCondition($condition); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,107 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Form\Requests; |  | ||||||
| 
 |  | ||||||
| use App\Contribution\Contracts\HasContributionData; |  | ||||||
| use App\Contribution\Data\MemberData; |  | ||||||
| use App\Contribution\Documents\ContributionDocument; |  | ||||||
| use App\Country; |  | ||||||
| use App\Form\Editor\FormConditionResolver; |  | ||||||
| use App\Form\Enums\SpecialType; |  | ||||||
| use App\Form\Models\Form; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| use Illuminate\Support\Facades\Validator; |  | ||||||
| use Spatie\LaravelData\Data; |  | ||||||
| 
 |  | ||||||
| class FormCompileRequest extends Data implements HasContributionData { |  | ||||||
| 
 |  | ||||||
|     public function __construct(public Form $form) {} |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return class-string<ContributionDocument> |  | ||||||
|      */ |  | ||||||
|     public function type(): string |  | ||||||
|     { |  | ||||||
|         $payload = json_decode(rawurldecode(base64_decode(request()->input('payload'))), true); |  | ||||||
| 
 |  | ||||||
|         return $payload['type']; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function dateFrom(): Carbon |  | ||||||
|     { |  | ||||||
|         return $this->form->from; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function dateUntil(): Carbon |  | ||||||
|     { |  | ||||||
|         return $this->form->to; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function zipLocation(): string |  | ||||||
|     { |  | ||||||
|         return $this->form->zip.' '.$this->form->location; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function eventName(): string |  | ||||||
|     { |  | ||||||
|         return $this->form->name; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function members(): Collection |  | ||||||
|     { |  | ||||||
|         $members = []; |  | ||||||
|         $fields = [ |  | ||||||
|             [SpecialType::FIRSTNAME, 'firstname'], |  | ||||||
|             [SpecialType::LASTNAME, 'lastname'], |  | ||||||
|             [SpecialType::BIRTHDAY, 'birthday'], |  | ||||||
|             [SpecialType::GENDER, 'gender'], |  | ||||||
|             [SpecialType::ADDRESS, 'address'], |  | ||||||
|             [SpecialType::ZIP, 'zip'], |  | ||||||
|             [SpecialType::LOCATION, 'location'], |  | ||||||
|         ]; |  | ||||||
| 
 |  | ||||||
|         foreach ($this->form->participants as $participant) { |  | ||||||
|             $member = []; |  | ||||||
|             foreach ($fields as [$type, $name]) { |  | ||||||
|                 $f = $this->form->getFields()->findBySpecialType($type); |  | ||||||
|                 if (!$f) { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 $member[$name] = $participant->getFields()->find($f)->value; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $members[] = [ |  | ||||||
|                 'is_leader' => $participant->matchesCondition($participant->form->leader_conditions), |  | ||||||
|                 'gender' => 'weiblich', |  | ||||||
|                 ...$member, |  | ||||||
|             ]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return MemberData::fromApi($members); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function country(): ?Country |  | ||||||
|     { |  | ||||||
|         return Country::first(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function validateContribution(): void |  | ||||||
|     { |  | ||||||
|         Validator::make($this->form->toArray(), [ |  | ||||||
|             'zip' => 'required', |  | ||||||
|             'location' => 'required' |  | ||||||
|         ]) |  | ||||||
|             ->after(function($validator) { |  | ||||||
|                 foreach ($this->type()::requiredFormSpecialTypes() as $type) { |  | ||||||
|                     if (!$this->form->getFields()->hasSpecialType($type)) { |  | ||||||
|                         $validator->errors()->add($type->name, 'Kein Feld für ' . $type->value . ' vorhanden.'); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 if ($this->form->participants->count() === 0) { |  | ||||||
|                     $validator->errors()->add('participants',  'Veranstaltung besitzt noch keine Teilnehmer*innen.'); |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
|             ->validate(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Form\Resources; | namespace App\Form\Resources; | ||||||
| 
 | 
 | ||||||
| use App\Contribution\Enums\Country; |  | ||||||
| use App\Form\Data\ExportData; | use App\Form\Data\ExportData; | ||||||
| use App\Form\Enums\NamiType; | use App\Form\Enums\NamiType; | ||||||
| use App\Form\Enums\SpecialType; | use App\Form\Enums\SpecialType; | ||||||
|  | @ -15,7 +14,6 @@ use App\Group; | ||||||
| use App\Lib\Editor\EditorData; | use App\Lib\Editor\EditorData; | ||||||
| use App\Lib\HasMeta; | use App\Lib\HasMeta; | ||||||
| use Illuminate\Http\Resources\Json\JsonResource; | use Illuminate\Http\Resources\Json\JsonResource; | ||||||
| use App\Contribution\ContributionFactory; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @mixin Form |  * @mixin Form | ||||||
|  | @ -46,7 +44,6 @@ class FormResource extends JsonResource | ||||||
|             'mail_bottom' => $this->mail_bottom, |             'mail_bottom' => $this->mail_bottom, | ||||||
|             'registration_from' => $this->registration_from?->format('Y-m-d H:i:s'), |             'registration_from' => $this->registration_from?->format('Y-m-d H:i:s'), | ||||||
|             'registration_until' => $this->registration_until?->format('Y-m-d H:i:s'), |             'registration_until' => $this->registration_until?->format('Y-m-d H:i:s'), | ||||||
|             'is_in_dates' => $this->isInDates(), |  | ||||||
|             'config' => $this->config, |             'config' => $this->config, | ||||||
|             'participants_count' => $this->participants_count, |             'participants_count' => $this->participants_count, | ||||||
|             'is_active' => $this->is_active, |             'is_active' => $this->is_active, | ||||||
|  | @ -56,21 +53,14 @@ class FormResource extends JsonResource | ||||||
|             'needs_prevention' => $this->needs_prevention, |             'needs_prevention' => $this->needs_prevention, | ||||||
|             'prevention_text' => $this->prevention_text, |             'prevention_text' => $this->prevention_text, | ||||||
|             'prevention_conditions' => $this->prevention_conditions, |             'prevention_conditions' => $this->prevention_conditions, | ||||||
|             'leader_conditions' => $this->leader_conditions, |  | ||||||
|             'zip' => $this->zip, |  | ||||||
|             'location' => $this->location, |  | ||||||
|             'country' => $this->country, |  | ||||||
|             'links' => [ |             'links' => [ | ||||||
|                 'participant_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => null]), |                 'participant_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => null]), | ||||||
|                 'participant_root_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => -1]), |                 'participant_root_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => -1]), | ||||||
|                 'update' => route('form.update', $this->getModel()), |                 'update' => route('form.update', ['form' => $this->getModel()]), | ||||||
|                 'destroy' => route('form.destroy', $this->getModel()), |                 'destroy' => route('form.destroy', ['form' => $this->getModel()]), | ||||||
|                 'is_dirty' => route('form.is-dirty', $this->getModel()), |                 'is_dirty' => route('form.is-dirty', ['form' => $this->getModel()]), | ||||||
|                 'frontend' => str(app(FormSettings::class)->registerUrl)->replace('{slug}', $this->slug), |                 'frontend' => str(app(FormSettings::class)->registerUrl)->replace('{slug}', $this->slug), | ||||||
|                 'export' => route('form.export', $this->getModel()), |                 'export' => route('form.export', ['form' => $this->getModel()]), | ||||||
|                 'copy' => route('form.copy', $this->getModel()), |  | ||||||
|                 'contribution' => route('form.contribution', $this->getModel()), |  | ||||||
|                 'laterlink' => route('form.laterlink', $this->getModel()), |  | ||||||
|             ] |             ] | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | @ -92,8 +82,6 @@ class FormResource extends JsonResource | ||||||
|             'templates' => FormtemplateResource::collection(Formtemplate::get()), |             'templates' => FormtemplateResource::collection(Formtemplate::get()), | ||||||
|             'namiTypes' => NamiType::forSelect(), |             'namiTypes' => NamiType::forSelect(), | ||||||
|             'specialTypes' => SpecialType::forSelect(), |             'specialTypes' => SpecialType::forSelect(), | ||||||
|             'countries' => Country::forSelect(), |  | ||||||
|             'contribution_types' => app(ContributionFactory::class)->compilerSelect(), |  | ||||||
|             'default' => [ |             'default' => [ | ||||||
|                 'description' => [], |                 'description' => [], | ||||||
|                 'is_active' => true, |                 'is_active' => true, | ||||||
|  | @ -113,9 +101,6 @@ class FormResource extends JsonResource | ||||||
|                 'id' => null, |                 'id' => null, | ||||||
|                 'export' => ExportData::from([]), |                 'export' => ExportData::from([]), | ||||||
|                 'prevention_conditions' => ['mode' => 'all', 'ifs' => []], |                 'prevention_conditions' => ['mode' => 'all', 'ifs' => []], | ||||||
|                 'zip' => '', |  | ||||||
|                 'location' => '', |  | ||||||
|                 'country' => null, |  | ||||||
|             ], |             ], | ||||||
|             'section_default' => [ |             'section_default' => [ | ||||||
|                 'name' => '', |                 'name' => '', | ||||||
|  |  | ||||||
|  | @ -1,60 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Http\Controllers\Auth; |  | ||||||
| 
 |  | ||||||
| use App\Http\Controllers\Controller; |  | ||||||
| use App\Providers\RouteServiceProvider; |  | ||||||
| use Illuminate\Foundation\Auth\AuthenticatesUsers; |  | ||||||
| use Illuminate\Http\Request; |  | ||||||
| use Inertia\Response; |  | ||||||
| 
 |  | ||||||
| class LoginController extends Controller |  | ||||||
| { |  | ||||||
|     /* |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | Login Controller |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | |  | ||||||
|     | This controller handles authenticating users for the application and |  | ||||||
|     | redirecting them to your home screen. The controller uses a trait |  | ||||||
|     | to conveniently provide its functionality to your applications. |  | ||||||
|     | |  | ||||||
|     */ |  | ||||||
| 
 |  | ||||||
|     use AuthenticatesUsers; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Where to redirect users after login. |  | ||||||
|      * |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     protected $redirectTo = RouteServiceProvider::HOME; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new controller instance. |  | ||||||
|      * |  | ||||||
|      * @return void |  | ||||||
|      */ |  | ||||||
|     public function __construct() |  | ||||||
|     { |  | ||||||
|         $this->middleware('guest')->except('logout'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function showLoginForm(): Response |  | ||||||
|     { |  | ||||||
|         session()->put('title', 'Anmelden'); |  | ||||||
| 
 |  | ||||||
|         return \Inertia::render('authentication/VLogin'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Validate the user login request. |  | ||||||
|      */ |  | ||||||
|     protected function validateLogin(Request $request): void |  | ||||||
|     { |  | ||||||
|         $request->validate([ |  | ||||||
|             $this->username() => 'required|max:255|string|email', |  | ||||||
|             'password' => 'required|string', |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,71 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Http\Controllers\Auth; |  | ||||||
| 
 |  | ||||||
| use App\Http\Controllers\Controller; |  | ||||||
| use App\Providers\RouteServiceProvider; |  | ||||||
| use App\User; |  | ||||||
| use Illuminate\Foundation\Auth\RegistersUsers; |  | ||||||
| use Illuminate\Support\Facades\Hash; |  | ||||||
| use Illuminate\Support\Facades\Validator; |  | ||||||
| 
 |  | ||||||
| class RegisterController extends Controller |  | ||||||
| { |  | ||||||
|     /* |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | Register Controller |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | |  | ||||||
|     | This controller handles the registration of new users as well as their |  | ||||||
|     | validation and creation. By default this controller uses a trait to |  | ||||||
|     | provide this functionality without requiring any additional code. |  | ||||||
|     | |  | ||||||
|     */ |  | ||||||
| 
 |  | ||||||
|     use RegistersUsers; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Where to redirect users after registration. |  | ||||||
|      * |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     protected $redirectTo = RouteServiceProvider::HOME; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new controller instance. |  | ||||||
|      * |  | ||||||
|      * @return void |  | ||||||
|      */ |  | ||||||
|     public function __construct() |  | ||||||
|     { |  | ||||||
|         $this->middleware('guest'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get a validator for an incoming registration request. |  | ||||||
|      * |  | ||||||
|      * @return \Illuminate\Contracts\Validation\Validator |  | ||||||
|      */ |  | ||||||
|     protected function validator(array $data) |  | ||||||
|     { |  | ||||||
|         return Validator::make($data, [ |  | ||||||
|             'name' => ['required', 'string', 'max:255'], |  | ||||||
|             'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], |  | ||||||
|             'password' => ['required', 'string', 'min:8', 'confirmed'], |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new user instance after a valid registration. |  | ||||||
|      * |  | ||||||
|      * @return \App\User |  | ||||||
|      */ |  | ||||||
|     protected function create(array $data) |  | ||||||
|     { |  | ||||||
|         return User::create([ |  | ||||||
|             'name' => $data['name'], |  | ||||||
|             'email' => $data['email'], |  | ||||||
|             'password' => Hash::make($data['password']), |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,50 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Http\Controllers\Auth; |  | ||||||
| 
 |  | ||||||
| use App\Http\Controllers\Controller; |  | ||||||
| use App\Providers\RouteServiceProvider; |  | ||||||
| use Illuminate\Foundation\Auth\ResetsPasswords; |  | ||||||
| use Illuminate\Http\Request; |  | ||||||
| use Inertia\Inertia; |  | ||||||
| use Inertia\Response; |  | ||||||
| 
 |  | ||||||
| class ResetPasswordController extends Controller |  | ||||||
| { |  | ||||||
|     /* |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | Password Reset Controller |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | |  | ||||||
|     | This controller is responsible for handling password reset requests |  | ||||||
|     | and uses a simple trait to include this behavior. You're free to |  | ||||||
|     | explore this trait and override any methods you wish to tweak. |  | ||||||
|     | |  | ||||||
|     */ |  | ||||||
| 
 |  | ||||||
|     use ResetsPasswords; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Where to redirect users after resetting their password. |  | ||||||
|      * |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     protected $redirectTo = RouteServiceProvider::HOME; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Display the password reset view for the given token. |  | ||||||
|      * |  | ||||||
|      * If no token is present, display the link request form. |  | ||||||
|      * |  | ||||||
|      * @param  \Illuminate\Http\Request  $request |  | ||||||
|      */ |  | ||||||
|     public function showResetForm(Request $request): Response |  | ||||||
|     { |  | ||||||
|         $token = $request->route()->parameter('token'); |  | ||||||
| 
 |  | ||||||
|         return Inertia::render('authentication/PasswordResetConfirm', [ |  | ||||||
|             'token' => $token, |  | ||||||
|             'email' => $request->email, |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| namespace App\Http\Middleware; | namespace App\Http\Middleware; | ||||||
| 
 | 
 | ||||||
| use App\Http\Resources\UserResource; | use App\Http\Resources\UserResource; | ||||||
| use App\Module\ModuleSettings; | use Modules\Module\ModuleSettings; | ||||||
| use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||||
| use Illuminate\Support\Facades\Session; | use Illuminate\Support\Facades\Session; | ||||||
| use Inertia\Middleware; | use Inertia\Middleware; | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ class InitializeAction | ||||||
| 
 | 
 | ||||||
|     public function asController(ActionRequest $request, NamiSettings $settings): RedirectResponse |     public function asController(ActionRequest $request, NamiSettings $settings): RedirectResponse | ||||||
|     { |     { | ||||||
|         $settings->mglnr = $request->input('mglnr'); |         $settings->mglnr = (int) $request->input('mglnr'); | ||||||
|         $settings->password = $request->input('password'); |         $settings->password = $request->input('password'); | ||||||
|         $settings->default_group_id = (int) $request->input('group_id'); |         $settings->default_group_id = (int) $request->input('group_id'); | ||||||
|         $settings->search_params = $request->input('params'); |         $settings->search_params = $request->input('params'); | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ class NamiGetSearchLayerAction | ||||||
|      */ |      */ | ||||||
|     public function handle(array $input): Collection |     public function handle(array $input): Collection | ||||||
|     { |     { | ||||||
|         return Nami::login($input['mglnr'], $input['password'])->searchLayerOptions( |         return Nami::login((int) $input['mglnr'], $input['password'])->searchLayerOptions( | ||||||
|             SearchLayer::from($input['layer'] ?: 0), |             SearchLayer::from($input['layer'] ?: 0), | ||||||
|             $input['parent'] ?: null |             $input['parent'] ?: null | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ class NamiLoginCheckAction | ||||||
|      */ |      */ | ||||||
|     public function handle(array $input): void |     public function handle(array $input): void | ||||||
|     { |     { | ||||||
|         Nami::freshLogin($input['mglnr'], $input['password']); |         Nami::freshLogin((int) $input['mglnr'], $input['password']); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ class NamiSearchAction | ||||||
|     /** |     /** | ||||||
|      * @param array<string, mixed> $params |      * @param array<string, mixed> $params | ||||||
|      * |      * | ||||||
|      * @return LengthAwarePaginator<int, MemberEntry> |      * @return LengthAwarePaginator<MemberEntry> | ||||||
|      */ |      */ | ||||||
|     public function handle(Api $api, int $page, array $params, int $perPage = 10): LengthAwarePaginator |     public function handle(Api $api, int $page, array $params, int $perPage = 10): LengthAwarePaginator | ||||||
|     { |     { | ||||||
|  | @ -36,7 +36,7 @@ class NamiSearchAction | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return LengthAwarePaginator<int, MemberEntry> |      * @return LengthAwarePaginator<MemberEntry> | ||||||
|      */ |      */ | ||||||
|     public function asController(ActionRequest $request): LengthAwarePaginator |     public function asController(ActionRequest $request): LengthAwarePaginator | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ use App\Invoice\Models\Invoice; | ||||||
| use App\Payment\Payment; | use App\Payment\Payment; | ||||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | use Illuminate\Database\Eloquent\Relations\HasMany; | ||||||
| use Illuminate\Support\Str; | use Illuminate\Support\Str; | ||||||
|  | use Modules\Invoice\InvoiceSettings; | ||||||
| use Zoomyboy\Tex\Document; | use Zoomyboy\Tex\Document; | ||||||
| use Zoomyboy\Tex\Engine; | use Zoomyboy\Tex\Engine; | ||||||
| use Zoomyboy\Tex\Template; | use Zoomyboy\Tex\Template; | ||||||
|  |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Invoice; |  | ||||||
| 
 |  | ||||||
| use App\Dashboard\Blocks\Block; |  | ||||||
| use App\Invoice\Models\InvoicePosition; |  | ||||||
| use App\Member\Member; |  | ||||||
| 
 |  | ||||||
| class MemberPaymentBlock extends Block |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @return array<string, string|int> |  | ||||||
|      */ |  | ||||||
|     public function data(): array |  | ||||||
|     { |  | ||||||
|         $amount = InvoicePosition::whereHas('invoice', fn ($query) => $query->whereNeedsPayment()) |  | ||||||
|             ->selectRaw('sum(price) AS price') |  | ||||||
|             ->first(); |  | ||||||
|         $members = Member::whereHasPendingPayment()->count(); |  | ||||||
| 
 |  | ||||||
|         return [ |  | ||||||
|             'members' => $members, |  | ||||||
|             'total_members' => Member::count(), |  | ||||||
|             'amount' => number_format((int) $amount->price / 100, 2, ',', '.') . ' €', |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function component(): string |  | ||||||
|     { |  | ||||||
|         return 'member-payment'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function title(): string |  | ||||||
|     { |  | ||||||
|         return 'Ausstehende Mitgliedsbeiträge'; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -6,7 +6,7 @@ use App\Invoice\BillDocument; | ||||||
| use App\Invoice\BillKind; | use App\Invoice\BillKind; | ||||||
| use App\Invoice\Enums\InvoiceStatus; | use App\Invoice\Enums\InvoiceStatus; | ||||||
| use App\Invoice\InvoiceDocument; | use App\Invoice\InvoiceDocument; | ||||||
| use App\Invoice\InvoiceSettings; | use Modules\Invoice\InvoiceSettings; | ||||||
| use App\Invoice\RememberDocument; | use App\Invoice\RememberDocument; | ||||||
| use App\Invoice\Scopes\InvoiceFilterScope; | use App\Invoice\Scopes\InvoiceFilterScope; | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ namespace App\Invoice\Scopes; | ||||||
| 
 | 
 | ||||||
| use App\Invoice\Enums\InvoiceStatus; | use App\Invoice\Enums\InvoiceStatus; | ||||||
| use App\Invoice\Models\Invoice; | use App\Invoice\Models\Invoice; | ||||||
|  | use App\Lib\Filter; | ||||||
| use App\Lib\ScoutFilter; | use App\Lib\ScoutFilter; | ||||||
| use Laravel\Scout\Builder; | use Laravel\Scout\Builder; | ||||||
| use Spatie\LaravelData\Attributes\MapInputName; | use Spatie\LaravelData\Attributes\MapInputName; | ||||||
|  |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Lib\Data; |  | ||||||
| 
 |  | ||||||
| use Spatie\LaravelData\Normalizers\Normalizer; |  | ||||||
| use App\Lib\Normalizers\DateNormalizer; |  | ||||||
| use Spatie\LaravelData\Data; |  | ||||||
| use Spatie\LaravelData\Attributes\WithTransformer; |  | ||||||
| use App\Lib\Transformers\DateTransformer; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 |  | ||||||
| class DateData extends Data |  | ||||||
| { |  | ||||||
|     public function __construct( |  | ||||||
|         #[WithTransformer(DateTransformer::class)]
 |  | ||||||
|         public Carbon $raw, |  | ||||||
|         public string $human, |  | ||||||
|     ) {} |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<int, class-string<Normalizer>> |  | ||||||
|      */ |  | ||||||
|     public static function normalizers(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             DateNormalizer::class, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Lib\Data; |  | ||||||
| 
 |  | ||||||
| use Spatie\LaravelData\Data; |  | ||||||
| 
 |  | ||||||
| class RecordData extends Data { |  | ||||||
| 
 |  | ||||||
|     public function __construct( |  | ||||||
|         public int $id, |  | ||||||
|         public string $name, |  | ||||||
|     ) {} |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -2,19 +2,24 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Lib\Editor; | namespace App\Lib\Editor; | ||||||
| 
 | 
 | ||||||
|  | use Livewire\Wireable; | ||||||
|  | use Spatie\LaravelData\Concerns\WireableData; | ||||||
| use Spatie\LaravelData\Data; | use Spatie\LaravelData\Data; | ||||||
| use Spatie\LaravelData\Support\EloquentCasts\DataEloquentCast; | use Spatie\LaravelData\Support\EloquentCasts\DataEloquentCast; | ||||||
| 
 | 
 | ||||||
| /** @todo replace blocks with actual block data classes */ | /** @todo replace blocks with actual block data classes */ | ||||||
| class EditorData extends Data implements Editorable | class EditorData extends Data implements Editorable, Wireable | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|  |     use WireableData; | ||||||
|  | 
 | ||||||
|     /** @param array<int, mixed> $blocks */ |     /** @param array<int, mixed> $blocks */ | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         public string $version, |         public string $version, | ||||||
|         public array $blocks, |         public array $blocks, | ||||||
|         public int $time |         public int $time | ||||||
|     ) {} |     ) { | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     public function placeholder(string $search, string $replacement): self |     public function placeholder(string $search, string $replacement): self | ||||||
|     { |     { | ||||||
|  | @ -29,12 +34,7 @@ class EditorData extends Data implements Editorable | ||||||
|      */ |      */ | ||||||
|     public function hasAll(array $wanted): bool |     public function hasAll(array $wanted): bool | ||||||
|     { |     { | ||||||
|         return collect($wanted)->doesntContain(fn($search) => !str(json_encode($this->blocks))->contains($search)); |         return collect($wanted)->first(fn ($search) => !str(json_encode($this->blocks))->contains($search)) === null; | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function hasNot(string $should): bool |  | ||||||
|     { |  | ||||||
|         return !str(json_encode($this->blocks))->contains($should); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function default(): self |     public static function default(): self | ||||||
|  | @ -69,7 +69,7 @@ class EditorData extends Data implements Editorable | ||||||
|                     'type' => 'list', |                     'type' => 'list', | ||||||
|                     'data' => [ |                     'data' => [ | ||||||
|                         'style' => 'unordered', |                         'style' => 'unordered', | ||||||
|                         'items' => collect($replacements)->map(fn($replacement) => [ |                         'items' => collect($replacements)->map(fn ($replacement) => [ | ||||||
|                             'content' => $replacement, |                             'content' => $replacement, | ||||||
|                             'items' => [], |                             'items' => [], | ||||||
|                         ]), |                         ]), | ||||||
|  |  | ||||||
|  | @ -14,12 +14,10 @@ abstract class Filter extends Data | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |      * @param Builder<T> $query | ||||||
|      * @return Builder<T> |      * @return Builder<T> | ||||||
|      */ |      */ | ||||||
|     abstract public function getQuery(): Builder; |     abstract public function apply(Builder $query): Builder; | ||||||
| 
 |  | ||||||
|     /** @var Builder<T> */ |  | ||||||
|     protected Builder $query; |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param array<string, mixed>|string|null $request |      * @param array<string, mixed>|string|null $request | ||||||
|  | @ -38,6 +36,14 @@ abstract class Filter extends Data | ||||||
|      */ |      */ | ||||||
|     public static function fromPost(?array $post = null): static |     public static function fromPost(?array $post = null): static | ||||||
|     { |     { | ||||||
|         return static::factory()->withoutMagicalCreation()->from($post ?: []); |         return static::factory()->withoutMagicalCreation()->from($post ?: [])->toDefault(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @return static | ||||||
|  |      */ | ||||||
|  |     public function toDefault(): self | ||||||
|  |     { | ||||||
|  |         return $this; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Lib; |  | ||||||
| 
 |  | ||||||
| use Spatie\LaravelData\PaginatedDataCollection; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @mixin Spatie\LaravelData\Data |  | ||||||
|  */ |  | ||||||
| trait HasDataMeta |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public static function collectPages(mixed $items): array { |  | ||||||
|         $source = parent::collect($items, PaginatedDataCollection::class)->toArray(); |  | ||||||
|         return [ |  | ||||||
|             ...parent::collect($items, PaginatedDataCollection::class)->toArray(), |  | ||||||
|             'meta' => [...$source['meta'], ...static::meta()] |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -2,9 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Lib; | namespace App\Lib; | ||||||
| 
 | 
 | ||||||
| /** | /** @mixin \Illuminate\Http\Resources\Json\JsonResource */ | ||||||
|  * @mixin \Illuminate\Http\Resources\Json\JsonResource |  | ||||||
|  */ |  | ||||||
| trait HasMeta | trait HasMeta | ||||||
| { | { | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -1,24 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Lib\Normalizers; |  | ||||||
| 
 |  | ||||||
| use Spatie\LaravelData\Normalizers\Normalizer; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 |  | ||||||
| class DateNormalizer implements Normalizer |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public function normalize(mixed $value): ?array |  | ||||||
|     { |  | ||||||
|         if (!$value instanceof Carbon) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return [ |  | ||||||
|             'raw' => $value, |  | ||||||
|             'human' => $value->format('d.m.Y'), |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Lib\Transformers; |  | ||||||
| 
 |  | ||||||
| use Spatie\LaravelData\Transformers\Transformer; |  | ||||||
| use Spatie\LaravelData\Support\DataProperty; |  | ||||||
| use Spatie\LaravelData\Support\Transformation\TransformationContext; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| 
 |  | ||||||
| class DateTransformer implements Transformer |  | ||||||
| { |  | ||||||
|     public function transform(DataProperty $property, mixed $value, TransformationContext $context): string |  | ||||||
|     { |  | ||||||
|         return Carbon::parse($value)->format('Y-m-d'); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Maildispatcher\Models; | namespace App\Maildispatcher\Models; | ||||||
| 
 | 
 | ||||||
| use App\Mailgateway\Models\Mailgateway; | use Modules\Mailgateway\Models\Mailgateway; | ||||||
| use Database\Factories\Maildispatcher\Models\MaildispatcherFactory; | use Database\Factories\Maildispatcher\Models\MaildispatcherFactory; | ||||||
| use Illuminate\Database\Eloquent\Concerns\HasUuids; | use Illuminate\Database\Eloquent\Concerns\HasUuids; | ||||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||||
|  |  | ||||||
|  | @ -4,8 +4,7 @@ namespace App\Maildispatcher\Resources; | ||||||
| 
 | 
 | ||||||
| use App\Lib\HasMeta; | use App\Lib\HasMeta; | ||||||
| use App\Maildispatcher\Models\Maildispatcher; | use App\Maildispatcher\Models\Maildispatcher; | ||||||
| use App\Mailgateway\Models\Mailgateway; | use Modules\Mailgateway\Models\Mailgateway; | ||||||
| use App\Mailgateway\Resources\MailgatewayResource; |  | ||||||
| use App\Member\FilterScope; | use App\Member\FilterScope; | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
| use Illuminate\Http\Resources\Json\JsonResource; | use Illuminate\Http\Resources\Json\JsonResource; | ||||||
|  |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Mailgateway\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Mailgateway\Models\Mailgateway; |  | ||||||
| use Lorisleiva\Actions\ActionRequest; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class StoreAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
|     use ValidatesRequests; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param array<string, mixed> $input |  | ||||||
|      */ |  | ||||||
|     public function handle(array $input): void |  | ||||||
|     { |  | ||||||
|         $this->checkIfWorks($input); |  | ||||||
|         Mailgateway::create($input); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function asController(ActionRequest $request): void |  | ||||||
|     { |  | ||||||
|         $this->handle($request->validated()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Mailgateway\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Mailgateway\Models\Mailgateway; |  | ||||||
| use Lorisleiva\Actions\ActionRequest; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class UpdateAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
|     use ValidatesRequests; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param array<string, mixed> $input |  | ||||||
|      */ |  | ||||||
|     public function handle(Mailgateway $mailgateway, array $input): void |  | ||||||
|     { |  | ||||||
|         $this->checkIfWorks($input); |  | ||||||
| 
 |  | ||||||
|         $mailgateway->update($input); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function asController(Mailgateway $mailgateway, ActionRequest $request): void |  | ||||||
|     { |  | ||||||
|         $this->handle($mailgateway, $request->validated()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,65 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Mailgateway\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Mailgateway\Types\Type; |  | ||||||
| use Illuminate\Validation\Rule; |  | ||||||
| use Illuminate\Validation\ValidationException; |  | ||||||
| use Lorisleiva\Actions\ActionRequest; |  | ||||||
| 
 |  | ||||||
| trait ValidatesRequests |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @param array<string, mixed> $input |  | ||||||
|      */ |  | ||||||
|     public function checkIfWorks(array $input): void |  | ||||||
|     { |  | ||||||
|         if (!app(data_get($input, 'type.cls'))->setParams($input['type']['params'])->works()) { |  | ||||||
|             throw ValidationException::withMessages(['connection' => 'Verbindung fehlgeschlagen.']); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public function rules(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'name' => 'required|string|max:255', |  | ||||||
|             'domain' => 'required|string|max:255', |  | ||||||
|             ...$this->typeValidation(), |  | ||||||
|             'type.params' => 'present|array', |  | ||||||
|             ...collect(request()->input('type.cls')::rules('storeValidator'))->mapWithKeys(fn ($rules, $key) => ["type.params.{$key}" => $rules]), |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public function getValidationAttributes(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'type.cls' => 'Typ', |  | ||||||
|             'name' => 'Beschreibung', |  | ||||||
|             'domain' => 'Domain', |  | ||||||
|             ...collect(request()->input('type.cls')::fieldNames())->mapWithKeys(fn ($attribute, $key) => ["type.params.{$key}" => $attribute]), |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     private function typeValidation(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'type.cls' => ['required', 'string', 'max:255', Rule::in(app('mail-gateways'))], |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function prepareForValidation(ActionRequest $request): void |  | ||||||
|     { |  | ||||||
|         if (!is_subclass_of(request()->input('type.cls'), Type::class)) { |  | ||||||
|             throw ValidationException::withMessages(['type.cls' => 'Typ ist nicht valide.']); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,42 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Mailgateway\Casts; |  | ||||||
| 
 |  | ||||||
| use App\Mailgateway\Types\Type; |  | ||||||
| use Illuminate\Contracts\Database\Eloquent\CastsAttributes; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @implements CastsAttributes<Type, Type> |  | ||||||
|  */ |  | ||||||
| class TypeCast implements CastsAttributes |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * Cast the given value. |  | ||||||
|      * |  | ||||||
|      * @param \Illuminate\Database\Eloquent\Model $model |  | ||||||
|      * @param mixed                               $value |  | ||||||
|      * @param array<string, mixed>                $attributes |  | ||||||
|      * |  | ||||||
|      * @return mixed |  | ||||||
|      */ |  | ||||||
|     public function get($model, string $key, $value, array $attributes) |  | ||||||
|     { |  | ||||||
|         $value = json_decode($value, true); |  | ||||||
| 
 |  | ||||||
|         return app($value['cls'])->setParams($value['params']); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Prepare the given value for storage. |  | ||||||
|      * |  | ||||||
|      * @param \Illuminate\Database\Eloquent\Model $model |  | ||||||
|      * @param mixed                               $value |  | ||||||
|      * @param array<string, mixed>                $attributes |  | ||||||
|      * |  | ||||||
|      * @return mixed |  | ||||||
|      */ |  | ||||||
|     public function set($model, string $key, $value, array $attributes) |  | ||||||
|     { |  | ||||||
|         return json_encode($value); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,68 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Mailgateway\Resources; |  | ||||||
| 
 |  | ||||||
| use App\Lib\HasMeta; |  | ||||||
| use App\Mailgateway\Models\Mailgateway; |  | ||||||
| use Illuminate\Http\Resources\Json\JsonResource; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @mixin Mailgateway |  | ||||||
|  */ |  | ||||||
| class MailgatewayResource extends JsonResource |  | ||||||
| { |  | ||||||
|     use HasMeta; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Transform the resource into an array. |  | ||||||
|      * |  | ||||||
|      * @param \Illuminate\Http\Request $request |  | ||||||
|      * |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public function toArray($request) |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'name' => $this->name, |  | ||||||
|             'domain' => $this->domain, |  | ||||||
|             'type_human' => $this->type::name(), |  | ||||||
|             'works' => $this->type->works(), |  | ||||||
|             'type' => $this->type->toResource(), |  | ||||||
|             'id' => $this->id, |  | ||||||
|             'links' => [ |  | ||||||
|                 'update' => route('mailgateway.update', ['mailgateway' => $this->getModel()]), |  | ||||||
|             ], |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public static function meta(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'links' => [ |  | ||||||
|                 'store' => route('mailgateway.store'), |  | ||||||
|             ], |  | ||||||
|             'types' => app('mail-gateways')->map(fn ($gateway) => [ |  | ||||||
|                 'id' => $gateway, |  | ||||||
|                 'name' => $gateway::name(), |  | ||||||
|                 'fields' => $gateway::presentFields('storeValidator'), |  | ||||||
|                 'defaults' => (object) $gateway::defaults(), |  | ||||||
|             ])->prepend([ |  | ||||||
|                 'id' => null, |  | ||||||
|                 'name' => '-- kein --', |  | ||||||
|                 'fields' => [], |  | ||||||
|                 'defaults' => (object) [], |  | ||||||
|             ]), |  | ||||||
|             'default' => [ |  | ||||||
|                 'domain' => '', |  | ||||||
|                 'name' => '', |  | ||||||
|                 'type' => [ |  | ||||||
|                     'params' => [], |  | ||||||
|                     'cls' => null, |  | ||||||
|                 ], |  | ||||||
|             ], |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -85,7 +85,7 @@ class MailmanService | ||||||
|         return app(Paginator::class)->result( |         return app(Paginator::class)->result( | ||||||
|             fn ($page) => $this->http()->get("/lists/{$list->listId}/roster/member?page={$page}&count=10"), |             fn ($page) => $this->http()->get("/lists/{$list->listId}/roster/member?page={$page}&count=10"), | ||||||
|             function ($response) use ($list) { |             function ($response) use ($list) { | ||||||
|                 throw_unless($response->ok(), MailmanServiceException::class, 'Fetching members for listId '.$list->listId.' failed.'); |                 throw_unless($response->ok(), MailmanServiceException::class, 'Fetching members for listId ' . $list->listId . ' failed.'); | ||||||
|                 /** @var array<int, array{email: string, self_link: string}>|null */ |                 /** @var array<int, array{email: string, self_link: string}>|null */ | ||||||
|                 $entries = data_get($response->json(), 'entries', []); |                 $entries = data_get($response->json(), 'entries', []); | ||||||
|                 throw_if(is_null($entries), MailmanServiceException::class, 'Failed getting member list from response'); |                 throw_if(is_null($entries), MailmanServiceException::class, 'Failed getting member list from response'); | ||||||
|  | @ -112,7 +112,7 @@ class MailmanService | ||||||
|             'pre_confirmed' => 'true', |             'pre_confirmed' => 'true', | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         throw_unless(201 === $response->status(), MailmanServiceException::class, 'Adding member '.$email.' to '.$list->listId.' failed'); |         throw_unless(201 === $response->status(), MailmanServiceException::class, 'Adding member ' . $email . ' to ' . $list->listId . ' failed'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function removeMember(Member $member): void |     public function removeMember(Member $member): void | ||||||
|  |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Member\Data; |  | ||||||
| 
 |  | ||||||
| use Spatie\LaravelData\Data; |  | ||||||
| use App\Member\Member; |  | ||||||
| 
 |  | ||||||
| class MemberData extends Data |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
|     public function __construct( |  | ||||||
|         public string $fullname, |  | ||||||
|     ) {} |  | ||||||
| 
 |  | ||||||
|     public static function fromModel(Member $member): static |  | ||||||
|     { |  | ||||||
|         return static::factory()->withoutMagicalCreation()->from([ |  | ||||||
|             'fullname' => $member->fullname |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,67 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Member\Data; |  | ||||||
| 
 |  | ||||||
| use App\Activity; |  | ||||||
| use App\Group; |  | ||||||
| use Spatie\LaravelData\Data; |  | ||||||
| use App\Lib\Data\DateData; |  | ||||||
| use App\Lib\Data\RecordData; |  | ||||||
| use App\Lib\HasDataMeta; |  | ||||||
| use App\Member\Membership; |  | ||||||
| use App\Membership\FilterScope; |  | ||||||
| use App\Subactivity; |  | ||||||
| 
 |  | ||||||
| class MembershipData extends Data |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
|     use HasDataMeta; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param array<string, string> $links |  | ||||||
|      */ |  | ||||||
|     public function __construct( |  | ||||||
|         public int $id, |  | ||||||
|         public RecordData $activity, |  | ||||||
|         public ?RecordData $subactivity, |  | ||||||
|         public RecordData $group, |  | ||||||
|         public ?DateData $promisedAt, |  | ||||||
|         public DateData $from, |  | ||||||
|         public ?DateData $to, |  | ||||||
|         public MemberData $member, |  | ||||||
|         public bool $isActive, |  | ||||||
|         public array $links, |  | ||||||
|     ) {} |  | ||||||
| 
 |  | ||||||
|     public static function fromModel(Membership $membership): static |  | ||||||
|     { |  | ||||||
|         return static::factory()->withoutMagicalCreation()->from([ |  | ||||||
|             'id' => $membership->id, |  | ||||||
|             'activity' => $membership->activity, |  | ||||||
|             'subactivity' => $membership->subactivity, |  | ||||||
|             'isActive' => $membership->isActive(), |  | ||||||
|             'from' => $membership->from, |  | ||||||
|             'to' => $membership->to, |  | ||||||
|             'group' => $membership->group, |  | ||||||
|             'promisedAt' => $membership->promised_at, |  | ||||||
|             'member' => $membership->member, |  | ||||||
|             'links' => [ |  | ||||||
|                 'update' => route('membership.update', $membership), |  | ||||||
|                 'destroy' => route('membership.destroy', $membership), |  | ||||||
|             ] |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public static function meta(): array { |  | ||||||
|         return [ |  | ||||||
|             'activities' => RecordData::collect(Activity::get()), |  | ||||||
|             'subactivities' => RecordData::collect(Subactivity::get()), |  | ||||||
|             'groups' => RecordData::collect(Group::get()), |  | ||||||
|             'filter' => FilterScope::fromRequest(request()->input('filter', '')), |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -48,7 +48,8 @@ class FilterScope extends ScoutFilter | ||||||
|         public ?bool $hasBirthday = null, |         public ?bool $hasBirthday = null, | ||||||
|         public ?bool $hasSvk = null, |         public ?bool $hasSvk = null, | ||||||
|         public ?bool $hasVk = null, |         public ?bool $hasVk = null, | ||||||
|     ) {} |     ) { | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param array<string, mixed> $options |      * @param array<string, mixed> $options | ||||||
|  | @ -109,20 +110,20 @@ class FilterScope extends ScoutFilter | ||||||
|             } |             } | ||||||
|             if ($this->subactivityIds && $this->activityIds) { |             if ($this->subactivityIds && $this->activityIds) { | ||||||
|                 $combinations = $this->combinations($this->activityIds, $this->subactivityIds) |                 $combinations = $this->combinations($this->activityIds, $this->subactivityIds) | ||||||
|                     ->map(fn($combination) => implode('|', $combination)) |                     ->map(fn ($combination) => implode('|', $combination)) | ||||||
|                     ->map(fn($combination) => str($combination)->wrap('"')); |                     ->map(fn ($combination) => str($combination)->wrap('"')); | ||||||
|                 $filter->push($this->inExpression('memberships.both', $combinations)); |                 $filter->push($this->inExpression('memberships.both', $combinations)); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             foreach ($this->memberships as $membership) { |             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)) { |             if (count($this->exclude)) { | ||||||
|                 $filter->push($this->notInExpression('id', $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['filter'] = $this->implode(collect([$andFilter])->push($this->inExpression('id', $this->include)), 'OR'); | ||||||
|             $options['sort'] = ['lastname:asc', 'firstname:asc']; |             $options['sort'] = ['lastname:asc', 'firstname:asc']; | ||||||
|  | @ -136,7 +137,7 @@ class FilterScope extends ScoutFilter | ||||||
|      */ |      */ | ||||||
|     protected function implode(Collection $values, string $between): string |     protected function implode(Collection $values, string $between): string | ||||||
|     { |     { | ||||||
|         return $values->filter(fn($expression) => $expression)->implode(" {$between} "); |         return $values->filter(fn ($expression) => $expression)->implode(" {$between} "); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -176,7 +177,7 @@ class FilterScope extends ScoutFilter | ||||||
|         $membership['activity_ids'] = count($membership['activity_ids']) === 0 ? Activity::pluck('id')->toArray() : $membership['activity_ids']; |         $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']; |         $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']) |         return $this->combinations($membership['group_ids'], $membership['activity_ids'], $membership['subactivity_ids']) | ||||||
|             ->map(fn($combination) => collect($combination)->implode('|')); |             ->map(fn ($combination) => collect($combination)->implode('|')); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -190,7 +191,7 @@ class FilterScope extends ScoutFilter | ||||||
| 
 | 
 | ||||||
|         if (!count($otherParts)) { |         if (!count($otherParts)) { | ||||||
|             /** @var Collection<int, Collection<int, int>> */ |             /** @var Collection<int, Collection<int, int>> */ | ||||||
|             return collect($firstPart)->map(fn($p) => [$p]); |             return collect($firstPart)->map(fn ($p) => [$p]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /** @var Collection<int, mixed> */ |         /** @var Collection<int, mixed> */ | ||||||
|  |  | ||||||
|  | @ -13,8 +13,6 @@ use App\Nami\HasNamiField; | ||||||
| use App\Nationality; | use App\Nationality; | ||||||
| use App\Payment\Subscription; | use App\Payment\Subscription; | ||||||
| use App\Pdf\Sender; | use App\Pdf\Sender; | ||||||
| use App\Prevention\Contracts\Preventable; |  | ||||||
| use App\Prevention\Data\PreventionData; |  | ||||||
| use App\Region; | use App\Region; | ||||||
| use App\Setting\NamiSettings; | use App\Setting\NamiSettings; | ||||||
| use Carbon\Carbon; | use Carbon\Carbon; | ||||||
|  | @ -37,14 +35,12 @@ use Zoomyboy\Phone\HasPhoneNumbers; | ||||||
| use App\Prevention\Enums\Prevention; | use App\Prevention\Enums\Prevention; | ||||||
| use Database\Factories\Member\MemberFactory; | use Database\Factories\Member\MemberFactory; | ||||||
| use Illuminate\Database\Eloquent\Relations\HasOne; | use Illuminate\Database\Eloquent\Relations\HasOne; | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| use stdClass; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @property string $subscription_name |  * @property string $subscription_name | ||||||
|  * @property int    $pending_payment |  * @property int    $pending_payment | ||||||
|  */ |  */ | ||||||
| class Member extends Model implements Geolocatable, Preventable | class Member extends Model implements Geolocatable | ||||||
| { | { | ||||||
|     use Notifiable; |     use Notifiable; | ||||||
|     use HasNamiField; |     use HasNamiField; | ||||||
|  | @ -195,25 +191,7 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
| 
 | 
 | ||||||
|     protected function getAusstand(): int |     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'); | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function getMailRecipient(): ?stdClass |  | ||||||
|     { |  | ||||||
|         if (!$this->fullname) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!$this->email) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return (object) ['name' => $this->fullname, 'email' => $this->email]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function preventableSubject(): string |  | ||||||
|     { |  | ||||||
|         return 'Nachweise erforderlich'; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // ---------------------------------- Relations ----------------------------------
 |     // ---------------------------------- Relations ----------------------------------
 | ||||||
|  | @ -361,7 +339,7 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
|         return $query->addSelect([ |         return $query->addSelect([ | ||||||
|             'pending_payment' => InvoicePosition::selectRaw('SUM(price)') |             'pending_payment' => InvoicePosition::selectRaw('SUM(price)') | ||||||
|                 ->whereColumn('invoice_positions.member_id', 'members.id') |                 ->whereColumn('invoice_positions.member_id', 'members.id') | ||||||
|                 ->whereHas('invoice', fn($query) => $query->whereNeedsPayment()), |                 ->whereHas('invoice', fn ($query) => $query->whereNeedsPayment()), | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -372,7 +350,7 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
|      */ |      */ | ||||||
|     public function scopeWhereHasPendingPayment(Builder $query): Builder |     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())); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -386,47 +364,32 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * @return array<int, Prevention> | ||||||
|      */ |      */ | ||||||
|     public function preventions(?Carbon $date = null): Collection |     public function preventions(?Carbon $date = null): array | ||||||
|     { |     { | ||||||
|         $date = $date ?: now(); |         $date = $date ?: now(); | ||||||
| 
 | 
 | ||||||
|         /** @var Collection<int, PreventionData> */ |         /** @var array<int, Prevention> */ | ||||||
|         $preventions = collect([]); |         $preventions = []; | ||||||
| 
 | 
 | ||||||
|         if ($this->efz === null || $this->efz->diffInYears($date) >= 5) { |         if ($this->efz === null || $this->efz->diffInYears($date) >= 5) { | ||||||
|             $preventions->push(PreventionData::from([ |             $preventions[] = Prevention::EFZ; | ||||||
|                 'type' => Prevention::EFZ, |  | ||||||
|                 'expires' => $this->efz === null ? now() : $this->efz->addYears(5) |  | ||||||
|             ])); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!$this->has_vk) { |         if (!$this->has_vk) { | ||||||
|             $preventions->push(PreventionData::from([ |             $preventions[] = Prevention::VK; | ||||||
|                 'type' => Prevention::VK, |  | ||||||
|                 'expires' => now(), |  | ||||||
|             ])); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ($this->more_ps_at === null) { |         if ($this->more_ps_at === null) { | ||||||
|             if ($this->ps_at === null) { |             if ($this->ps_at === null) { | ||||||
|                 $preventions->push(PreventionData::from([ |                 $preventions[] = Prevention::PS; | ||||||
|                     'type' => Prevention::PS, |  | ||||||
|                     'expires' => now(), |  | ||||||
|                 ])); |  | ||||||
|             } else if ($this->ps_at->diffInYears($date) >= 5) { |             } else if ($this->ps_at->diffInYears($date) >= 5) { | ||||||
|                 $preventions->push(PreventionData::from([ |                 $preventions[] = Prevention::MOREPS; | ||||||
|                     'type' => Prevention::MOREPS, |  | ||||||
|                     'expires' => $this->ps_at->addYears(5), |  | ||||||
|                 ])); |  | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             if ($this->more_ps_at === null || $this->more_ps_at->diffInYears($date) >= 5) { |             if ($this->more_ps_at === null || $this->more_ps_at->diffInYears($date) >= 5) { | ||||||
|                 $preventions->push(PreventionData::from([ |                 $preventions[] = Prevention::MOREPS; | ||||||
|                     'type' => Prevention::MOREPS, |  | ||||||
|                     'expires' => $this->more_ps_at->addYears(5), |  | ||||||
|                 ])); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -536,7 +499,7 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
|             'name' => $this->fullname, |             'name' => $this->fullname, | ||||||
|             'address' => $this->address, |             'address' => $this->address, | ||||||
|             'zipLocation' => $this->zip . ' ' . $this->location, |             'zipLocation' => $this->zip . ' ' . $this->location, | ||||||
|             'mglnr' => Lazy::create(fn() => 'Mglnr.: ' . $this->nami_id), |             'mglnr' => Lazy::create(fn () => 'Mglnr.: ' . $this->nami_id), | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -545,7 +508,7 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
|      */ |      */ | ||||||
|     public static function forSelect(): array |     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 --------------------------------
 |     // -------------------------------- Geolocation --------------------------------
 | ||||||
|  | @ -604,7 +567,7 @@ class Member extends Model implements Geolocatable, Preventable | ||||||
|             'age_group_icon' => $this->ageGroupMemberships->first()?->subactivity->slug, |             'age_group_icon' => $this->ageGroupMemberships->first()?->subactivity->slug, | ||||||
|             'is_leader' => $this->leaderMemberships()->count() > 0, |             'is_leader' => $this->leaderMemberships()->count() > 0, | ||||||
|             'memberships' => $this->memberships()->active()->get() |             '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]), | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ class MemberController extends Controller | ||||||
|         session()->put('title', "Mitglied {$member->firstname} {$member->lastname} bearbeiten"); |         session()->put('title', "Mitglied {$member->firstname} {$member->lastname} bearbeiten"); | ||||||
| 
 | 
 | ||||||
|         return Inertia::render('member/VForm', [ |         return Inertia::render('member/VForm', [ | ||||||
|             'data' => new MemberResource($member->load('bankAccount')), |             'data' => new MemberResource($member), | ||||||
|             'mode' => 'edit', |             'mode' => 'edit', | ||||||
|             'conflict' => '1' === $request->query('conflict', '0'), |             'conflict' => '1' === $request->query('conflict', '0'), | ||||||
|             'meta' => MemberResource::meta(), |             'meta' => MemberResource::meta(), | ||||||
|  |  | ||||||
|  | @ -84,24 +84,16 @@ class MemberRequest extends FormRequest | ||||||
|             'salutation' => '', |             'salutation' => '', | ||||||
|             'comment' => '', |             'comment' => '', | ||||||
|             'keepdata' => 'boolean', |             'keepdata' => 'boolean', | ||||||
|             'bank_account' => 'array', |  | ||||||
|             'bank_account.iban' => 'nullable|string|max:255', |  | ||||||
|             'bank_account.bic' => 'nullable|string|max:255', |  | ||||||
|             'bank_account.blz' => 'nullable|string|max:255', |  | ||||||
|             'bank_account.bank_name' => 'nullable|string|max:255', |  | ||||||
|             'bank_account.person' => 'nullable|string|max:255', |  | ||||||
|             'bank_account.account_number' => 'nullable|string|max:255', |  | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function persistCreate(NamiSettings $settings): void |     public function persistCreate(NamiSettings $settings): void | ||||||
|     { |     { | ||||||
|         $member = new Member([ |         $member = new Member([ | ||||||
|             ...$this->dataToInsert(), |             ...$this->validated(), | ||||||
|             'group_id' => Group::where('nami_id', $settings->default_group_id)->firstOrFail()->id, |             'group_id' => Group::where('nami_id', $settings->default_group_id)->firstOrFail()->id, | ||||||
|         ]); |         ]); | ||||||
|         $member->updatePhoneNumbers()->save(); |         $member->updatePhoneNumbers()->save(); | ||||||
|         $member->bankAccount->update($this->validated('bank_account')); |  | ||||||
| 
 | 
 | ||||||
|         if ($this->input('has_nami')) { |         if ($this->input('has_nami')) { | ||||||
|             $this->storeFreshMemberInNami($member); |             $this->storeFreshMemberInNami($member); | ||||||
|  | @ -120,12 +112,11 @@ class MemberRequest extends FormRequest | ||||||
| 
 | 
 | ||||||
|     public function persistUpdate(Member $member): void |     public function persistUpdate(Member $member): void | ||||||
|     { |     { | ||||||
|         $member->fill($this->dataToInsert())->updatePhoneNumbers(); |         $member->fill($this->validated())->updatePhoneNumbers(); | ||||||
| 
 | 
 | ||||||
|         $namiSync = $member->isDirty(Member::$namiFields); |         $namiSync = $member->isDirty(Member::$namiFields); | ||||||
| 
 | 
 | ||||||
|         $member->save(); |         $member->save(); | ||||||
|         $member->bankAccount->update($this->validated('bank_account')); |  | ||||||
| 
 | 
 | ||||||
|         if ($this->input('has_nami') && null === $member->nami_id) { |         if ($this->input('has_nami') && null === $member->nami_id) { | ||||||
|             $this->storeFreshMemberInNami($member); |             $this->storeFreshMemberInNami($member); | ||||||
|  | @ -168,12 +159,4 @@ class MemberRequest extends FormRequest | ||||||
|         $when = fn () => true === $request->input('has_nami') && ($member === null || !$member->has_nami); |         $when = fn () => true === $request->input('has_nami') && ($member === null || !$member->has_nami); | ||||||
|         $validator->sometimes($attribute, $rules, $when); |         $validator->sometimes($attribute, $rules, $when); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     protected function dataToInsert(): array |  | ||||||
|     { |  | ||||||
|         return $this->safe()->except('bank_account'); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ use App\Invoice\BillKind; | ||||||
| use App\Invoice\Resources\InvoicePositionResource; | use App\Invoice\Resources\InvoicePositionResource; | ||||||
| use App\Lib\HasMeta; | use App\Lib\HasMeta; | ||||||
| use App\Member\Data\NestedGroup; | use App\Member\Data\NestedGroup; | ||||||
| use App\Member\Resources\BankAccountResource; |  | ||||||
| use App\Member\Resources\NationalityResource; | use App\Member\Resources\NationalityResource; | ||||||
| use App\Member\Resources\RegionResource; | use App\Member\Resources\RegionResource; | ||||||
| use App\Membership\MembershipResource; | use App\Membership\MembershipResource; | ||||||
|  | @ -108,7 +107,6 @@ class MemberResource extends JsonResource | ||||||
|             'lon' => $this->lon, |             'lon' => $this->lon, | ||||||
|             'group_name' => $this->group->name, |             'group_name' => $this->group->name, | ||||||
|             'keepdata' => $this->keepdata, |             'keepdata' => $this->keepdata, | ||||||
|             'bank_account' => new BankAccountResource($this->whenLoaded('bankAccount')), |  | ||||||
|             'links' => [ |             'links' => [ | ||||||
|                 'membership_index' => route('member.membership.index', ['member' => $this->getModel()]), |                 'membership_index' => route('member.membership.index', ['member' => $this->getModel()]), | ||||||
|                 'invoiceposition_index' => route('member.invoice-position.index', ['member' => $this->getModel()]), |                 'invoiceposition_index' => route('member.invoice-position.index', ['member' => $this->getModel()]), | ||||||
|  | @ -133,11 +131,11 @@ class MemberResource extends JsonResource | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $activities = Activity::with('subactivities')->get(); |         $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 [ |         return [ | ||||||
|             'filterActivities' => Activity::where('is_filterable', true)->get()->map(fn($a) => ['id' => $a->id, 'name' => $a->name]), |             'filterActivities' => Activity::where('is_filterable', true)->pluck('name', 'id'), | ||||||
|             'filterSubactivities' => Subactivity::where('is_filterable', true)->get()->map(fn($a) => ['id' => $a->id, 'name' => $a->name]), |             'filterSubactivities' => Subactivity::where('is_filterable', true)->pluck('name', 'id'), | ||||||
|             'formActivities' => $activities->pluck('name', 'id'), |             'formActivities' => $activities->pluck('name', 'id'), | ||||||
|             'formSubactivities' => $activities->map(function (Activity $activity) { |             'formSubactivities' => $activities->map(function (Activity $activity) { | ||||||
|                 return ['subactivities' => $activity->subactivities->pluck('name', 'id'), 'id' => $activity->id]; |                 return ['subactivities' => $activity->subactivities->pluck('name', 'id'), 'id' => $activity->id]; | ||||||
|  | @ -155,7 +153,7 @@ class MemberResource extends JsonResource | ||||||
|             'genders' => Gender::pluck('name', 'id'), |             'genders' => Gender::pluck('name', 'id'), | ||||||
|             'billKinds' => BillKind::forSelect(), |             'billKinds' => BillKind::forSelect(), | ||||||
|             'nationalities' => Nationality::pluck('name', 'id'), |             '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' => [ |             'links' => [ | ||||||
|                 'index' => route('member.index'), |                 'index' => route('member.index'), | ||||||
|                 'create' => route('member.create'), |                 'create' => route('member.create'), | ||||||
|  | @ -210,14 +208,6 @@ class MemberResource extends JsonResource | ||||||
|                 'multiply_pv' => false, |                 'multiply_pv' => false, | ||||||
|                 'multiply_more_pv' => false, |                 'multiply_more_pv' => false, | ||||||
|                 'keepdata' => false, |                 'keepdata' => false, | ||||||
|                 'bank_account' => [ |  | ||||||
|                     'iban' => '', |  | ||||||
|                     'bic' => '', |  | ||||||
|                     'blz' => '', |  | ||||||
|                     'bank_name' => '', |  | ||||||
|                     'person' => '', |  | ||||||
|                     'account_number' => '', |  | ||||||
|                 ] |  | ||||||
|             ] |             ] | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -79,19 +79,6 @@ class Membership extends Model | ||||||
|             ->where(fn ($query) => $query->whereNull('to')->orWhere('to', '>=', now())); |             ->where(fn ($query) => $query->whereNull('to')->orWhere('to', '>=', now())); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * @param Builder<Membership> $query |  | ||||||
|      * |  | ||||||
|      * @return Builder<Membership> |  | ||||||
|      */ |  | ||||||
|     public function scopeInactive(Builder $query): Builder |  | ||||||
|     { |  | ||||||
|         return $query->where(fn ($q) => $q |  | ||||||
|             ->orWhere('from', '>=', now()) |  | ||||||
|             ->orWhere('to', '<=', now()) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * @param Builder<Membership> $query |      * @param Builder<Membership> $query | ||||||
|      * |      * | ||||||
|  |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Member\Resources; |  | ||||||
| 
 |  | ||||||
| use App\Member\BankAccount; |  | ||||||
| use Illuminate\Http\Resources\Json\JsonResource; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @mixin BankAccount |  | ||||||
|  */ |  | ||||||
| class BankAccountResource extends JsonResource |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * Transform the resource into an array. |  | ||||||
|      * |  | ||||||
|      * @param \Illuminate\Http\Request $request |  | ||||||
|      * |  | ||||||
|      * @return array<string, int|string> |  | ||||||
|      */ |  | ||||||
|     public function toArray($request) |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'iban' => $this->iban, |  | ||||||
|             'bic' => $this->bic, |  | ||||||
|             'blz' => $this->blz, |  | ||||||
|             'bank_name' => $this->bank_name, |  | ||||||
|             'person' => $this->person, |  | ||||||
|             'account_number' => $this->account_number, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Collection; | ||||||
| use Illuminate\Http\Resources\Json\AnonymousResourceCollection; | use Illuminate\Http\Resources\Json\AnonymousResourceCollection; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
| 
 | 
 | ||||||
| class MemberIndexAction | class IndexAction | ||||||
| { | { | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|  | @ -10,7 +10,6 @@ use App\Maildispatcher\Actions\ResyncAction; | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
| use App\Member\Membership; | use App\Member\Membership; | ||||||
| use App\Subactivity; | use App\Subactivity; | ||||||
| use Illuminate\Http\JsonResponse; |  | ||||||
| use Illuminate\Support\Facades\DB; | use Illuminate\Support\Facades\DB; | ||||||
| use Lorisleiva\Actions\ActionRequest; | use Lorisleiva\Actions\ActionRequest; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
|  | @ -21,18 +20,13 @@ class MassStoreAction | ||||||
|     use TracksJob; |     use TracksJob; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return array<string, mixed> |      * @return array<string, string> | ||||||
|      */ |      */ | ||||||
|     public function rules(): array |     public function rules(): array | ||||||
|     { |     { | ||||||
|         return [ |         return [ | ||||||
|             'group_id' => 'required|numeric|exists:groups,id', |             'group_id' => 'required|numeric|exists:groups,id', | ||||||
|             'activity_id' => ['required', 'numeric', 'exists:activities,id', function($key, $value, $fail) { |             'activity_id' => 'required|numeric|exists:activities,id', | ||||||
|                 $activity = Activity::findOrFail($value); |  | ||||||
|                 if ($activity->subactivities->pluck('id')->doesntContain(request()->subactivity_id)) { |  | ||||||
|                     return $fail(':attribute ist nicht vorhanden.'); |  | ||||||
|                 } |  | ||||||
|             }], |  | ||||||
|             'subactivity_id' => 'required|numeric|exists:subactivities,id', |             'subactivity_id' => 'required|numeric|exists:subactivities,id', | ||||||
|             'members' => 'array', |             'members' => 'array', | ||||||
|             'members.*' => 'numeric|exists:members,id', |             'members.*' => 'numeric|exists:members,id', | ||||||
|  | @ -54,7 +48,9 @@ class MassStoreAction | ||||||
|             Membership::where($attributes)->active()->whereNotIn('member_id', $members)->get() |             Membership::where($attributes)->active()->whereNotIn('member_id', $members)->get() | ||||||
|                 ->each(fn ($membership) => MembershipDestroyAction::run($membership->id)); |                 ->each(fn ($membership) => MembershipDestroyAction::run($membership->id)); | ||||||
| 
 | 
 | ||||||
|             Member::whereIn('id', $members)->whereDoesntHave('memberships', fn ($q) => $q->where($attributes))->get() |             collect($members) | ||||||
|  |                 ->except(Membership::where($attributes)->active()->pluck('member_id')) | ||||||
|  |                 ->map(fn ($memberId) => Member::findOrFail($memberId)) | ||||||
|                 ->each(fn ($member) => MembershipStoreAction::run( |                 ->each(fn ($member) => MembershipStoreAction::run( | ||||||
|                     $member, |                     $member, | ||||||
|                     $activity, |                     $activity, | ||||||
|  | @ -63,11 +59,12 @@ class MassStoreAction | ||||||
|                     null, |                     null, | ||||||
|                 )); |                 )); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|             ResyncAction::dispatch(); |             ResyncAction::dispatch(); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function asController(ActionRequest $request): JsonResponse |     public function asController(ActionRequest $request): void | ||||||
|     { |     { | ||||||
|         /** |         /** | ||||||
|          * @var array{members: array<int, int>, group_id: int, activity_id: int, subactivity_id: int} |          * @var array{members: array<int, int>, group_id: int, activity_id: int, subactivity_id: int} | ||||||
|  | @ -80,19 +77,6 @@ class MassStoreAction | ||||||
|             Subactivity::findOrFail($input['subactivity_id']), |             Subactivity::findOrFail($input['subactivity_id']), | ||||||
|             $input['members'], |             $input['members'], | ||||||
|         ); |         ); | ||||||
| 
 |  | ||||||
|         return response()->json([], 200); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, string> |  | ||||||
|      */ |  | ||||||
|     public function getValidationAttributes(): array { |  | ||||||
|         return [ |  | ||||||
|             'activity_id' => 'Tätigkeit', |  | ||||||
|             'subactivity_id' => 'Untertätigkeit', |  | ||||||
|             'group_id' => 'Gruppe', |  | ||||||
|         ]; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Membership\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Member\Data\MembershipData; |  | ||||||
| use App\Membership\FilterScope; |  | ||||||
| use Inertia\Inertia; |  | ||||||
| use Inertia\Response; |  | ||||||
| use Lorisleiva\Actions\ActionRequest; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class MembershipIndexAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     public function asController(ActionRequest $request): Response |  | ||||||
|     { |  | ||||||
|         return Inertia::render( |  | ||||||
|             'membership/Index', |  | ||||||
|             ['data' => MembershipData::collectPages(FilterScope::fromRequest($request->input('filter', ''))->getQuery()->paginate(20))] |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,52 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Membership; |  | ||||||
| 
 |  | ||||||
| use App\Lib\Filter; |  | ||||||
| use App\Member\Membership; |  | ||||||
| use Illuminate\Database\Eloquent\Builder; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @extends Filter<Membership> |  | ||||||
|  */ |  | ||||||
| class FilterScope extends Filter |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @param array<int, int> $activities |  | ||||||
|      * @param array<int, int> $subactivities |  | ||||||
|      * @param array<int, int> $groups |  | ||||||
|      */ |  | ||||||
|     public function __construct( |  | ||||||
|         public array $activities = [], |  | ||||||
|         public array $subactivities = [], |  | ||||||
|         public array $groups = [], |  | ||||||
|         public ?bool $active = true, |  | ||||||
|     ) {} |  | ||||||
| 
 |  | ||||||
|     public function getQuery(): Builder |  | ||||||
|     { |  | ||||||
|         $query = Membership::orderByRaw('member_id, activity_id, subactivity_id'); |  | ||||||
| 
 |  | ||||||
|         if ($this->active === true) { |  | ||||||
|             $query = $query->active(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($this->active === false) { |  | ||||||
|             $query = $query->inactive(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (count($this->groups)) { |  | ||||||
|             $query = $query->whereIn('group_id', $this->groups); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (count($this->activities)) { |  | ||||||
|             $query = $query->whereIn('activity_id', $this->activities); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (count($this->subactivities)) { |  | ||||||
|             $query = $query->whereIn('subactivity_id', $this->subactivities); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $query; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,43 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Membership; |  | ||||||
| 
 |  | ||||||
| use App\Dashboard\Blocks\Block; |  | ||||||
| use App\Member\Member; |  | ||||||
| use Illuminate\Database\Eloquent\Builder; |  | ||||||
| 
 |  | ||||||
| class TestersBlock extends Block |  | ||||||
| { |  | ||||||
|     /** |  | ||||||
|      * @return Builder<Member> |  | ||||||
|      */ |  | ||||||
|     public function query(): Builder |  | ||||||
|     { |  | ||||||
|         return Member::whereHas('memberships', fn ($q) => $q->isTrying()) |  | ||||||
|             ->with('memberships', fn ($q) => $q->isTrying()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array{members: array<int, array{name: string, try_ends_at: string, try_ends_at_human: string}>} |  | ||||||
|      */ |  | ||||||
|     public function data(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'members' => $this->query()->get()->map(fn ($member) => [ |  | ||||||
|                 'name' => $member->fullname, |  | ||||||
|                 'try_ends_at' => $member->memberships->first()->from->addWeeks(8)->format('d.m.Y'), |  | ||||||
|                 'try_ends_at_human' => $member->memberships->first()->from->addWeeks(8)->diffForHumans(), |  | ||||||
|             ])->toArray(), |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function component(): string |  | ||||||
|     { |  | ||||||
|         return 'testers'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function title(): string |  | ||||||
|     { |  | ||||||
|         return 'Endende Schhnupperzeiten'; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Prevention\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Prevention\Enums\Prevention; |  | ||||||
| use App\Prevention\PreventionSettings; |  | ||||||
| use Illuminate\Http\JsonResponse; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class SettingApiAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     public function handle(): JsonResponse |  | ||||||
|     { |  | ||||||
|         return response()->json([ |  | ||||||
|             'data' => app(PreventionSettings::class)->toFrontend(), |  | ||||||
|             'meta' => [ |  | ||||||
|                 'preventAgainsts' => Prevention::values(), |  | ||||||
|             ] |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,44 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| 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; |  | ||||||
| 
 |  | ||||||
| class SettingStoreAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<string, string> |  | ||||||
|      */ |  | ||||||
|     public function rules(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             'formmail' => 'array', |  | ||||||
|             'yearlymail' => 'array', |  | ||||||
|             'weeks' => 'required|numeric|gte:0', |  | ||||||
|             'freshRememberInterval' => 'required|numeric|gte:0', |  | ||||||
|             'active' => 'boolean', |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function handle(ActionRequest $request): void |  | ||||||
|     { |  | ||||||
|         $settings = app(PreventionSettings::class); |  | ||||||
|         $settings->formmail = EditorData::from($request->formmail); |  | ||||||
|         $settings->yearlymail = EditorData::from($request->yearlymail); |  | ||||||
|         $settings->weeks = $request->weeks; |  | ||||||
|         $settings->freshRememberInterval = $request->freshRememberInterval; |  | ||||||
|         $settings->active = $request->active; |  | ||||||
|         $settings->yearlyMemberFilter = FilterScope::from($request->yearlyMemberFilter); |  | ||||||
|         $settings->preventAgainst = $request->preventAgainst; |  | ||||||
|         $settings->save(); |  | ||||||
| 
 |  | ||||||
|         Succeeded::message('Einstellungen gespeichert.')->dispatch(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,79 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Prevention\Actions; |  | ||||||
| 
 |  | ||||||
| use App\Member\Member; |  | ||||||
| use App\Prevention\Data\PreventionData; |  | ||||||
| use App\Prevention\Mails\YearlyMail; |  | ||||||
| use App\Prevention\PreventionSettings; |  | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| use Illuminate\Support\Facades\Cache; |  | ||||||
| use Illuminate\Support\Facades\Mail; |  | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; |  | ||||||
| 
 |  | ||||||
| class YearlyRememberAction |  | ||||||
| { |  | ||||||
|     use AsAction; |  | ||||||
| 
 |  | ||||||
|     public string $commandSignature = 'prevention:remember-yearly'; |  | ||||||
| 
 |  | ||||||
|     public function handle(): void |  | ||||||
|     { |  | ||||||
|         $settings = app(PreventionSettings::class); |  | ||||||
|         $expireDate = now()->addWeeks($settings->weeks); |  | ||||||
| 
 |  | ||||||
|         if (!$settings->active) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         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)) |  | ||||||
|                 ->filter(fn($p) => $p->appliesToSettings($settings)); |  | ||||||
| 
 |  | ||||||
|             if ($noticePreventions->count() === 0) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Mail::send($this->createMail($member, $noticePreventions)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         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())) |  | ||||||
|                 ->filter(fn($p) => $p->appliesToSettings($settings)); |  | ||||||
| 
 |  | ||||||
|             if ($preventions->count() === 0) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Cache::remember( |  | ||||||
|                 'prevention-' . $member->id, |  | ||||||
|                 (int) now()->diffInSeconds(now()->addWeeks($settings->freshRememberInterval)), |  | ||||||
|                 function () use ($member, $preventions) { |  | ||||||
|                     Mail::send($this->createMail($member, $preventions)); |  | ||||||
|                     return 0; |  | ||||||
|                 } |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param Collection<int, PreventionData> $preventions |  | ||||||
|      */ |  | ||||||
|     protected function createMail(Member $member, Collection $preventions): YearlyMail |  | ||||||
|     { |  | ||||||
|         $body = app(PreventionSettings::class)->refresh()->yearlymail; |  | ||||||
|         return new YearlyMail($member, $body, $preventions); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -2,19 +2,19 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Prevention\Contracts; | namespace App\Prevention\Contracts; | ||||||
| 
 | 
 | ||||||
| use App\Prevention\Data\PreventionData; | use App\Prevention\Enums\Prevention; | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| use stdClass; | use stdClass; | ||||||
| 
 | 
 | ||||||
| interface Preventable | interface Preventable | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|  |     public function preventableLayout(): string; | ||||||
|     public function preventableSubject(): string; |     public function preventableSubject(): string; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return Collection<int, PreventionData> |      * @return array<int, Prevention> | ||||||
|      */ |      */ | ||||||
|     public function preventions(): Collection; |     public function preventions(): array; | ||||||
| 
 | 
 | ||||||
|     public function getMailRecipient(): ?stdClass; |     public function getMailRecipient(): stdClass; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Prevention\Data; |  | ||||||
| 
 |  | ||||||
| use App\Prevention\Enums\Prevention; |  | ||||||
| use App\Prevention\PreventionSettings; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| use Spatie\LaravelData\Data; |  | ||||||
| 
 |  | ||||||
| class PreventionData extends Data |  | ||||||
| { |  | ||||||
|     public function __construct(public Prevention $type, public Carbon $expires) {} |  | ||||||
| 
 |  | ||||||
|     public function expiresAt(Carbon $date): bool |  | ||||||
|     { |  | ||||||
|         return $this->expires->isSameDay($date); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function text(): string |  | ||||||
|     { |  | ||||||
|         return str($this->type->text())->when( |  | ||||||
|             !$this->expiresAt(now()), |  | ||||||
|             fn($str) => $str->append(' (fällig am ' . $this->expires->format('d.m.Y') . ')') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function appliesToSettings(PreventionSettings $settings): bool |  | ||||||
|     { |  | ||||||
|         return in_array($this->type->name, $settings->preventAgainst); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -2,7 +2,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Prevention\Enums; | namespace App\Prevention\Enums; | ||||||
| 
 | 
 | ||||||
| use App\Prevention\Data\PreventionData; | use App\Member\Member; | ||||||
|  | use Carbon\Carbon; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| 
 | 
 | ||||||
| enum Prevention | enum Prevention | ||||||
|  | @ -38,26 +39,15 @@ enum Prevention | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param Collection<int, PreventionData> $preventions |      * @param array<int, self> $preventions | ||||||
|      * @return Collection<int, array{letter: string, value: bool, tooltip: string}> |      * @return Collection<int, array{letter: string, value: bool, tooltip: string}> | ||||||
|      */ |      */ | ||||||
|     public static function items(Collection $preventions): Collection |     public static function items(array $preventions): Collection | ||||||
|     { |     { | ||||||
|         return collect(static::cases())->map(fn($case) => [ |         return collect(static::cases())->map(fn ($case) => [ | ||||||
|             'letter' => $case->letter(), |             'letter' => $case->letter(), | ||||||
|             'value' => $preventions->pluck('type')->doesntContain($case), |             'value' => !in_array($case, $preventions), | ||||||
|             'tooltip' => $case->tooltip($preventions->pluck('type')->doesntContain($case)), |             'tooltip' => $case->tooltip(!in_array($case, $preventions)), | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<int, string> |  | ||||||
|      */ |  | ||||||
|     public static function values(): array |  | ||||||
|     { |  | ||||||
|         return collect(static::cases())->map(fn($case) => [ |  | ||||||
|             'id' => $case->name, |  | ||||||
|             'name' => $case->text(), |  | ||||||
|         ])->toArray(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,17 +2,15 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Prevention\Mails; | namespace App\Prevention\Mails; | ||||||
| 
 | 
 | ||||||
| use App\Invoice\InvoiceSettings; |  | ||||||
| use App\Lib\Editor\EditorData; | use App\Lib\Editor\EditorData; | ||||||
| use App\Prevention\Contracts\Preventable; | use App\Prevention\Contracts\Preventable; | ||||||
| use App\Prevention\Data\PreventionData; |  | ||||||
| use Illuminate\Bus\Queueable; | use Illuminate\Bus\Queueable; | ||||||
| use Illuminate\Mail\Attachment; | use Illuminate\Mail\Attachment; | ||||||
| use Illuminate\Mail\Mailable; | use Illuminate\Mail\Mailable; | ||||||
| use Illuminate\Mail\Mailables\Content; | use Illuminate\Mail\Mailables\Content; | ||||||
| use Illuminate\Mail\Mailables\Envelope; | use Illuminate\Mail\Mailables\Envelope; | ||||||
| use Illuminate\Queue\SerializesModels; | use Illuminate\Queue\SerializesModels; | ||||||
| use Illuminate\Support\Collection; | use Modules\Invoice\InvoiceSettings; | ||||||
| 
 | 
 | ||||||
| class PreventionRememberMail extends Mailable | class PreventionRememberMail extends Mailable | ||||||
| { | { | ||||||
|  | @ -22,13 +20,12 @@ class PreventionRememberMail extends Mailable | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Create a new message instance. |      * Create a new message instance. | ||||||
|      * @param Collection<int, PreventionData> $preventions |  | ||||||
|      */ |      */ | ||||||
|     public function __construct(public Preventable $preventable, public EditorData $bodyText, public Collection $preventions) |     public function __construct(public Preventable $preventable, public EditorData $bodyText) | ||||||
|     { |     { | ||||||
|         $this->settings = app(InvoiceSettings::class); |         $this->settings = app(InvoiceSettings::class); | ||||||
|         $this->bodyText = $this->bodyText |         $this->bodyText = $this->bodyText | ||||||
|             ->replaceWithList('wanted', $preventions->map(fn($prevention) => $prevention->text())->toArray()); |             ->replaceWithList('wanted', collect($preventable->preventions())->map(fn ($prevention) => $prevention->text())->toArray()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -51,7 +48,7 @@ class PreventionRememberMail extends Mailable | ||||||
|     public function content() |     public function content() | ||||||
|     { |     { | ||||||
|         return new Content( |         return new Content( | ||||||
|             markdown: 'mail.prevention.prevention-remember-participant', |             markdown: $this->preventable->preventableLayout(), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,67 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Prevention\Mails; |  | ||||||
| 
 |  | ||||||
| use App\Invoice\InvoiceSettings; |  | ||||||
| use App\Lib\Editor\EditorData; |  | ||||||
| use App\Prevention\Contracts\Preventable; |  | ||||||
| use App\Prevention\Data\PreventionData; |  | ||||||
| use Illuminate\Bus\Queueable; |  | ||||||
| use Illuminate\Mail\Attachment; |  | ||||||
| use Illuminate\Mail\Mailable; |  | ||||||
| use Illuminate\Mail\Mailables\Content; |  | ||||||
| use Illuminate\Mail\Mailables\Envelope; |  | ||||||
| use Illuminate\Queue\SerializesModels; |  | ||||||
| use Illuminate\Support\Collection; |  | ||||||
| 
 |  | ||||||
| class YearlyMail extends Mailable |  | ||||||
| { |  | ||||||
|     use Queueable, SerializesModels; |  | ||||||
| 
 |  | ||||||
|     public InvoiceSettings $settings; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new message instance. |  | ||||||
|      * @param Collection<int, PreventionData> $preventions |  | ||||||
|      */ |  | ||||||
|     public function __construct(public Preventable $preventable, public EditorData $bodyText, public Collection $preventions) |  | ||||||
|     { |  | ||||||
|         $this->settings = app(InvoiceSettings::class); |  | ||||||
|         $this->bodyText = $this->bodyText |  | ||||||
|             ->replaceWithList('wanted', $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: 'mail.prevention.prevention-remember-participant', |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the attachments for the message. |  | ||||||
|      * |  | ||||||
|      * @return array<int, Attachment> |  | ||||||
|      */ |  | ||||||
|     public function attachments(): array |  | ||||||
|     { |  | ||||||
|         return []; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -3,23 +3,12 @@ | ||||||
| namespace App\Prevention; | namespace App\Prevention; | ||||||
| 
 | 
 | ||||||
| use App\Lib\Editor\EditorData; | use App\Lib\Editor\EditorData; | ||||||
| use App\Member\FilterScope; |  | ||||||
| use App\Setting\LocalSettings; | use App\Setting\LocalSettings; | ||||||
| 
 | 
 | ||||||
| class PreventionSettings extends LocalSettings | class PreventionSettings extends LocalSettings | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     public EditorData $formmail; |     public EditorData $formmail; | ||||||
|     public EditorData $yearlymail; |  | ||||||
|     public int $weeks; |  | ||||||
|     public int $freshRememberInterval; |  | ||||||
|     public bool $active; |  | ||||||
|     public FilterScope $yearlyMemberFilter; |  | ||||||
|     /** |  | ||||||
|      * @var array<int, string> |  | ||||||
|      * @todo Create collection cast to Collection of enums |  | ||||||
|      */ |  | ||||||
|     public array $preventAgainst; |  | ||||||
| 
 | 
 | ||||||
|     public static function group(): string |     public static function group(): string | ||||||
|     { |     { | ||||||
|  | @ -38,17 +27,4 @@ class PreventionSettings extends LocalSettings | ||||||
|     { |     { | ||||||
|         return []; |         return []; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @todo return int value here and handle this in vue with a number field that only expects integers |  | ||||||
|      * @return array<string, mixed> |  | ||||||
|      */ |  | ||||||
|     public function toFrontend(): array |  | ||||||
|     { |  | ||||||
|         return [ |  | ||||||
|             ...$this->toArray(), |  | ||||||
|             'weeks' => (string) $this->weeks, |  | ||||||
|             'freshRememberInterval' => (string) $this->freshRememberInterval, |  | ||||||
|         ]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,13 +3,10 @@ | ||||||
| namespace App\Providers; | namespace App\Providers; | ||||||
| 
 | 
 | ||||||
| use App\Form\Models\Form; | use App\Form\Models\Form; | ||||||
| use App\Mailgateway\Types\LocalType; |  | ||||||
| use App\Mailgateway\Types\MailmanType; |  | ||||||
| use Illuminate\Http\RedirectResponse; | use Illuminate\Http\RedirectResponse; | ||||||
| use Illuminate\Http\Resources\Json\JsonResource; | use Illuminate\Http\Resources\Json\JsonResource; | ||||||
| use Illuminate\Support\Facades\Blade; | use Illuminate\Support\Facades\Blade; | ||||||
| use Illuminate\Support\ServiceProvider; | use Illuminate\Support\ServiceProvider; | ||||||
| use Laravel\Telescope\Telescope; |  | ||||||
| 
 | 
 | ||||||
| class AppServiceProvider extends ServiceProvider | class AppServiceProvider extends ServiceProvider | ||||||
| { | { | ||||||
|  | @ -30,11 +27,6 @@ class AppServiceProvider extends ServiceProvider | ||||||
|             return $this; |             return $this; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         app()->bind('mail-gateways', fn () => collect([ |  | ||||||
|             LocalType::class, |  | ||||||
|             MailmanType::class, |  | ||||||
|         ])); |  | ||||||
| 
 |  | ||||||
|         app()->extend('media-library-helpers', fn ($p) => $p->put('form', Form::class)); |         app()->extend('media-library-helpers', fn ($p) => $p->put('form', Form::class)); | ||||||
| 
 | 
 | ||||||
|         Blade::componentNamespace('App\\View\\Mail', 'mail-view'); |         Blade::componentNamespace('App\\View\\Mail', 'mail-view'); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Providers; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Support\ServiceProvider; | ||||||
|  | use Illuminate\Support\Facades\Blade; | ||||||
|  | use Illuminate\View\ComponentAttributeBag; | ||||||
|  | use Livewire\Livewire; | ||||||
|  | 
 | ||||||
|  | class BaseServiceProvider extends ServiceProvider | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Register services. | ||||||
|  |      */ | ||||||
|  |     public function register(): void | ||||||
|  |     { | ||||||
|  |         Blade::componentNamespace('App\\View\\Ui', 'ui'); | ||||||
|  |         Blade::componentNamespace('App\\View\\Page', 'page'); | ||||||
|  |         Blade::componentNamespace('App\\View\\Form', 'form'); | ||||||
|  | 
 | ||||||
|  |         ComponentAttributeBag::macro('mergeWhen', function ($condition, $key, $attributes) { | ||||||
|  |             /** @var ComponentAttributeBag */ | ||||||
|  |             $self = $this; | ||||||
|  |             return $condition ? $self->merge([$key => $attributes]) : $self; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         Livewire::resolveMissingComponent(function ($name) { | ||||||
|  |             'modules.dashboard.components.dashboard-component'; | ||||||
|  |             if (str($name)->startsWith('modules.')) { | ||||||
|  |                 return str($name)->explode('.')->map(fn ($name) => str($name)->studly()->toString())->implode('\\'); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return null; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Bootstrap services. | ||||||
|  |      */ | ||||||
|  |     public function boot(): void | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -14,7 +14,7 @@ class SearchAction | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return LengthAwarePaginator<int, array<string, mixed>> |      * @return LengthAwarePaginator<array<string, mixed>> | ||||||
|      */ |      */ | ||||||
|     public function handle(ActionRequest $request): LengthAwarePaginator |     public function handle(ActionRequest $request): LengthAwarePaginator | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ use Illuminate\Http\RedirectResponse; | ||||||
| use Lorisleiva\Actions\ActionRequest; | use Lorisleiva\Actions\ActionRequest; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
| 
 | 
 | ||||||
|  | /** @deprecated */ | ||||||
| class StoreAction | class StoreAction | ||||||
| { | { | ||||||
|     use AsAction; |     use AsAction; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ use Inertia\Inertia; | ||||||
| use Inertia\Response; | use Inertia\Response; | ||||||
| use Lorisleiva\Actions\Concerns\AsAction; | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
| 
 | 
 | ||||||
|  | /** @deprecated */ | ||||||
| class ViewAction | class ViewAction | ||||||
| { | { | ||||||
|     use AsAction; |     use AsAction; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,36 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Setting\Data; | ||||||
|  | 
 | ||||||
|  | use Livewire\Mechanisms\HandleComponents\Synthesizers\Synth; | ||||||
|  | use Spatie\LaravelSettings\Settings; | ||||||
|  | 
 | ||||||
|  | class SettingSynthesizer extends Synth | ||||||
|  | { | ||||||
|  |     public static $key = 'setting-class'; | ||||||
|  | 
 | ||||||
|  |     public static function match($target) | ||||||
|  |     { | ||||||
|  |         return $target instanceof Settings; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function dehydrate($target) | ||||||
|  |     { | ||||||
|  |         return [$target->toArray(), ['setting_class' => get_class($target)]]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function hydrate($value, $meta) | ||||||
|  |     { | ||||||
|  |         return app($meta['setting_class'])->fill($value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function get(&$target, $key) | ||||||
|  |     { | ||||||
|  |         return $target->{$key}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function set(&$target, $key, $value) | ||||||
|  |     { | ||||||
|  |         $target->{$key} = $value; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -10,7 +10,7 @@ abstract class LocalSettings extends Settings | ||||||
| 
 | 
 | ||||||
|     public function url(): string |     public function url(): string | ||||||
|     { |     { | ||||||
|         return route('setting.view', ['settingGroup' => $this->group()]); |         return url('setting/' . $this->group()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue