Compare commits
146 Commits
Author | SHA1 | Date |
---|---|---|
|
37c9021e76 | |
|
2e0b42bfbd | |
|
3c7cd1173a | |
|
0c14ebb229 | |
|
0679a09d26 | |
|
2dd0c3d2a0 | |
|
21964e9665 | |
|
5afe8613e1 | |
|
c047f8da63 | |
|
9097f0bbd9 | |
|
3f44fbe5b0 | |
|
8e1f7869e2 | |
|
abf4158256 | |
|
f2d59e4535 | |
|
a75b4a7808 | |
|
ecf3cc998c | |
|
7202cd60f5 | |
|
9d5c71adcd | |
|
eb00d928fc | |
|
6683990755 | |
|
2a5f4832e1 | |
|
719b1920c9 | |
|
3260202465 | |
|
9b995641b6 | |
|
2d5921fe1d | |
|
efa9eeccbe | |
|
b33072ad20 | |
|
2e77823c3a | |
|
9a5d9ae30e | |
|
d607c8beac | |
|
1a86ea7ea5 | |
|
a157f2b656 | |
|
e3c7dad51e | |
|
749e3dcd7c | |
|
4417008d22 | |
|
b38591b87c | |
|
17e4fe5f82 | |
|
b2f3e4f1fd | |
|
376c1ceb9b | |
|
056b8f9ed6 | |
|
011e414848 | |
|
5e64797277 | |
|
2e8dc96665 | |
|
b1dcdeb579 | |
|
a456cc1889 | |
|
784b49c3dd | |
|
c3a0381a0a | |
|
101ac7402a | |
|
2c38488962 | |
|
8a26276e28 | |
|
4651a4cb96 | |
|
688e10e230 | |
|
267da38c6b | |
|
499c61afff | |
|
02531938b3 | |
|
1c53e81f1a | |
|
98712093b2 | |
|
a32ef33113 | |
|
088ea4f0a2 | |
|
4ac89da124 | |
|
286ceea0d4 | |
|
52ac5937a4 | |
|
4690e86bbf | |
|
1a4a7c3652 | |
|
b05c5025c0 | |
|
acf7b94094 | |
|
57b1efb065 | |
|
3e563f8390 | |
|
0a5590d533 | |
|
10deaf15a1 | |
|
ae447a7a60 | |
|
baea21807a | |
|
58426c5537 | |
|
84cd13c085 | |
|
b8d2b37057 | |
|
44eb3719b9 | |
|
c65a208e34 | |
|
95a8d4d689 | |
|
e1e8669beb | |
|
a3ecfa756d | |
|
70e085a49e | |
|
1178b011e0 | |
|
6a91e857d2 | |
|
82baf67a73 | |
|
36466420f6 | |
|
54c37fccd1 | |
|
d03f036a2b | |
|
75d11f9860 | |
|
da4d553ea6 | |
|
5bad88c5f4 | |
|
75dc0d9769 | |
|
9fc8548504 | |
|
c990ce3171 | |
|
e7f63d19e5 | |
|
f549b31862 | |
|
1f4efb22d3 | |
|
3e725d9842 | |
|
16d3bc51a3 | |
|
0467f24ff6 | |
|
ed1a8f30bc | |
|
e5066af192 | |
|
31afa2e418 | |
|
a897690e7a | |
|
24ed5d4ab9 | |
|
e807aff3e6 | |
|
096224fe98 | |
|
646ce647da | |
|
f858716e57 | |
|
1521aba1f8 | |
|
664b88564d | |
|
d30a2336cf | |
|
a5f1c90003 | |
|
c3908b714b | |
|
2f1e02151f | |
|
eaf014b7c7 | |
|
94d34d1afa | |
|
9b700be889 | |
|
e1ec6ad9df | |
|
aaa236fcd5 | |
|
4d74e53fc2 | |
|
b8d389bdb9 | |
|
3a4c99f154 | |
|
fca7fc463e | |
|
767df11e4f | |
|
295d85f4f8 | |
|
42ccd32740 | |
|
bbcf2fd768 | |
|
b335c20777 | |
|
c63c8316b5 | |
|
2e6c27b8f1 | |
|
1cf8d34b78 | |
|
e3b52bf901 | |
|
76e7b06630 | |
|
21def11ae9 | |
|
565b1ffbce | |
|
ef70203c8a | |
|
b3a29c706c | |
|
298140e387 | |
|
151477dbdd | |
|
a01e8adf8a | |
|
547c9edfd5 | |
|
6e09e2ec0d | |
|
9d37ebefc5 | |
|
11b1e6a198 | |
|
5008244de2 | |
|
8b3b3de9a8 |
.docker
.drone.yml.gitignoreCHANGELOG.mdEnvoy.blade.phpREADME.mdapp
Actions
Activity.phpConfession.phpContribution
ContributionFactory.php
Country.phpData
Documents
BdkjHesse.phpCityFrankfurtMainDocument.phpCityRemscheidDocument.phpCitySolingenDocument.phpContributionDocument.phpRdpNrwDocument.phpWuppertalDocument.php
Traits
Course/Models
Dashboard
Dav
Efz
Fee.phpFileshare
Form
Actions
FormUpdateMetaAction.phpParticipantIndexAction.phpPreventionRememberAction.phpRegisterAction.phpUpdateParticipantSearchIndexAction.php
Casts
Contracts
Data
Enums
Fields
Models
Resources
Scopes
Transformers
Group/Actions
Http/Middleware
Invoice
Lib
Maildispatcher
Mailgateway
Mailman/Data
Member
Actions
BankAccount.phpData
FilterScope.phpMember.phpMemberController.phpMemberRequest.phpMemberResource.phpMembership.phpPsPendingBlock.phpPayment
Providers
Region.phpSubactivity.phpUser.phpbin
composer.jsoncomposer.lock
|
@ -1,11 +1,12 @@
|
|||
FROM php:8.1.6-fpm as php
|
||||
FROM php:8.3.11-fpm AS php
|
||||
WORKDIR /app
|
||||
RUN ls /app
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y rsync libcurl3-dev apt-utils zlib1g-dev libpng-dev libicu-dev libonig-dev unzip poppler-utils libpng-dev libjpeg-dev default-mysql-client libzip-dev
|
||||
RUN apt-get install -y rsync libcurl3-dev apt-utils zlib1g-dev libpng-dev libicu-dev libonig-dev unzip poppler-utils libpng-dev libjpeg-dev default-mysql-client libzip-dev imagemagick libmagickwand-dev
|
||||
RUN apt-get install -y --no-install-recommends texlive-base texlive-latex-base texlive-pictures texlive-latex-extra texlive-lang-german texlive-plain-generic texlive-fonts-recommended texlive-fonts-extra texlive-extra-utils
|
||||
RUN docker-php-ext-install pdo_mysql curl exif intl mbstring pcntl zip
|
||||
RUN pecl install redis && docker-php-ext-enable redis
|
||||
RUN pecl install imagick && docker-php-ext-enable imagick
|
||||
RUN docker-php-ext-configure gd --with-jpeg
|
||||
RUN docker-php-ext-install gd
|
||||
RUN usermod -s /bin/bash www-data
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker buildx build -f .docker/base.Dockerfile .
|
||||
docker image tag sha256:... zoomyboy/adrema-base
|
||||
docker push zoomyboy/adrema-base
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
FROM composer:2.2.7 as composer
|
||||
FROM composer:2.7.9 AS composer
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN composer install --ignore-platform-reqs --no-dev
|
||||
RUN php artisan telescope:publish
|
||||
RUN php artisan horizon:publish
|
||||
|
||||
FROM node:20.15.0-slim as node
|
||||
FROM node:20.15.0-slim AS node
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN npm install && npm run prod && npm run img && rm -R node_modules
|
||||
RUN cd packages/adrema-form && npm ci && npm run build && rm -R node_modules && cd ../../
|
||||
RUN npm ci && 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
|
||||
COPY --from=node /app /app
|
||||
COPY --from=composer /app/public/vendor /app/public/vendor
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
FROM composer:2.2.7 as composer
|
||||
FROM composer:2.7.9 AS composer
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
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
|
||||
COPY . /app
|
||||
RUN npm install && npm run prod && npm run img && rm -R node_modules
|
||||
RUN cd packages/adrema-form && npm ci && npm run build && rm -R node_modules && cd ../../
|
||||
RUN npm ci && 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 --from=node /app/public /app/public
|
||||
COPY --chown=www-data:www-data --from=composer /app/vendor /app/vendor
|
||||
|
|
|
@ -12,7 +12,7 @@ steps:
|
|||
- git submodule update --init --recursive
|
||||
|
||||
- name: composer_dev
|
||||
image: composer:2.2.7
|
||||
image: composer:2.7.9
|
||||
commands:
|
||||
- composer install --ignore-platform-reqs --dev
|
||||
|
||||
|
@ -39,6 +39,7 @@ steps:
|
|||
- name: tests
|
||||
image: zoomyboy/adrema-base:latest
|
||||
commands:
|
||||
- touch .env
|
||||
- php artisan migrate
|
||||
- php artisan test
|
||||
- rm -f .env
|
||||
|
@ -102,7 +103,7 @@ steps:
|
|||
event: tag
|
||||
|
||||
- name: deploy
|
||||
image: php:8.1.6
|
||||
image: zoomyboy/adrema-base:latest
|
||||
environment:
|
||||
SSH_KEY:
|
||||
from_secret: deploy_private_key
|
||||
|
@ -135,7 +136,7 @@ steps:
|
|||
event: push
|
||||
|
||||
- name: composer_no_dev
|
||||
image: composer:2.2.7
|
||||
image: composer:2.7.9
|
||||
commands:
|
||||
- composer install --ignore-platform-reqs --no-dev
|
||||
|
||||
|
|
|
@ -40,3 +40,4 @@ Homestead.json
|
|||
/public/sprite.svg
|
||||
/.php-cs-fixer.cache
|
||||
/groups.sql
|
||||
/.phpunit.cache
|
||||
|
|
57
CHANGELOG.md
57
CHANGELOG.md
|
@ -1,5 +1,62 @@
|
|||
# Letzte Änderungen
|
||||
|
||||
### 1.12.7
|
||||
|
||||
- Fix: Synchronisation von allen Mitgliedern bei Mail-Verteilern - nicht nur den ersten 20
|
||||
|
||||
### 1.12.6
|
||||
|
||||
- Fix: Beiträge von Familienmitgliedern splitten
|
||||
|
||||
### 1.12.5
|
||||
|
||||
- Fix: Synchronisieren von bestehenden Beiträgen aus NaMi
|
||||
|
||||
### 1.12.4
|
||||
|
||||
- Filter Mitglieder nach Verhaltenskodex
|
||||
|
||||
### 1.12.3
|
||||
|
||||
- Volltextsuche für Rechnungen
|
||||
|
||||
### 1.12.2
|
||||
|
||||
- Zuschussliste Gallier
|
||||
|
||||
### 1.12.1
|
||||
|
||||
- In Teilnehmer-Liste von Veranstaltungen kann nun sortiert und gefiltert werden.
|
||||
- Formulare: Feld Registrierung von / bis
|
||||
|
||||
### 1.11.5
|
||||
|
||||
- Fix: Synchronisation von NaMi-Mitgliedern
|
||||
|
||||
### 1.11.4
|
||||
|
||||
- Fix: Nicht an Präventions-Unterlagen für vergangene Veranstaltungen erinnern
|
||||
|
||||
### 1.11.1
|
||||
|
||||
- Es kann nun auch das Feld "Datenweiterverwendung" über Adrema gepflegt werden.
|
||||
|
||||
### 1.10.20
|
||||
|
||||
- Fixed: Bei Textfeldern wird nun die Einleitung dargestellt
|
||||
|
||||
### 1.10.19
|
||||
|
||||
- Fixed: Erweiterte Führungszeugnisse und Präventionsschulungen nur für aktive Mitgliedschaften auf Dashboard anzeigen
|
||||
|
||||
### 1.10.18
|
||||
|
||||
- Fixed: All Gruppen als Option anbieten bei Bedingungen
|
||||
|
||||
### 1.10.17
|
||||
|
||||
- Es können nun auch Bedingungen für Felder vom Typ Gruppierung definiert werden
|
||||
|
||||
### 1.10.16
|
||||
|
||||
- Rechnungen und Erinnerungen werden nun automatisch täglich um 10 Uhr verschickt
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@servers(['docker' => ['stammsilva@zoomyboy.de', 'stammgallier@stamm-gallier.de', 'dpsg-lennep@zoomyboy.de', 'dpsgbergischland@zoomyboy.de', 'dpsg-koeln@dpsg-koeln.de']])
|
||||
@servers(['docker' => ['stamm-silva@zoomyboy.de', 'stammgallier@stamm-gallier.de', 'dpsg-lennep@zoomyboy.de', 'dpsgbergischland@zoomyboy.de', 'dpsg-koeln@dpsg-koeln.de']])
|
||||
|
||||
@task('deploy', ['on' => 'docker'])
|
||||
cd $ADREMA_PATH
|
||||
|
|
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
|
||||
|
||||
1. Herunterladen der Beispiel Docker-Compose
|
||||
1. Verschieben der Docker-Compose
|
||||
|
||||
```cmd
|
||||
curl https://git.zoomyboy.de/silva/adrema/raw/branch/master/docker-compose.prod.yml -o docker-compose.yml
|
||||
mv docker-compose.prod.yml docker-compose.yml
|
||||
```
|
||||
|
||||
2. Herunterladen der Beispiel Environmentvariablen-Datei
|
||||
2. Anwenden der Beispiel Environmentvariablen-Datei
|
||||
|
||||
```cmd
|
||||
curl https://git.zoomyboy.de/silva/adrema/raw/branch/master/.app.env.example -o .app.env
|
||||
mv .app.env.example .app.env
|
||||
```
|
||||
|
||||
3. In der `.app.env` notwendige Einstellungen vornehmen:
|
||||
|
@ -50,7 +50,8 @@ 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_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`)
|
||||
- `DB_PASSWORD` und `MYSQL_PASSWORD`: Mit dem selben sicheren Passwort für die Datenbank versehen
|
||||
- `DB_PASSWORD` und `MYSQL_PASSWORD`: Mit dem selben neu erstellten, 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
|
||||
|
||||
4. Container zur Gennerierung des App-Key starten
|
||||
|
@ -99,28 +100,22 @@ Bei dem Setup wird im Daten-Verzeichniss ein Ordner `./data/setup` angelegt. Hie
|
|||
git clone https://git.zoomyboy.de/silva/adrema.git
|
||||
```
|
||||
|
||||
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
|
||||
2. Kopieren der Beispiel Environmentvariablen-Datei
|
||||
|
||||
```cmd
|
||||
cp .app.env.example .app.env
|
||||
```
|
||||
|
||||
4. Submodule aktuallisieren
|
||||
3. Submodule aktuallisieren
|
||||
|
||||
```cmd
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
5. Container erstellen
|
||||
4. Container erstellen
|
||||
|
||||
```cmd
|
||||
docker compose build
|
||||
```
|
||||
|
||||
6. Mit Schritt 3 und den folgenden der [Installation des Produktivsystems](#installation-des-produktivsystems) fortfahren
|
||||
5. Mit Schritt 3 und den folgenden der [Installation des Produktivsystems](#installation-des-produktivsystems) fortfahren
|
||||
|
|
|
@ -22,7 +22,7 @@ class InsertMemberAction
|
|||
{
|
||||
$region = Region::firstWhere('nami_id', $member->regionId ?: -1);
|
||||
|
||||
return Member::updateOrCreate(['nami_id' => $member->id], [
|
||||
$payload = [
|
||||
'firstname' => $member->firstname,
|
||||
'lastname' => $member->lastname,
|
||||
'joined_at' => $member->joinedAt,
|
||||
|
@ -50,7 +50,27 @@ class InsertMemberAction
|
|||
'nationality_id' => Nationality::where('nami_id', $member->nationalityId)->firstOrFail()->id,
|
||||
'mitgliedsnr' => $member->memberId,
|
||||
'version' => $member->version,
|
||||
]);
|
||||
'keepdata' => $member->keepdata,
|
||||
];
|
||||
|
||||
// Dont update subscription if fee id of existing member's subscription is already the same
|
||||
if ($existing = Member::nami($member->id)) {
|
||||
if ($existing->subscription && $existing->subscription->fee->nami_id === $member->feeId) {
|
||||
$payload['subscription_id'] = $existing->subscription->id;
|
||||
}
|
||||
}
|
||||
|
||||
return tap(Member::updateOrCreate(['nami_id' => $member->id], $payload), function ($insertedMember) use ($member) {
|
||||
$insertedMember->bankAccount->update([
|
||||
'iban' => $member->bankAccount->iban,
|
||||
'bic' => $member->bankAccount->bic,
|
||||
'blz' => $member->bankAccount->blz,
|
||||
'account_number' => $member->bankAccount->accountNumber,
|
||||
'person' => $member->bankAccount->person,
|
||||
'bank_name' => $member->bankAccount->bankName,
|
||||
'nami_id' => $member->bankAccount->id,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function getSubscription(NamiMember $member): ?Subscription
|
||||
|
|
|
@ -4,12 +4,14 @@ namespace App;
|
|||
|
||||
use App\Nami\HasNamiField;
|
||||
use Cviebrock\EloquentSluggable\Sluggable;
|
||||
use Database\Factories\ActivityFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Activity extends Model
|
||||
{
|
||||
/** @use HasFactory<ActivityFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
use Sluggable;
|
||||
|
@ -32,7 +34,7 @@ class Activity extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany<Subactivity>
|
||||
* @return BelongsToMany<Subactivity, $this>
|
||||
*/
|
||||
public function subactivities(): BelongsToMany
|
||||
{
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
namespace App;
|
||||
|
||||
use Database\Factories\ConfessionFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Confession extends Model
|
||||
{
|
||||
/** @use HasFactory<ConfessionFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public $fillable = ['name', 'nami_id', 'is_null'];
|
||||
|
|
|
@ -8,6 +8,7 @@ use App\Contribution\Documents\RdpNrwDocument;
|
|||
use App\Contribution\Documents\CityRemscheidDocument;
|
||||
use App\Contribution\Documents\CitySolingenDocument;
|
||||
use App\Contribution\Documents\CityFrankfurtMainDocument;
|
||||
use App\Contribution\Documents\WuppertalDocument;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
|
@ -22,6 +23,7 @@ class ContributionFactory
|
|||
CityRemscheidDocument::class,
|
||||
CityFrankfurtMainDocument::class,
|
||||
BdkjHesse::class,
|
||||
WuppertalDocument::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -30,7 +32,7 @@ class ContributionFactory
|
|||
public function compilerSelect(): Collection
|
||||
{
|
||||
return collect($this->documents)->map(fn ($document) => [
|
||||
'title' => $document::getName(),
|
||||
'title' => $document::buttonName(),
|
||||
'class' => $document,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class MemberData extends Data
|
|||
*/
|
||||
public static function fromModels(array $ids): Collection
|
||||
{
|
||||
return Member::whereIn('id', $ids)->orderByRaw('lastname, firstname')->get()->map(fn ($member) => self::withoutMagicalCreationFrom([
|
||||
return Member::whereIn('id', $ids)->orderByRaw('lastname, firstname')->get()->map(fn ($member) => self::factory()->withoutMagicalCreation()->from([
|
||||
...$member->toArray(),
|
||||
'birthday' => $member->birthday->toAtomString(),
|
||||
'isLeader' => $member->isLeader(),
|
||||
|
@ -44,7 +44,7 @@ class MemberData extends Data
|
|||
*/
|
||||
public static function fromApi(array $data): Collection
|
||||
{
|
||||
return collect($data)->map(fn ($member) => self::withoutMagicalCreationFrom([
|
||||
return collect($data)->map(fn ($member) => self::factory()->withoutMagicalCreation()->from([
|
||||
...$member,
|
||||
'birthday' => Carbon::parse($member['birthday'])->toAtomString(),
|
||||
'gender' => Gender::fromString($member['gender']),
|
||||
|
@ -54,31 +54,41 @@ class MemberData extends Data
|
|||
|
||||
public function fullname(): string
|
||||
{
|
||||
return $this->firstname.' '.$this->lastname;
|
||||
return $this->firstname . ' ' . $this->lastname;
|
||||
}
|
||||
|
||||
public function separatedName(): string
|
||||
{
|
||||
return $this->lastname.', '.$this->firstname;
|
||||
return $this->lastname . ', ' . $this->firstname;
|
||||
}
|
||||
|
||||
public function fullAddress(): string
|
||||
{
|
||||
return $this->address.', '.$this->zip.' '.$this->location;
|
||||
return $this->address . ', ' . $this->zip . ' ' . $this->location;
|
||||
}
|
||||
|
||||
public function city(): string
|
||||
{
|
||||
return $this->zip.' '.$this->location;
|
||||
return $this->zip . ' ' . $this->location;
|
||||
}
|
||||
|
||||
public function age(): string
|
||||
public function age(): int
|
||||
{
|
||||
return (string) $this->birthday->diffInYears(now()) ?: '';
|
||||
return intval($this->birthday->diffInYears(now()));
|
||||
}
|
||||
|
||||
public function birthYear(): string
|
||||
{
|
||||
return (string) $this->birthday->year;
|
||||
}
|
||||
|
||||
public function birthdayHuman(): string
|
||||
{
|
||||
return $this->birthday->format('d.m.Y');
|
||||
}
|
||||
|
||||
public function genderLetter(): string
|
||||
{
|
||||
return $this->gender->short;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,16 @@
|
|||
namespace App\Contribution\Documents;
|
||||
|
||||
use App\Contribution\Data\MemberData;
|
||||
use App\Contribution\Traits\HasPdfBackground;
|
||||
use App\Country;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Zoomyboy\Tex\Engine;
|
||||
use Zoomyboy\Tex\Template;
|
||||
|
||||
class BdkjHesse extends ContributionDocument
|
||||
{
|
||||
|
||||
use HasPdfBackground;
|
||||
|
||||
/**
|
||||
* @param Collection<int, Collection<int, MemberData>> $members
|
||||
*/
|
||||
|
@ -25,6 +26,7 @@ class BdkjHesse extends ContributionDocument
|
|||
public ?string $filename = '',
|
||||
public string $type = 'F',
|
||||
) {
|
||||
$this->setEventName($eventName);
|
||||
}
|
||||
|
||||
public function dateFrom(): string
|
||||
|
@ -74,7 +76,7 @@ class BdkjHesse extends ContributionDocument
|
|||
|
||||
public function durationDays(): int
|
||||
{
|
||||
return Carbon::parse($this->dateUntil)->diffInDays(Carbon::parse($this->dateFrom)) + 1;
|
||||
return intVal(Carbon::parse($this->dateUntil)->diffInDays(Carbon::parse($this->dateFrom))) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,36 +116,9 @@ class BdkjHesse extends ContributionDocument
|
|||
return $member->birthYear();
|
||||
}
|
||||
|
||||
public function basename(): string
|
||||
{
|
||||
return 'zuschuesse-bdkj-hessen' . Str::slug($this->eventName);
|
||||
}
|
||||
|
||||
public function view(): string
|
||||
{
|
||||
return 'tex.contribution.bdkj-hesse';
|
||||
}
|
||||
|
||||
public function template(): Template
|
||||
{
|
||||
return Template::make('tex.templates.contribution');
|
||||
}
|
||||
|
||||
public function setFilename(string $filename): static
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return Engine::PDFLATEX;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'Für BDKJ Hessen erstellen';
|
||||
return 'BDKJ Hessen';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,7 +131,6 @@ class BdkjHesse extends ContributionDocument
|
|||
'dateUntil' => 'required|string|date_format:Y-m-d',
|
||||
'country' => 'required|integer|exists:countries,id',
|
||||
'zipLocation' => 'required|string',
|
||||
'eventName' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
namespace App\Contribution\Documents;
|
||||
|
||||
use App\Contribution\Data\MemberData;
|
||||
use App\Contribution\Traits\FormatsDates;
|
||||
use App\Contribution\Traits\HasPdfBackground;
|
||||
use App\Country;
|
||||
use App\Invoice\InvoiceSettings;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Zoomyboy\Tex\Engine;
|
||||
use Zoomyboy\Tex\Template;
|
||||
|
||||
class CityFrankfurtMainDocument extends ContributionDocument
|
||||
{
|
||||
use HasPdfBackground;
|
||||
use FormatsDates;
|
||||
|
||||
public string $fromName;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +29,7 @@ class CityFrankfurtMainDocument extends ContributionDocument
|
|||
public ?string $filename = '',
|
||||
public string $type = 'F',
|
||||
) {
|
||||
$this->setEventName($eventName);
|
||||
$this->fromName = app(InvoiceSettings::class)->from_long;
|
||||
}
|
||||
|
||||
|
@ -61,17 +63,6 @@ class CityFrankfurtMainDocument extends ContributionDocument
|
|||
);
|
||||
}
|
||||
|
||||
public function dateFromHuman(): string
|
||||
{
|
||||
return Carbon::parse($this->dateFrom)->format('d.m.Y');
|
||||
}
|
||||
|
||||
public function dateUntilHuman(): string
|
||||
{
|
||||
return Carbon::parse($this->dateUntil)->format('d.m.Y');
|
||||
}
|
||||
|
||||
|
||||
public function countryName(): string
|
||||
{
|
||||
return $this->country->name;
|
||||
|
@ -82,56 +73,9 @@ class CityFrankfurtMainDocument extends ContributionDocument
|
|||
return count($this->members);
|
||||
}
|
||||
|
||||
public function memberShort(MemberData $member): string
|
||||
{
|
||||
return $member->isLeader ? 'L' : '';
|
||||
}
|
||||
|
||||
public function memberName(MemberData $member): string
|
||||
{
|
||||
return $member->separatedName();
|
||||
}
|
||||
|
||||
public function memberAddress(MemberData $member): string
|
||||
{
|
||||
return $member->fullAddress();
|
||||
}
|
||||
|
||||
public function memberAge(MemberData $member): string
|
||||
{
|
||||
return $member->age();
|
||||
}
|
||||
|
||||
public function basename(): string
|
||||
{
|
||||
return 'zuschuesse-frankfurt-' . Str::slug($this->eventName);
|
||||
}
|
||||
|
||||
public function view(): string
|
||||
{
|
||||
return 'tex.contribution.city-frankfurt-main';
|
||||
}
|
||||
|
||||
public function template(): Template
|
||||
{
|
||||
return Template::make('tex.templates.contribution');
|
||||
}
|
||||
|
||||
public function setFilename(string $filename): static
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return Engine::PDFLATEX;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'Für Frankfurt erstellen';
|
||||
return 'Frankfurt';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +88,6 @@ class CityFrankfurtMainDocument extends ContributionDocument
|
|||
'dateUntil' => 'required|string|date_format:Y-m-d',
|
||||
'country' => 'required|integer|exists:countries,id',
|
||||
'zipLocation' => 'required|string',
|
||||
'eventName' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,17 @@
|
|||
namespace App\Contribution\Documents;
|
||||
|
||||
use App\Contribution\Data\MemberData;
|
||||
use App\Contribution\Traits\FormatsDates;
|
||||
use App\Contribution\Traits\HasPdfBackground;
|
||||
use App\Country;
|
||||
use App\Member\Member;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Zoomyboy\Tex\Engine;
|
||||
use Zoomyboy\Tex\Template;
|
||||
|
||||
class CityRemscheidDocument extends ContributionDocument
|
||||
{
|
||||
use HasPdfBackground;
|
||||
use FormatsDates;
|
||||
|
||||
/**
|
||||
* @param Collection<int, Collection<int, Member>> $leaders
|
||||
* @param Collection<int, Collection<int, Member>> $children
|
||||
|
@ -25,17 +27,9 @@ class CityRemscheidDocument extends ContributionDocument
|
|||
public Collection $children,
|
||||
public ?string $filename = '',
|
||||
public string $type = 'F',
|
||||
public string $eventName = '',
|
||||
) {
|
||||
}
|
||||
|
||||
public function niceDateFrom(): string
|
||||
{
|
||||
return Carbon::parse($this->dateFrom)->format('d.m.Y');
|
||||
}
|
||||
|
||||
public function niceDateUntil(): string
|
||||
{
|
||||
return Carbon::parse($this->dateUntil)->format('d.m.Y');
|
||||
$this->setEventName($eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,6 +46,7 @@ class CityRemscheidDocument extends ContributionDocument
|
|||
country: Country::where('id', $request['country'])->firstOrFail(),
|
||||
leaders: $leaders->values()->toBase()->chunk(6),
|
||||
children: $children->values()->toBase()->chunk(20),
|
||||
eventName: $request['eventName'],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -70,39 +65,13 @@ class CityRemscheidDocument extends ContributionDocument
|
|||
country: Country::where('id', $request['country'])->firstOrFail(),
|
||||
leaders: $leaders->values()->toBase()->chunk(6),
|
||||
children: $children->values()->toBase()->chunk(20),
|
||||
eventName: $request['eventName'],
|
||||
);
|
||||
}
|
||||
|
||||
public function basename(): string
|
||||
{
|
||||
return 'zuschuesse-remscheid';
|
||||
}
|
||||
|
||||
public function view(): string
|
||||
{
|
||||
return 'tex.contribution.city-remscheid';
|
||||
}
|
||||
|
||||
public function template(): Template
|
||||
{
|
||||
return Template::make('tex.templates.contribution');
|
||||
}
|
||||
|
||||
public function setFilename(string $filename): static
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return Engine::PDFLATEX;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'Für Remscheid erstellen';
|
||||
return 'Remscheid';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,9 +6,7 @@ use App\Contribution\Data\MemberData;
|
|||
use App\Invoice\InvoiceSettings;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Zoomyboy\Tex\Engine;
|
||||
use Zoomyboy\Tex\Template;
|
||||
|
||||
class CitySolingenDocument extends ContributionDocument
|
||||
{
|
||||
|
@ -25,6 +23,7 @@ class CitySolingenDocument extends ContributionDocument
|
|||
public string $eventName,
|
||||
public string $type = 'F',
|
||||
) {
|
||||
$this->setEventName($eventName);
|
||||
$this->fromName = app(InvoiceSettings::class)->from_long;
|
||||
}
|
||||
|
||||
|
@ -74,11 +73,6 @@ class CitySolingenDocument extends ContributionDocument
|
|||
return Carbon::parse($this->dateUntil)->format('d.m.Y');
|
||||
}
|
||||
|
||||
public function template(): Template
|
||||
{
|
||||
return Template::make('tex.templates.contribution');
|
||||
}
|
||||
|
||||
public function checkboxes(): string
|
||||
{
|
||||
$output = '';
|
||||
|
@ -94,16 +88,6 @@ class CitySolingenDocument extends ContributionDocument
|
|||
return $firstRow . "\n" . $secondRow;
|
||||
}
|
||||
|
||||
public function basename(): string
|
||||
{
|
||||
return 'zuschuesse-solingen-' . Str::slug($this->eventName);
|
||||
}
|
||||
|
||||
public function view(): string
|
||||
{
|
||||
return 'tex.contribution.city-solingen';
|
||||
}
|
||||
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return Engine::PDFLATEX;
|
||||
|
@ -111,7 +95,7 @@ class CitySolingenDocument extends ContributionDocument
|
|||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'Für Stadt Solingen erstellen';
|
||||
return 'Stadt Solingen';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +107,6 @@ class CitySolingenDocument extends ContributionDocument
|
|||
'dateFrom' => 'required|string|date_format:Y-m-d',
|
||||
'dateUntil' => 'required|string|date_format:Y-m-d',
|
||||
'zipLocation' => 'required|string',
|
||||
'eventName' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
namespace App\Contribution\Documents;
|
||||
|
||||
use Zoomyboy\Tex\Document;
|
||||
use Zoomyboy\Tex\Template;
|
||||
|
||||
abstract class ContributionDocument extends Document
|
||||
{
|
||||
private string $eventName;
|
||||
|
||||
abstract public static function getName(): string;
|
||||
|
||||
/**
|
||||
|
@ -29,8 +32,34 @@ abstract class ContributionDocument extends Document
|
|||
public static function globalRules(): array
|
||||
{
|
||||
return [
|
||||
'eventName' => 'required|string',
|
||||
'members' => 'present|array|min:1',
|
||||
'members.*' => 'integer|exists:members,id',
|
||||
];
|
||||
}
|
||||
|
||||
public static function buttonName(): string
|
||||
{
|
||||
return 'Für ' . static::getName() . ' erstellen';;
|
||||
}
|
||||
|
||||
public function setEventName(string $eventName): void
|
||||
{
|
||||
$this->eventName = $eventName;
|
||||
}
|
||||
|
||||
public function basename(): string
|
||||
{
|
||||
return str('Zuschüsse ')->append($this->getName())->append(' ')->append($this->eventName)->slug();
|
||||
}
|
||||
|
||||
public function template(): Template
|
||||
{
|
||||
return Template::make('tex.templates.contribution');
|
||||
}
|
||||
|
||||
public function view(): string
|
||||
{
|
||||
return 'tex.contribution.' . str(class_basename(static::class))->replace('Document', '')->kebab()->toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
namespace App\Contribution\Documents;
|
||||
|
||||
use App\Contribution\Data\MemberData;
|
||||
use App\Contribution\Traits\FormatsDates;
|
||||
use App\Contribution\Traits\HasPdfBackground;
|
||||
use App\Country;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Zoomyboy\Tex\Engine;
|
||||
use Zoomyboy\Tex\Template;
|
||||
|
||||
class RdpNrwDocument extends ContributionDocument
|
||||
{
|
||||
use HasPdfBackground;
|
||||
use FormatsDates;
|
||||
|
||||
/**
|
||||
* @param Collection<int, Collection<int, MemberData>> $members
|
||||
*/
|
||||
|
@ -22,14 +24,9 @@ class RdpNrwDocument extends ContributionDocument
|
|||
public Collection $members,
|
||||
public ?string $filename = '',
|
||||
public string $type = 'F',
|
||||
public string $eventName = '',
|
||||
) {
|
||||
}
|
||||
|
||||
public function dateRange(): string
|
||||
{
|
||||
return Carbon::parse($this->dateFrom)->format('d.m.Y')
|
||||
. ' - '
|
||||
. Carbon::parse($this->dateUntil)->format('d.m.Y');
|
||||
$this->setEventName($eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,6 +40,7 @@ class RdpNrwDocument extends ContributionDocument
|
|||
zipLocation: $request['zipLocation'],
|
||||
country: Country::where('id', $request['country'])->firstOrFail(),
|
||||
members: MemberData::fromModels($request['members'])->chunk(17),
|
||||
eventName: $request['eventName'],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -57,6 +55,7 @@ class RdpNrwDocument extends ContributionDocument
|
|||
zipLocation: $request['zipLocation'],
|
||||
country: Country::where('id', $request['country'])->firstOrFail(),
|
||||
members: MemberData::fromApi($request['member_data'])->chunk(17),
|
||||
eventName: $request['eventName'],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -65,65 +64,9 @@ class RdpNrwDocument extends ContributionDocument
|
|||
return $this->country->name;
|
||||
}
|
||||
|
||||
public function memberShort(MemberData $member): string
|
||||
{
|
||||
return $member->isLeader ? 'L' : '';
|
||||
}
|
||||
|
||||
public function memberName(MemberData $member): string
|
||||
{
|
||||
return $member->separatedName();
|
||||
}
|
||||
|
||||
public function memberAddress(MemberData $member): string
|
||||
{
|
||||
return $member->fullAddress();
|
||||
}
|
||||
|
||||
public function memberGender(MemberData $member): string
|
||||
{
|
||||
if (!$member->gender) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return strtolower(substr($member->gender->name, 0, 1));
|
||||
}
|
||||
|
||||
public function memberAge(MemberData $member): string
|
||||
{
|
||||
return $member->age();
|
||||
}
|
||||
|
||||
public function basename(): string
|
||||
{
|
||||
return 'zuschuesse-rdp-nrw';
|
||||
}
|
||||
|
||||
public function view(): string
|
||||
{
|
||||
return 'tex.contribution.rdp-nrw';
|
||||
}
|
||||
|
||||
public function template(): Template
|
||||
{
|
||||
return Template::make('tex.templates.contribution');
|
||||
}
|
||||
|
||||
public function setFilename(string $filename): static
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return Engine::PDFLATEX;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'Für RdP NRW erstellen';
|
||||
return 'RdP NRW';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,7 +79,6 @@ class RdpNrwDocument extends ContributionDocument
|
|||
'dateUntil' => 'required|string|date_format:Y-m-d',
|
||||
'country' => 'required|integer|exists:countries,id',
|
||||
'zipLocation' => 'required|string',
|
||||
'eventName' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace App\Contribution\Documents;
|
||||
|
||||
use App\Contribution\Data\MemberData;
|
||||
use App\Contribution\Traits\FormatsDates;
|
||||
use App\Contribution\Traits\HasPdfBackground;
|
||||
use App\Country;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class WuppertalDocument extends ContributionDocument
|
||||
{
|
||||
|
||||
use HasPdfBackground;
|
||||
use FormatsDates;
|
||||
|
||||
/**
|
||||
* @param Collection<int, Collection<int, MemberData>> $members
|
||||
*/
|
||||
public function __construct(
|
||||
public string $dateFrom,
|
||||
public string $dateUntil,
|
||||
public string $zipLocation,
|
||||
public ?Country $country,
|
||||
public Collection $members,
|
||||
public ?string $filename = '',
|
||||
public string $type = 'F',
|
||||
public string $eventName = '',
|
||||
) {
|
||||
$this->setEventName($eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function fromRequest(array $request): self
|
||||
{
|
||||
return new self(
|
||||
dateFrom: $request['dateFrom'],
|
||||
dateUntil: $request['dateUntil'],
|
||||
zipLocation: $request['zipLocation'],
|
||||
country: Country::where('id', $request['country'])->firstOrFail(),
|
||||
members: MemberData::fromModels($request['members'])->chunk(14),
|
||||
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'],
|
||||
);
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'Wuppertal';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
'dateFrom' => 'required|string|date_format:Y-m-d',
|
||||
'dateUntil' => 'required|string|date_format:Y-m-d',
|
||||
'zipLocation' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Contribution\Traits;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
trait FormatsDates
|
||||
{
|
||||
|
||||
public function niceDateFrom(): string
|
||||
{
|
||||
return Carbon::parse($this->dateFrom)->format('d.m.Y');
|
||||
}
|
||||
|
||||
public function niceDateUntil(): string
|
||||
{
|
||||
return Carbon::parse($this->dateUntil)->format('d.m.Y');
|
||||
}
|
||||
|
||||
public function dateRange(): string
|
||||
{
|
||||
return implode(' - ', [$this->niceDateFrom(), $this->niceDateUntil()]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Contribution\Traits;
|
||||
|
||||
use Zoomyboy\Tex\Engine;
|
||||
|
||||
trait HasPdfBackground
|
||||
{
|
||||
public function getEngine(): Engine
|
||||
{
|
||||
return Engine::PDFLATEX;
|
||||
}
|
||||
}
|
|
@ -3,11 +3,13 @@
|
|||
namespace App;
|
||||
|
||||
use App\Nami\HasNamiField;
|
||||
use Database\Factories\CountryFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Country extends Model
|
||||
{
|
||||
/** @use HasFactory<CountryFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
namespace App\Course\Models;
|
||||
|
||||
use App\Nami\HasNamiField;
|
||||
use Database\Factories\Course\Models\CourseFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Course extends Model
|
||||
{
|
||||
/** @use HasFactory<CourseFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
|
||||
|
|
|
@ -3,19 +3,21 @@
|
|||
namespace App\Course\Models;
|
||||
|
||||
use App\Member\Member;
|
||||
use Database\Factories\Course\Models\CourseMemberFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class CourseMember extends Model
|
||||
{
|
||||
/** @use HasFactory<CourseMemberFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/** @var array<int, string> */
|
||||
public $guarded = [];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Course, self>
|
||||
* @return BelongsTo<Course, $this>
|
||||
*/
|
||||
public function course(): BelongsTo
|
||||
{
|
||||
|
@ -23,7 +25,7 @@ class CourseMember extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Member, self>
|
||||
* @return BelongsTo<Member, $this>
|
||||
*/
|
||||
public function member(): BelongsTo
|
||||
{
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
namespace App\Dashboard;
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use App\Dashboard\Actions\IndexAction as DashboardIndexAction;
|
||||
|
||||
class DashboardServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
@ -23,5 +25,8 @@ class DashboardServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot()
|
||||
{
|
||||
app(Router::class)->middleware(['web', 'auth:web'])->group(function ($router) {
|
||||
$router->get('/', DashboardIndexAction::class)->name('home');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,7 @@ class Principal implements PrincipalBackendInterface
|
|||
*/
|
||||
public function findByUri($uri, $principalPrefix)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,7 @@ class EfzPendingBlock extends Block
|
|||
})
|
||||
->whereCurrentGroup()
|
||||
->orderByRaw('lastname, firstname')
|
||||
->whereHas('memberships', fn ($builder) => $builder->isLeader());
|
||||
->whereHas('memberships', fn ($builder) => $builder->isLeader()->active());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,23 +4,24 @@ namespace App;
|
|||
|
||||
use App\Nami\HasNamiField;
|
||||
use App\Payment\Subscription;
|
||||
use Database\Factories\FeeFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Fee extends Model
|
||||
{
|
||||
/** @use HasFactory<FeeFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
|
||||
/** @var array<int, string> */
|
||||
public $fillable = ['name', 'nami_id'];
|
||||
|
||||
/** @var bool */
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* @return HasMany<Subscription>
|
||||
* @return HasMany<Subscription, $this>
|
||||
*/
|
||||
public function subscriptions(): HasMany
|
||||
{
|
||||
|
|
|
@ -17,6 +17,6 @@ class ListFilesAction
|
|||
*/
|
||||
public function handle(ActionRequest $request, Fileshare $fileshare): DataCollection
|
||||
{
|
||||
return ResourceData::collection($fileshare->type->getSubDirectories($request->input('parent')))->wrap('data');
|
||||
return ResourceData::collect($fileshare->type->getSubDirectories($request->input('parent')), DataCollection::class)->wrap('data');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
namespace App\Fileshare\Models;
|
||||
|
||||
use App\Fileshare\ConnectionTypes\ConnectionType;
|
||||
use Database\Factories\Fileshare\Models\FileshareFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Fileshare extends Model
|
||||
{
|
||||
/** @use HasFactory<FileshareFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public $guarded = [];
|
||||
|
|
|
@ -22,8 +22,8 @@ class FormUpdateMetaAction
|
|||
|
||||
return [
|
||||
'sorting' => 'array',
|
||||
'sorting.0' => 'required|string',
|
||||
'sorting.1' => 'required|string|in:asc,desc',
|
||||
'sorting.by' => 'required|string',
|
||||
'sorting.direction' => 'required|boolean',
|
||||
'active_columns' => 'array',
|
||||
'active_columns.*' => ['string', Rule::in([...$form->getFields()->pluck('key')->toArray(), 'created_at', 'prevention'])]
|
||||
];
|
||||
|
|
|
@ -6,9 +6,8 @@ use App\Form\Models\Form;
|
|||
use App\Form\Models\Participant;
|
||||
use App\Form\Resources\ParticipantResource;
|
||||
use App\Form\Scopes\ParticipantFilterScope;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Laravel\Scout\Builder;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class ParticipantIndexAction
|
||||
|
@ -16,29 +15,22 @@ class ParticipantIndexAction
|
|||
use AsAction;
|
||||
|
||||
/**
|
||||
* @return HasMany<Participant>
|
||||
* @return Builder<Participant>
|
||||
*/
|
||||
protected function getQuery(Form $form, ParticipantFilterScope $filter): HasMany
|
||||
protected function getQuery(Form $form, ParticipantFilterScope $filter): Builder
|
||||
{
|
||||
return $form->participants()->withFilter($filter)->withCount('children')->with('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LengthAwarePaginator<Participant>
|
||||
*/
|
||||
public function handle(Form $form, ParticipantFilterScope $filter): LengthAwarePaginator
|
||||
{
|
||||
return $this->getQuery($form, $filter)->paginate(15);
|
||||
return $filter->setForm($form)->getQuery()
|
||||
->query(fn ($q) => $q->withCount('children')->with('form'));
|
||||
}
|
||||
|
||||
public function asController(Form $form, ?int $parent = null): AnonymousResourceCollection
|
||||
{
|
||||
$filter = ParticipantFilterScope::fromRequest(request()->input('filter'));
|
||||
$filter = ParticipantFilterScope::fromRequest(request()->input('filter', ''))->parent($parent);
|
||||
|
||||
$data = match ($parent) {
|
||||
null => $this->handle($form, $filter),
|
||||
-1 => $this->getQuery($form, $filter)->where('parent_id', null)->paginate(15),
|
||||
default => $this->getQuery($form, $filter)->where('parent_id', $parent)->get(),
|
||||
null => $this->getQuery($form, $filter)->paginate(15), // initial all elements - paginate
|
||||
-1 => $this->getQuery($form, $filter)->paginate(15), // initial root elements - parinate
|
||||
default => $this->getQuery($form, $filter)->get(), // specific parent element - show all
|
||||
};
|
||||
|
||||
return ParticipantResource::collection($data)->additional(['meta' => ParticipantResource::meta($form)]);
|
||||
|
|
|
@ -17,7 +17,12 @@ class PreventionRememberAction
|
|||
|
||||
public function handle(): void
|
||||
{
|
||||
$query = Participant::whereHas('form', fn ($form) => $form->where('needs_prevention', true))
|
||||
$query = Participant::whereHas(
|
||||
'form',
|
||||
fn ($form) => $form
|
||||
->where('needs_prevention', true)
|
||||
->where('from', '>=', now())
|
||||
)
|
||||
->where(
|
||||
fn ($q) => $q
|
||||
->where('last_remembered_at', '<=', now()->subWeeks(2))
|
||||
|
|
|
@ -7,6 +7,7 @@ use App\Form\Models\Form;
|
|||
use App\Form\Models\Participant;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
@ -19,6 +20,10 @@ class RegisterAction
|
|||
*/
|
||||
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)
|
||||
->withNamiType()
|
||||
->reduce(fn ($query, $field) => $field->namiType->performQuery($query, $field->value), (new Member())->newQuery());
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Form\Actions;
|
||||
|
||||
use App\Form\Models\Form;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class UpdateParticipantSearchIndexAction
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Form $form): void
|
||||
{
|
||||
if (config('scout.driver') !== 'meilisearch') {
|
||||
return;
|
||||
}
|
||||
|
||||
$form->searchableUsing()->updateIndexSettings(
|
||||
$form->participantsSearchableAs(),
|
||||
[
|
||||
'filterableAttributes' => [...$form->getFields()->filterables()->getKeys(), 'parent-id'],
|
||||
'searchableAttributes' => $form->getFields()->searchables()->getKeys(),
|
||||
'sortableAttributes' => [...$form->getFields()->sortables()->getKeys(), 'id', 'created_at'],
|
||||
'displayedAttributes' => [...$form->getFields()->filterables()->getKeys(), ...$form->getFields()->searchables()->getKeys(), 'id'],
|
||||
'pagination' => [
|
||||
'maxTotalHits' => 1000000,
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use App\Form\Fields\Field;
|
|||
use Illuminate\Support\Collection;
|
||||
use Spatie\LaravelData\Casts\Cast;
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\Support\Creation\CreationContext;
|
||||
use Spatie\LaravelData\Support\DataProperty;
|
||||
|
||||
class CollectionCast implements Cast
|
||||
|
@ -20,10 +21,9 @@ class CollectionCast implements Cast
|
|||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $value
|
||||
* @param array<string, mixed> $context
|
||||
* @return Collection<int, Data>
|
||||
*/
|
||||
public function cast(DataProperty $property, mixed $value, array $context): mixed
|
||||
public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): mixed
|
||||
{
|
||||
return collect($value)->map(fn ($item) => $this->target::from($item));
|
||||
}
|
||||
|
|
|
@ -5,16 +5,16 @@ namespace App\Form\Casts;
|
|||
use App\Form\Data\FieldCollection;
|
||||
use App\Form\Fields\Field;
|
||||
use Spatie\LaravelData\Casts\Cast;
|
||||
use Spatie\LaravelData\Support\Creation\CreationContext;
|
||||
use Spatie\LaravelData\Support\DataProperty;
|
||||
|
||||
class FieldCollectionCast implements Cast
|
||||
{
|
||||
/**
|
||||
* @param array<int, array<string, string>> $value
|
||||
* @param array<string, mixed> $context
|
||||
* @return FieldCollection
|
||||
*/
|
||||
public function cast(DataProperty $property, mixed $value, array $context): mixed
|
||||
public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): mixed
|
||||
{
|
||||
return new FieldCollection(collect($value)->map(fn ($value) => Field::classFromType($value['type'])::from($value))->all());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\Form\Contracts;
|
||||
|
||||
interface Filterable
|
||||
{
|
||||
/** @param mixed $value */
|
||||
public function filter($value): string;
|
||||
}
|
|
@ -3,11 +3,11 @@
|
|||
namespace App\Form\Data;
|
||||
|
||||
use App\Fileshare\Data\FileshareResourceData;
|
||||
use App\Form\Fields\Field;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
use Spatie\LaravelData\Support\EloquentCasts\DataEloquentCast;
|
||||
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
#[MapOutputName(SnakeCaseMapper::class)]
|
||||
|
@ -16,4 +16,13 @@ class ExportData extends Data
|
|||
public function __construct(public ?FileshareResourceData $root = null, public ?string $groupBy = null, public ?string $toGroupField = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return DataEloquentCast<self>
|
||||
*/
|
||||
public static function castUsing(array $arguments): DataEloquentCast
|
||||
{
|
||||
return new DataEloquentCast(static::class, $arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Form\Data;
|
||||
|
||||
use App\Form\Contracts\Filterable;
|
||||
use App\Form\Enums\SpecialType;
|
||||
use App\Form\Fields\Field;
|
||||
use App\Form\Fields\NamiField;
|
||||
|
@ -117,4 +118,27 @@ class FieldCollection extends Collection
|
|||
{
|
||||
return $this->first(fn ($field) => $field->specialType === $specialType);
|
||||
}
|
||||
|
||||
public function searchables(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sortables(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterables(): self
|
||||
{
|
||||
return $this->filter(fn ($field) => $field instanceof Filterable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function getKeys(): array
|
||||
{
|
||||
return $this->map(fn ($field) => $field->key)->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use Illuminate\Support\Collection;
|
|||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\Attributes\WithCast;
|
||||
use Spatie\LaravelData\Attributes\WithTransformer;
|
||||
use Spatie\LaravelData\Support\EloquentCasts\DataEloquentCast;
|
||||
|
||||
class FormConfigData extends Data
|
||||
{
|
||||
|
@ -29,4 +30,13 @@ class FormConfigData extends Data
|
|||
new FieldCollection([])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return DataEloquentCast<self>
|
||||
*/
|
||||
public static function castUsing(array $arguments): DataEloquentCast
|
||||
{
|
||||
return new DataEloquentCast(static::class, $arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ enum NamiType: string
|
|||
static::LOCATION => $member->location,
|
||||
static::NICKNAME => $member->nickname,
|
||||
static::GENDER => $member->gender?->name,
|
||||
static::AGE => $member->birthday->diffInYears(now()),
|
||||
static::AGE => intVal($member->birthday->diffInYears(now())),
|
||||
static::AGEEVENT => $member->birthday->diffInYears($form->from),
|
||||
static::MOBILEPHONE => $member->mobile_phone,
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Form\Fields;
|
||||
|
||||
use App\Form\Contracts\Filterable;
|
||||
use App\Form\Matchers\BooleanMatcher;
|
||||
use App\Form\Matchers\Matcher;
|
||||
use App\Form\Models\Form;
|
||||
|
@ -9,9 +10,8 @@ use App\Form\Models\Participant;
|
|||
use App\Form\Presenters\BooleanPresenter;
|
||||
use App\Form\Presenters\Presenter;
|
||||
use Faker\Generator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CheckboxField extends Field
|
||||
class CheckboxField extends Field implements Filterable
|
||||
{
|
||||
public bool $required;
|
||||
public string $description;
|
||||
|
@ -86,4 +86,11 @@ class CheckboxField extends Field
|
|||
{
|
||||
return app(BooleanMatcher::class);
|
||||
}
|
||||
|
||||
public function filter($value): string
|
||||
{
|
||||
$asString = $value ? 'true' : 'false';
|
||||
|
||||
return "{$this->key} = $asString";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Form\Fields;
|
||||
|
||||
use App\Form\Contracts\Filterable;
|
||||
use App\Form\Matchers\Matcher;
|
||||
use App\Form\Matchers\SingleValueMatcher;
|
||||
use App\Form\Models\Form;
|
||||
|
@ -9,7 +10,7 @@ use App\Form\Models\Participant;
|
|||
use Faker\Generator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class DropdownField extends Field
|
||||
class DropdownField extends Field implements Filterable
|
||||
{
|
||||
public bool $required;
|
||||
/** @var array<int, string> */
|
||||
|
@ -87,4 +88,14 @@ class DropdownField extends Field
|
|||
{
|
||||
return app(SingleValueMatcher::class);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function filter($value): string
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return "{$this->key} IS NULL";
|
||||
}
|
||||
|
||||
return $this->key . ' = \'' . $value . '\'';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,4 +180,10 @@ abstract class Field extends Data
|
|||
{
|
||||
return app(SingleValueMatcher::class);
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
public function filter($value): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Form\Fields;
|
||||
|
||||
use App\Form\Contracts\Filterable;
|
||||
use App\Form\Matchers\Matcher;
|
||||
use App\Form\Matchers\SingleValueMatcher;
|
||||
use App\Form\Models\Form;
|
||||
|
@ -9,7 +10,7 @@ use App\Form\Models\Participant;
|
|||
use Faker\Generator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class RadioField extends Field
|
||||
class RadioField extends Field implements Filterable
|
||||
{
|
||||
public bool $required;
|
||||
/** @var array<int, string> */
|
||||
|
@ -87,4 +88,13 @@ class RadioField extends Field
|
|||
{
|
||||
return app(SingleValueMatcher::class);
|
||||
}
|
||||
|
||||
public function filter($value): string
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return "{$this->key} IS NULL";
|
||||
}
|
||||
|
||||
return $this->key . ' = \'' . $value . '\'';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,20 @@
|
|||
|
||||
namespace App\Form\Models;
|
||||
|
||||
use App\Form\Actions\UpdateParticipantSearchIndexAction;
|
||||
use App\Form\Data\ExportData;
|
||||
use App\Form\Data\FieldCollection;
|
||||
use App\Form\Data\FormConfigData;
|
||||
use App\Lib\Editor\Condition;
|
||||
use App\Lib\Editor\EditorData;
|
||||
use App\Lib\Sorting;
|
||||
use Cviebrock\EloquentSluggable\Sluggable;
|
||||
use Database\Factories\Form\Models\FormFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Spatie\Image\Manipulations;
|
||||
use Spatie\Image\Enums\Fit;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
@ -21,6 +24,7 @@ use Zoomyboy\MedialibraryHelper\DefersUploads;
|
|||
/** @todo replace editor content with EditorData cast */
|
||||
class Form extends Model implements HasMedia
|
||||
{
|
||||
/** @use HasFactory<FormFactory> */
|
||||
use HasFactory;
|
||||
use Sluggable;
|
||||
use InteractsWithMedia;
|
||||
|
@ -41,11 +45,12 @@ class Form extends Model implements HasMedia
|
|||
'needs_prevention' => 'boolean',
|
||||
'prevention_text' => EditorData::class,
|
||||
'prevention_conditions' => Condition::class,
|
||||
'from' => 'datetime',
|
||||
'to' => 'datetime',
|
||||
'registration_from' => 'datetime',
|
||||
'registration_until' => 'datetime',
|
||||
];
|
||||
|
||||
/** @var array<int, string> */
|
||||
public $dates = ['from', 'to', 'registration_from', 'registration_until'];
|
||||
|
||||
/**
|
||||
* @return SluggableConfig
|
||||
*/
|
||||
|
@ -57,7 +62,7 @@ class Form extends Model implements HasMedia
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<Participant>
|
||||
* @return HasMany<Participant, $this>
|
||||
*/
|
||||
public function participants(): HasMany
|
||||
{
|
||||
|
@ -73,7 +78,7 @@ class Form extends Model implements HasMedia
|
|||
->forceFileName(fn (Form $model, string $name) => $model->slug)
|
||||
->convert(fn () => 'jpg')
|
||||
->registerMediaConversions(function (Media $media) {
|
||||
$this->addMediaConversion('square')->fit(Manipulations::FIT_CROP, 400, 400);
|
||||
$this->addMediaConversion('square')->fit(Fit::Crop, 400, 400);
|
||||
});
|
||||
$this->addMediaCollection('mailattachments')
|
||||
->withDefaultProperties(fn () => [
|
||||
|
@ -155,7 +160,7 @@ class Form extends Model implements HasMedia
|
|||
if (is_null(data_get($model->meta, 'active_columns'))) {
|
||||
$model->setAttribute('meta', [
|
||||
'active_columns' => $model->getFields()->count() ? $model->getFields()->take(4)->pluck('key')->toArray() : null,
|
||||
'sorting' => $model->getFields()->count() ? [$model->getFields()->first()->key, 'asc'] : null,
|
||||
'sorting' => Sorting::by('id'),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
@ -168,5 +173,32 @@ class Form extends Model implements HasMedia
|
|||
return;
|
||||
}
|
||||
});
|
||||
|
||||
static::saved(function ($model) {
|
||||
UpdateParticipantSearchIndexAction::dispatch($model);
|
||||
});
|
||||
}
|
||||
|
||||
public function participantsSearchableAs(): string
|
||||
{
|
||||
return config('scout.prefix') . 'forms_' . $this->id . '_participants';
|
||||
}
|
||||
|
||||
public function defaultSorting(): Sorting
|
||||
{
|
||||
return Sorting::from($this->meta['sorting']);
|
||||
}
|
||||
|
||||
public function canRegister(): bool
|
||||
{
|
||||
if ($this->registration_from && $this->registration_from->gt(now())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->registration_until && $this->registration_until->lt(now())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Form\Models;
|
|||
|
||||
use App\Form\Data\FormConfigData;
|
||||
use App\Lib\Editor\EditorData;
|
||||
use Database\Factories\Form\Models\FormtemplateFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
|
@ -12,6 +13,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||
*/
|
||||
class Formtemplate extends Model
|
||||
{
|
||||
/** @use HasFactory<FormtemplateFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public $guarded = [];
|
||||
|
|
|
@ -8,17 +8,22 @@ use App\Form\Mails\ConfirmRegistrationMail;
|
|||
use App\Form\Scopes\ParticipantFilterScope;
|
||||
use App\Member\Member;
|
||||
use App\Prevention\Contracts\Preventable;
|
||||
use Database\Factories\Form\Models\ParticipantFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Laravel\Scout\Searchable;
|
||||
use stdClass;
|
||||
|
||||
class Participant extends Model implements Preventable
|
||||
{
|
||||
|
||||
/** @use HasFactory<ParticipantFactory> */
|
||||
use HasFactory;
|
||||
use Searchable;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
|
@ -28,7 +33,7 @@ class Participant extends Model implements Preventable
|
|||
];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Form, self>
|
||||
* @return BelongsTo<Form, $this>
|
||||
*/
|
||||
public function form(): BelongsTo
|
||||
{
|
||||
|
@ -36,7 +41,7 @@ class Participant extends Model implements Preventable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<self>
|
||||
* @return HasMany<Participant, $this>
|
||||
*/
|
||||
public function children(): HasMany
|
||||
{
|
||||
|
@ -44,16 +49,7 @@ class Participant extends Model implements Preventable
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Builder<self> $query
|
||||
* @return Builder<self>
|
||||
*/
|
||||
public function scopeWithFilter(Builder $query, ParticipantFilterScope $filter): Builder
|
||||
{
|
||||
return $filter->apply($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Member, self>
|
||||
* @return BelongsTo<Member, $this>
|
||||
*/
|
||||
public function member(): BelongsTo
|
||||
{
|
||||
|
@ -107,4 +103,15 @@ class Participant extends Model implements Preventable
|
|||
{
|
||||
return 'Nachweise erforderlich für deine Anmeldung zu ' . $this->form->name;
|
||||
}
|
||||
|
||||
public function searchableAs(): string
|
||||
{
|
||||
return $this->form->participantsSearchableAs();
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ class FormApiResource extends JsonResource
|
|||
'image' => $this->getMedia('headerImage')->first()->getFullUrl('square'),
|
||||
'is_active' => $this->is_active,
|
||||
'is_private' => $this->is_private,
|
||||
'can_register' => $this->getModel()->canRegister(),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -4,32 +4,76 @@ namespace App\Form\Scopes;
|
|||
|
||||
use App\Form\Models\Form;
|
||||
use App\Form\Models\Participant;
|
||||
use App\Lib\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Lib\ScoutFilter;
|
||||
use App\Lib\Sorting;
|
||||
use Illuminate\Support\Arr;
|
||||
use Laravel\Scout\Builder;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
|
||||
/**
|
||||
* @extends Filter<Participant>
|
||||
* @extends ScoutFilter<Participant>
|
||||
*/
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
#[MapOutputName(SnakeCaseMapper::class)]
|
||||
class ParticipantFilterScope extends Filter
|
||||
class ParticipantFilterScope extends ScoutFilter
|
||||
{
|
||||
|
||||
public static string $nan = 'deeb3ef4-d185-44b1-a4bc-0a4e7addebc3d8900c6f-a344-4afb-b54e-065ed483a7ba';
|
||||
private Form $form;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(
|
||||
public array $data = [],
|
||||
public string $search = '',
|
||||
public array $options = [],
|
||||
public ?int $parent = null,
|
||||
public ?Sorting $sort = null
|
||||
) {
|
||||
}
|
||||
|
||||
public static string $nan = 'deeb3ef4-d185-44b1-a4bc-0a4e7addebc3d8900c6f-a344-4afb-b54e-065ed483a7ba';
|
||||
public function getQuery(): Builder
|
||||
{
|
||||
$this->search = $this->search ?: '';
|
||||
|
||||
return Participant::search($this->search, function ($engine, string $query, array $options) {
|
||||
$filter = collect([]);
|
||||
|
||||
foreach ($this->form->getFields()->filterables() as $field) {
|
||||
if ($this->data[$field->key] === static::$nan) {
|
||||
continue;
|
||||
}
|
||||
$filter->push($field->filter($this->data[$field->key]));
|
||||
}
|
||||
|
||||
if ($this->parent === -1) {
|
||||
$filter->push('parent-id IS NULL');
|
||||
}
|
||||
|
||||
if ($this->parent !== null && $this->parent !== -1) {
|
||||
$filter->push('parent-id = ' . $this->parent);
|
||||
}
|
||||
|
||||
$options['filter'] = $filter->map(fn ($expression) => "($expression)")->implode(' AND ');
|
||||
|
||||
$options['sort'] = $this->sort->toMeilisearch();
|
||||
|
||||
return $engine->search($query, [...$this->options, ...$options]);
|
||||
})->within($this->form->participantsSearchableAs());
|
||||
}
|
||||
|
||||
public function setForm(Form $form): self
|
||||
{
|
||||
$this->form = $form;
|
||||
|
||||
if (is_null($this->sort)) {
|
||||
$this->sort = $this->form->defaultSorting();
|
||||
}
|
||||
|
||||
foreach ($form->getFields() as $field) {
|
||||
if (!Arr::has($this->data, $field->key)) {
|
||||
data_set($this->data, $field->key, static::$nan);
|
||||
|
@ -39,18 +83,10 @@ class ParticipantFilterScope extends Filter
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function apply(Builder $query): Builder
|
||||
public function parent(?int $parent): self
|
||||
{
|
||||
foreach ($this->data as $key => $value) {
|
||||
if ($value === static::$nan) {
|
||||
continue;
|
||||
}
|
||||
$query = $query->where('data->' . $key, $value);
|
||||
}
|
||||
$this->parent = $parent;
|
||||
|
||||
return $query;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Form\Transformers;
|
|||
use App\Form\Fields\Field;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\LaravelData\Support\DataProperty;
|
||||
use Spatie\LaravelData\Support\Transformation\TransformationContext;
|
||||
use Spatie\LaravelData\Transformers\Transformer;
|
||||
|
||||
class CollectionTransformer implements Transformer
|
||||
|
@ -18,7 +19,7 @@ class CollectionTransformer implements Transformer
|
|||
* @param Collection<int, Field> $value
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function transform(DataProperty $property, mixed $value): mixed
|
||||
public function transform(DataProperty $property, mixed $value, TransformationContext $context): mixed
|
||||
{
|
||||
return $value->toArray();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Form\Transformers;
|
|||
use App\Form\Fields\Field;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\LaravelData\Support\DataProperty;
|
||||
use Spatie\LaravelData\Support\Transformation\TransformationContext;
|
||||
use Spatie\LaravelData\Transformers\Transformer;
|
||||
|
||||
class FieldCollectionTransformer implements Transformer
|
||||
|
@ -14,7 +15,7 @@ class FieldCollectionTransformer implements Transformer
|
|||
* @param Collection<int, Field> $value
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function transform(DataProperty $property, mixed $value): mixed
|
||||
public function transform(DataProperty $property, mixed $value, TransformationContext $context): mixed
|
||||
{
|
||||
return $value->map(fn ($field) => [
|
||||
...$field->toArray(),
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
namespace App;
|
||||
|
||||
use App\Nami\HasNamiField;
|
||||
use Database\Factories\GenderFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Gender extends Model
|
||||
{
|
||||
use HasNamiField;
|
||||
|
||||
/** @use HasFactory<GenderFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public $fillable = ['name', 'nami_id'];
|
||||
|
@ -22,6 +25,15 @@ class Gender extends Model
|
|||
};
|
||||
}
|
||||
|
||||
public function getShortAttribute(): string
|
||||
{
|
||||
return match ($this->name) {
|
||||
'Männlich' => 'm',
|
||||
'Weiblich' => 'w',
|
||||
default => ''
|
||||
};
|
||||
}
|
||||
|
||||
public static function fromString(string $title): self
|
||||
{
|
||||
return self::firstWhere('name', $title);
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App;
|
|||
use App\Fileshare\Data\FileshareResourceData;
|
||||
use App\Group\Enums\Level;
|
||||
use App\Nami\HasNamiField;
|
||||
use Database\Factories\GroupFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
@ -12,6 +13,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
|
||||
class Group extends Model
|
||||
{
|
||||
/** @use HasFactory<GroupFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
|
||||
|
@ -24,19 +26,19 @@ class Group extends Model
|
|||
];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<static, self>
|
||||
* @return BelongsTo<Group, $this>
|
||||
*/
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(static::class, 'parent_id');
|
||||
return $this->belongsTo(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<self>
|
||||
* @return HasMany<Group, $this>
|
||||
*/
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this->hasMany(static::class, 'parent_id');
|
||||
return $this->hasMany(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
public static function booted(): void
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Group;
|
|||
use App\Group\Resources\GroupResource;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class GroupApiIndexAction
|
||||
|
@ -20,8 +21,12 @@ class GroupApiIndexAction
|
|||
return Group::get();
|
||||
}
|
||||
|
||||
public function asController(?Group $group = null): AnonymousResourceCollection
|
||||
public function asController(ActionRequest $request, ?Group $group = null): AnonymousResourceCollection
|
||||
{
|
||||
return GroupResource::collection($group ? $group->children()->withCount('children')->get() : Group::where('parent_id', null)->withCount('children')->get());
|
||||
return GroupResource::collection(
|
||||
$request->has('all')
|
||||
? Group::with('children')->get()
|
||||
: ($group ? $group->children()->withCount('children')->get() : Group::where('parent_id', null)->withCount('children')->get())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,5 +18,7 @@ class Authenticate extends Middleware
|
|||
if (!$request->expectsJson()) {
|
||||
return route('login');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Resources\InvoiceResource;
|
||||
use App\Invoice\Scopes\InvoiceFilterScope;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Laravel\Scout\Builder;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
|
||||
class InvoiceIndexAction
|
||||
|
@ -17,11 +17,11 @@ class InvoiceIndexAction
|
|||
|
||||
|
||||
/**
|
||||
* @return LengthAwarePaginator<Invoice>
|
||||
* @return Builder<Invoice>
|
||||
*/
|
||||
public function handle(InvoiceFilterScope $filter): LengthAwarePaginator
|
||||
public function handle(InvoiceFilterScope $filter): Builder
|
||||
{
|
||||
return Invoice::withFilter($filter)->with('positions')->paginate(15);
|
||||
return $filter->getQuery()->query(fn ($q) => $q->with('positions'));
|
||||
}
|
||||
|
||||
public function asController(ActionRequest $request): Response
|
||||
|
@ -32,7 +32,7 @@ class InvoiceIndexAction
|
|||
$filter = InvoiceFilterScope::fromRequest($request->input('filter', ''));
|
||||
|
||||
return Inertia::render('invoice/Index', [
|
||||
'data' => InvoiceResource::collection($this->handle($filter)),
|
||||
'data' => InvoiceResource::collection($this->handle($filter)->paginate(15)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class MassStoreAction
|
|||
foreach ($memberGroup as $members) {
|
||||
$invoice = Invoice::createForMember($members->first(), $members, $year);
|
||||
$invoice->save();
|
||||
$invoice->positions()->createMany($invoice->positions);
|
||||
$invoice->positions()->createMany($invoice->positions->toArray());
|
||||
$invoices->push($invoice->fresh('positions'));
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ abstract class InvoiceDocument extends Document
|
|||
|
||||
public static function fromInvoice(Invoice $invoice): self
|
||||
{
|
||||
return static::withoutMagicalCreationFrom([
|
||||
return static::factory()->withoutMagicalCreation()->from([
|
||||
'toName' => $invoice->to['name'],
|
||||
'toAddress' => $invoice->to['address'],
|
||||
'toZip' => $invoice->to['zip'],
|
||||
|
|
|
@ -8,27 +8,17 @@ use Lorisleiva\Actions\ActionRequest;
|
|||
|
||||
class InvoiceSettings extends LocalSettings implements Storeable
|
||||
{
|
||||
public string $from_long;
|
||||
|
||||
public string $from;
|
||||
|
||||
public string $mobile;
|
||||
|
||||
public string $email;
|
||||
|
||||
public string $website;
|
||||
|
||||
public string $address;
|
||||
|
||||
public string $place;
|
||||
|
||||
public string $zip;
|
||||
|
||||
public string $iban;
|
||||
|
||||
public string $bic;
|
||||
|
||||
public int $rememberWeeks;
|
||||
public ?string $from_long;
|
||||
public ?string $from;
|
||||
public ?string $mobile;
|
||||
public ?string $email;
|
||||
public ?string $website;
|
||||
public ?string $address;
|
||||
public ?string $place;
|
||||
public ?string $zip;
|
||||
public ?string $iban;
|
||||
public ?string $bic;
|
||||
public ?int $rememberWeeks;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
|
|
|
@ -11,16 +11,20 @@ use App\Invoice\RememberDocument;
|
|||
use App\Invoice\Scopes\InvoiceFilterScope;
|
||||
use App\Member\Member;
|
||||
use App\Payment\Subscription;
|
||||
use Database\Factories\Invoice\Models\InvoiceFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Laravel\Scout\Searchable;
|
||||
use stdClass;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
/** @use HasFactory<InvoiceFactory> */
|
||||
use HasFactory;
|
||||
use Searchable;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
|
@ -28,16 +32,12 @@ class Invoice extends Model
|
|||
'to' => 'json',
|
||||
'status' => InvoiceStatus::class,
|
||||
'via' => BillKind::class,
|
||||
];
|
||||
|
||||
/** @var array<int, string> */
|
||||
public $dates = [
|
||||
'sent_at',
|
||||
'last_remembered_at',
|
||||
'sent_at' => 'datetime',
|
||||
'last_remembered_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return HasMany<InvoicePosition>
|
||||
* @return HasMany<InvoicePosition, $this>
|
||||
*/
|
||||
public function positions(): HasMany
|
||||
{
|
||||
|
@ -49,7 +49,6 @@ class Invoice extends Model
|
|||
*/
|
||||
public static function createForMember(Member $member, Collection $members, int $year, Subscription $subscription = null): self
|
||||
{
|
||||
$subscription = $subscription ?: $member->subscription;
|
||||
$invoice = new self([
|
||||
'to' => [
|
||||
'name' => 'Familie ' . $member->lastname,
|
||||
|
@ -66,7 +65,8 @@ class Invoice extends Model
|
|||
|
||||
$positions = collect([]);
|
||||
foreach ($members as $member) {
|
||||
foreach ($subscription->children as $child) {
|
||||
$memberSubscription = $subscription ?: $member->subscription;
|
||||
foreach ($memberSubscription->children as $child) {
|
||||
$positions->push([
|
||||
'description' => str($child->name)->replace('{name}', $member->firstname . ' ' . $member->lastname)->replace('{year}', (string) $year),
|
||||
'price' => $child->amount,
|
||||
|
@ -122,15 +122,6 @@ class Invoice extends Model
|
|||
->where('last_remembered_at', '<=', now()->subWeeks($weeks));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<self> $query
|
||||
* @return Builder<self>
|
||||
*/
|
||||
public function scopeWithFilter(Builder $query, InvoiceFilterScope $filter): Builder
|
||||
{
|
||||
return $filter->apply($query);
|
||||
}
|
||||
|
||||
public function getMailRecipient(): stdClass
|
||||
{
|
||||
return (object) [
|
||||
|
@ -156,4 +147,20 @@ class Invoice extends Model
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the indexable data array for the model.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
return [
|
||||
'to' => implode(', ', $this->to),
|
||||
'usage' => $this->usage,
|
||||
'greeting' => $this->greeting,
|
||||
'mail_email' => $this->mail_email,
|
||||
'status' => $this->status->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,20 @@
|
|||
namespace App\Invoice\Models;
|
||||
|
||||
use App\Member\Member;
|
||||
use Database\Factories\Invoice\Models\InvoicePositionFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class InvoicePosition extends Model
|
||||
{
|
||||
/** @use HasFactory<InvoicePositionFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Member, self>
|
||||
* @return BelongsTo<Member, $this>
|
||||
*/
|
||||
public function member(): BelongsTo
|
||||
{
|
||||
|
@ -22,7 +24,7 @@ class InvoicePosition extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Invoice, self>
|
||||
* @return BelongsTo<Invoice, $this>
|
||||
*/
|
||||
public function invoice(): BelongsTo
|
||||
{
|
||||
|
|
|
@ -5,42 +5,43 @@ namespace App\Invoice\Scopes;
|
|||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Lib\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Lib\ScoutFilter;
|
||||
use Laravel\Scout\Builder;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
|
||||
/**
|
||||
* @extends Filter<Invoice>
|
||||
* @extends ScoutFilter<Invoice>
|
||||
*/
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
#[MapOutputName(SnakeCaseMapper::class)]
|
||||
class InvoiceFilterScope extends Filter
|
||||
class InvoiceFilterScope extends ScoutFilter
|
||||
{
|
||||
/**
|
||||
* @param array<int, string> $statuses
|
||||
*/
|
||||
public function __construct(
|
||||
public ?array $statuses = null,
|
||||
public ?string $search = null
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function apply(Builder $query): Builder
|
||||
{
|
||||
$query = $query->whereIn('status', $this->statuses);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function toDefault(): self
|
||||
{
|
||||
$this->statuses = $this->statuses === null ? InvoiceStatus::defaultVisibleValues()->toArray() : $this->statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQuery(): Builder
|
||||
{
|
||||
$this->search = $this->search ?: '';
|
||||
|
||||
return Invoice::search($this->search, function ($engine, string $query, array $options) {
|
||||
if (empty($this->statuses)) {
|
||||
$filter = 'status = "asa6aeruuni4BahC7Wei6ahm1"';
|
||||
} else {
|
||||
$filter = collect($this->statuses)->map(fn (string $status) => "status = \"$status\"")->join(' OR ');
|
||||
}
|
||||
return $engine->search($query, [...$options, 'filter' => $filter]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Spatie\LaravelData\Data;
|
|||
use Spatie\LaravelData\DataCollection;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
use Spatie\LaravelData\Attributes\DataCollectionOf;
|
||||
use Spatie\LaravelData\Support\EloquentCasts\DataEloquentCast;
|
||||
|
||||
class Condition extends Data
|
||||
{
|
||||
|
@ -20,12 +21,12 @@ class Condition extends Data
|
|||
|
||||
public static function fromMedia(Media $media): self
|
||||
{
|
||||
return $media->getCustomProperty('conditions') ? static::withoutMagicalCreationFrom($media->getCustomProperty('conditions')) : static::defaults();
|
||||
return $media->getCustomProperty('conditions') ? static::factory()->withoutMagicalCreation()->from($media->getCustomProperty('conditions')) : static::defaults();
|
||||
}
|
||||
|
||||
public static function defaults(): self
|
||||
{
|
||||
return static::withoutMagicalCreationFrom(['mode' => 'any', 'ifs' => []]);
|
||||
return static::factory()->withoutMagicalCreation()->from(['mode' => 'any', 'ifs' => []]);
|
||||
}
|
||||
|
||||
public function hasStatements(): bool
|
||||
|
@ -42,4 +43,13 @@ class Condition extends Data
|
|||
{
|
||||
return $this->mode === ConditionMode::ALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return DataEloquentCast<self>
|
||||
*/
|
||||
public static function castUsing(array $arguments): DataEloquentCast
|
||||
{
|
||||
return new DataEloquentCast(static::class, $arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ abstract class ConditionResolver
|
|||
*/
|
||||
public function filterBlock(array $block): bool
|
||||
{
|
||||
return $this->filterCondition(Condition::withoutMagicalCreationFrom([
|
||||
return $this->filterCondition(Condition::factory()->withoutMagicalCreation()->from([
|
||||
'mode' => data_get($block, 'tunes.condition.mode', 'any'),
|
||||
'ifs' => data_get($block, 'tunes.condition.ifs', []),
|
||||
]));
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Lib\Editor;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\Support\EloquentCasts\DataEloquentCast;
|
||||
|
||||
/** @todo replace blocks with actual block data classes */
|
||||
class EditorData extends Data implements Editorable
|
||||
|
@ -83,4 +84,13 @@ class EditorData extends Data implements Editorable
|
|||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return DataEloquentCast<self>
|
||||
*/
|
||||
public static function castUsing(array $arguments): DataEloquentCast
|
||||
{
|
||||
return new DataEloquentCast(static::class, $arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use Spatie\LaravelData\Data;
|
|||
|
||||
/**
|
||||
* @template T of Model
|
||||
* @property Builder $query
|
||||
* @property Builder<T> $query
|
||||
*/
|
||||
abstract class Filter extends Data
|
||||
{
|
||||
|
@ -36,11 +36,11 @@ abstract class Filter extends Data
|
|||
*/
|
||||
public static function fromPost(?array $post = null): static
|
||||
{
|
||||
return static::withoutMagicalCreationFrom($post ?: [])->toDefault();
|
||||
return static::factory()->withoutMagicalCreation()->from($post ?: [])->toDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static(self<T>)
|
||||
* @return static
|
||||
*/
|
||||
public function toDefault(): self
|
||||
{
|
||||
|
|
|
@ -8,15 +8,17 @@ use Spatie\LaravelData\Data;
|
|||
|
||||
/**
|
||||
* @template T of Model
|
||||
* @property Builder $query
|
||||
* @property Builder<T> $query
|
||||
*/
|
||||
abstract class ScoutFilter extends Data
|
||||
{
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
* @return Builder<T>
|
||||
*/
|
||||
abstract public function getQuery(): Builder;
|
||||
|
||||
/** @var Builder<T> */
|
||||
protected Builder $query;
|
||||
|
||||
/**
|
||||
|
@ -36,6 +38,6 @@ abstract class ScoutFilter extends Data
|
|||
*/
|
||||
public static function fromPost(?array $post = null): static
|
||||
{
|
||||
return static::withoutMagicalCreationFrom($post ?: []);
|
||||
return static::factory()->withoutMagicalCreation()->from($post ?: []);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class Sorting extends Data
|
||||
{
|
||||
public static function by(string $by): self
|
||||
{
|
||||
return static::factory()->withoutMagicalCreation()->from(['by' => $by]);
|
||||
}
|
||||
|
||||
public function __construct(public string $by, public bool $direction = false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function toMeilisearch(): array
|
||||
{
|
||||
return [$this->by . ':' . ($this->direction ? 'desc' : 'asc')];
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ class ResyncAction
|
|||
*/
|
||||
public function getResults(Maildispatcher $dispatcher): Collection
|
||||
{
|
||||
return FilterScope::fromPost($dispatcher->filter)->getQuery()->get()
|
||||
return FilterScope::fromPost($dispatcher->filter)->noPageLimit()->getQuery()->get()
|
||||
->filter(fn ($member) => $member->email || $member->email_parents)
|
||||
->map(fn ($member) => MailEntry::from(['email' => $member->email ?: $member->email_parents]))
|
||||
->unique(fn ($member) => $member->email);
|
||||
|
|
|
@ -8,7 +8,6 @@ use Illuminate\Database\Eloquent\Model;
|
|||
|
||||
class Localmaildispatcher extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasUuids;
|
||||
|
||||
public $guarded = [];
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Maildispatcher\Models;
|
||||
|
||||
use App\Mailgateway\Models\Mailgateway;
|
||||
use Database\Factories\Maildispatcher\Models\MaildispatcherFactory;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
@ -10,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
|
||||
class Maildispatcher extends Model
|
||||
{
|
||||
/** @use HasFactory<MaildispatcherFactory> */
|
||||
use HasFactory;
|
||||
use HasUuids;
|
||||
|
||||
|
@ -21,7 +23,7 @@ class Maildispatcher extends Model
|
|||
];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Mailgateway, self>
|
||||
* @return BelongsTo<Mailgateway, $this>
|
||||
*/
|
||||
public function gateway(): BelongsTo
|
||||
{
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
namespace App\Mailgateway\Models;
|
||||
|
||||
use App\Mailgateway\Casts\TypeCast;
|
||||
use Database\Factories\Mailgateway\Models\MailgatewayFactory;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Mailgateway extends Model
|
||||
{
|
||||
/** @use HasFactory<MailgatewayFactory> */
|
||||
use HasFactory;
|
||||
use HasUuids;
|
||||
|
||||
|
|
|
@ -68,9 +68,9 @@ abstract class Type
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
* @return mixed
|
||||
*/
|
||||
public function toResource(): array
|
||||
public function toResource()
|
||||
{
|
||||
return [
|
||||
'cls' => get_class($this),
|
||||
|
@ -93,8 +93,8 @@ abstract class Type
|
|||
}
|
||||
|
||||
$this->list($name, $domain)
|
||||
->filter(fn ($listEntry) => $results->doesntContain(fn ($r) => $r->is($listEntry)))
|
||||
->each(fn ($listEntry) => $this->remove($name, $domain, $listEntry->email));
|
||||
->filter(fn ($listEntry) => $results->doesntContain(fn ($r) => $r->is($listEntry)))
|
||||
->each(fn ($listEntry) => $this->remove($name, $domain, $listEntry->email));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,7 @@ class MailingList extends Data
|
|||
) {
|
||||
}
|
||||
|
||||
public static function factory(): MailinglistFactory
|
||||
public static function toFactory(): MailinglistFactory
|
||||
{
|
||||
return MailinglistFactory::new();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use App\Member\Member;
|
|||
use App\Setting\NamiSettings;
|
||||
use App\Subactivity;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Zoomyboy\LaravelNami\Data\BankAccount;
|
||||
use Zoomyboy\LaravelNami\Data\Member as NamiMember;
|
||||
|
||||
class NamiPutMemberAction
|
||||
|
@ -46,7 +47,17 @@ class NamiPutMemberAction
|
|||
'groupId' => $member->group->nami_id,
|
||||
'id' => $member->nami_id,
|
||||
'version' => $member->version,
|
||||
'keepdata' => false,
|
||||
'keepdata' => $member->keepdata,
|
||||
'bankAccount' => BankAccount::from([
|
||||
'bankName' => $member->bankAccount->bank_name,
|
||||
'id' => $member->bankAccount->nami_id,
|
||||
'memberId' => $member->mitgliedsnr,
|
||||
'iban' => $member->bankAccount->iban,
|
||||
'bic' => $member->bankAccount->bic,
|
||||
'blz' => $member->bankAccount->blz,
|
||||
'person' => $member->bankAccount->person,
|
||||
'accountNumber' => $member->bankAccount->account_number,
|
||||
]),
|
||||
]);
|
||||
$response = $api->putMember($namiMember, $activity ? $activity->nami_id : null, $subactivity ? $subactivity->nami_id : null);
|
||||
Member::withoutEvents(function () use ($response, $member) {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Member;
|
||||
|
||||
use Database\Factories\Member\BankAccountFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class BankAccount extends Model
|
||||
{
|
||||
/** @use HasFactory<BankAccountFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
public $primaryKey = 'member_id';
|
||||
}
|
|
@ -9,7 +9,8 @@ use Zoomyboy\LaravelNami\Data\Member as NamiMember;
|
|||
use Zoomyboy\LaravelNami\Data\MembershipEntry as NamiMembershipEntry;
|
||||
use Spatie\LaravelData\Attributes\DataCollectionOf;
|
||||
|
||||
class FullMember extends Data {
|
||||
class FullMember extends Data
|
||||
{
|
||||
/**
|
||||
* @param DataCollection<int, NamiCourse> $courses
|
||||
* @param DataCollection<int, NamiMembershipEntry> $memberships
|
||||
|
@ -20,6 +21,6 @@ class FullMember extends Data {
|
|||
public DataCollection $courses,
|
||||
#[DataCollectionOf(NamiMembershipEntry::class)]
|
||||
public DataCollection $memberships,
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace App\Member\Data;
|
|||
use App\Group;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\DataCollection;
|
||||
|
||||
class NestedGroup extends Data
|
||||
{
|
||||
|
@ -23,7 +22,7 @@ class NestedGroup extends Data
|
|||
$groups = collect([]);
|
||||
|
||||
foreach (Group::where('parent_id', $parentId)->orderBy('name')->get()->toBase() as $group) {
|
||||
$groups->push(['name' => str_repeat('- ', $level).$group->name, 'id' => $group->id]);
|
||||
$groups->push(['name' => str_repeat('- ', $level) . $group->name, 'id' => $group->id]);
|
||||
$groups = $groups->merge(static::forSelect($group->id, $level + 1));
|
||||
}
|
||||
|
||||
|
@ -31,10 +30,10 @@ class NestedGroup extends Data
|
|||
}
|
||||
|
||||
/**
|
||||
* @return DataCollection<int, static>
|
||||
* @return Collection<int, static>
|
||||
*/
|
||||
public static function cacheForSelect(): DataCollection
|
||||
public static function cacheForSelect(): Collection
|
||||
{
|
||||
return static::collection(static::forSelect());
|
||||
return static::collect(static::forSelect());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ class FilterScope extends ScoutFilter
|
|||
public array $exclude = [],
|
||||
public ?bool $hasFullAddress = null,
|
||||
public ?bool $hasBirthday = null,
|
||||
public ?bool $hasSvk = null,
|
||||
public ?bool $hasVk = null,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -85,6 +87,12 @@ class FilterScope extends ScoutFilter
|
|||
if ($this->hasBirthday === true) {
|
||||
$filter->push('birthday IS NOT NULL');
|
||||
}
|
||||
if ($this->hasSvk !== null) {
|
||||
$filter->push('has_svk = ' . ($this->hasSvk ? 'true' : 'false'));
|
||||
}
|
||||
if ($this->hasVk !== null) {
|
||||
$filter->push('has_vk = ' . ($this->hasVk ? 'true' : 'false'));
|
||||
}
|
||||
if ($this->ausstand === true) {
|
||||
$filter->push('ausstand > 0');
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ use Zoomyboy\Osm\Geolocatable;
|
|||
use Zoomyboy\Osm\HasGeolocation;
|
||||
use Zoomyboy\Phone\HasPhoneNumbers;
|
||||
use App\Prevention\Enums\Prevention;
|
||||
use Database\Factories\Member\MemberFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
|
||||
/**
|
||||
* @property string $subscription_name
|
||||
|
@ -42,6 +44,7 @@ class Member extends Model implements Geolocatable
|
|||
{
|
||||
use Notifiable;
|
||||
use HasNamiField;
|
||||
/** @use HasFactory<MemberFactory> */
|
||||
use HasFactory;
|
||||
use Sluggable;
|
||||
use Searchable;
|
||||
|
@ -56,12 +59,7 @@ class Member extends Model implements Geolocatable
|
|||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public static array $namiFields = ['firstname', 'lastname', 'joined_at', 'birthday', 'send_newspaper', 'address', 'zip', 'location', 'nickname', 'other_country', 'further_address', 'main_phone', 'mobile_phone', 'work_phone', 'fax', 'email', 'email_parents', 'gender_id', 'confession_id', 'region_id', 'country_id', 'fee_id', 'nationality_id', 'slug', 'subscription_id'];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $dates = ['try_created_at', 'recertified_at', 'joined_at', 'birthday', 'efz', 'ps_at', 'more_ps_at', 'without_education_at', 'without_efz_at'];
|
||||
public static array $namiFields = ['firstname', 'lastname', 'joined_at', 'birthday', 'send_newspaper', 'address', 'zip', 'location', 'nickname', 'other_country', 'further_address', 'main_phone', 'mobile_phone', 'work_phone', 'fax', 'email', 'email_parents', 'gender_id', 'confession_id', 'region_id', 'country_id', 'fee_id', 'nationality_id', 'slug', 'subscription_id', 'keepdata'];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
|
@ -80,8 +78,19 @@ class Member extends Model implements Geolocatable
|
|||
'multiply_pv' => 'boolean',
|
||||
'multiply_more_pv' => 'boolean',
|
||||
'is_leader' => 'boolean',
|
||||
'keepdata' => 'boolean',
|
||||
'bill_kind' => BillKind::class,
|
||||
'mitgliedsnr' => 'integer',
|
||||
|
||||
'try_created_at' => 'datetime',
|
||||
'recertified_at' => 'datetime',
|
||||
'joined_at' => 'datetime',
|
||||
'birthday' => 'datetime',
|
||||
'efz' => 'datetime',
|
||||
'ps_at' => 'datetime',
|
||||
'more_ps_at' => 'datetime',
|
||||
'without_education_at' => 'datetime',
|
||||
'without_efz_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -177,7 +186,7 @@ class Member extends Model implements Geolocatable
|
|||
|
||||
public function getAge(): ?int
|
||||
{
|
||||
return $this->birthday?->diffInYears(now());
|
||||
return $this->birthday ? intval($this->birthday->diffInYears(now())) : null;
|
||||
}
|
||||
|
||||
protected function getAusstand(): int
|
||||
|
@ -187,7 +196,7 @@ class Member extends Model implements Geolocatable
|
|||
|
||||
// ---------------------------------- Relations ----------------------------------
|
||||
/**
|
||||
* @return BelongsTo<Country, self>
|
||||
* @return BelongsTo<Country, $this>
|
||||
*/
|
||||
public function country(): BelongsTo
|
||||
{
|
||||
|
@ -195,7 +204,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Gender, self>
|
||||
* @return BelongsTo<Gender, $this>
|
||||
*/
|
||||
public function gender(): BelongsTo
|
||||
{
|
||||
|
@ -203,7 +212,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Region, self>
|
||||
* @return BelongsTo<Region, $this>
|
||||
*/
|
||||
public function region(): BelongsTo
|
||||
{
|
||||
|
@ -214,7 +223,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<InvoicePosition>
|
||||
* @return HasMany<InvoicePosition, $this>
|
||||
*/
|
||||
public function invoicePositions(): HasMany
|
||||
{
|
||||
|
@ -222,7 +231,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Confession, self>
|
||||
* @return BelongsTo<Confession, $this>
|
||||
*/
|
||||
public function confession(): BelongsTo
|
||||
{
|
||||
|
@ -230,7 +239,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Nationality, self>
|
||||
* @return BelongsTo<Nationality, $this>
|
||||
*/
|
||||
public function nationality(): BelongsTo
|
||||
{
|
||||
|
@ -238,7 +247,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Subscription, self>
|
||||
* @return BelongsTo<Subscription, $this>
|
||||
*/
|
||||
public function subscription(): BelongsTo
|
||||
{
|
||||
|
@ -246,7 +255,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Group, self>
|
||||
* @return BelongsTo<Group, $this>
|
||||
*/
|
||||
public function group(): BelongsTo
|
||||
{
|
||||
|
@ -254,7 +263,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<CourseMember>
|
||||
* @return HasMany<CourseMember, $this>
|
||||
*/
|
||||
public function courses(): HasMany
|
||||
{
|
||||
|
@ -262,7 +271,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<Membership>
|
||||
* @return HasMany<Membership, $this>
|
||||
*/
|
||||
public function memberships(): HasMany
|
||||
{
|
||||
|
@ -270,7 +279,7 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<Membership>
|
||||
* @return HasMany<Membership, $this>
|
||||
*/
|
||||
public function leaderMemberships(): HasMany
|
||||
{
|
||||
|
@ -278,21 +287,34 @@ class Member extends Model implements Geolocatable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<Membership>
|
||||
* @return HasMany<Membership, $this>
|
||||
*/
|
||||
public function ageGroupMemberships(): HasMany
|
||||
{
|
||||
return $this->memberships()->isAgeGroup()->active();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne<BankAccount, $this>
|
||||
*/
|
||||
public function bankAccount(): HasOne
|
||||
{
|
||||
return $this->hasOne(BankAccount::class);
|
||||
}
|
||||
|
||||
public static function booted()
|
||||
{
|
||||
static::created(function (self $model): void {
|
||||
$model->bankAccount()->create([]);
|
||||
});
|
||||
|
||||
static::deleting(function (self $model): void {
|
||||
$model->memberships->each->delete();
|
||||
$model->courses->each->delete();
|
||||
$model->invoicePositions->each(function ($position) {
|
||||
$position->delete();
|
||||
});
|
||||
$model->bankAccount()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -401,14 +423,14 @@ class Member extends Model implements Geolocatable
|
|||
return $query->where('group_id', $group->id);
|
||||
}
|
||||
|
||||
public static function fromVcard(string $url, string $data): static
|
||||
public static function fromVcard(string $url, string $data): self
|
||||
{
|
||||
$settings = app(NamiSettings::class);
|
||||
$card = Reader::read($data);
|
||||
[$lastname, $firstname] = $card->N->getParts();
|
||||
[$deprecated1, $deprecated2, $address, $location, $region, $zip, $country] = $card->ADR->getParts();
|
||||
|
||||
return new static([
|
||||
return new self([
|
||||
'joined_at' => now(),
|
||||
'send_newspaper' => false,
|
||||
'firstname' => $firstname,
|
||||
|
@ -536,6 +558,8 @@ class Member extends Model implements Geolocatable
|
|||
'bill_kind' => $this->bill_kind?->value,
|
||||
'group_id' => $this->group->id,
|
||||
'group_name' => $this->group->inner_name ?: $this->group->name,
|
||||
'has_vk' => $this->has_vk,
|
||||
'has_svk' => $this->has_svk,
|
||||
'links' => [
|
||||
'show' => route('member.show', ['member' => $this], false),
|
||||
'edit' => route('member.edit', ['member' => $this], false),
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Member;
|
||||
|
||||
use App\Country;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
@ -33,8 +32,7 @@ class MemberController extends Controller
|
|||
session()->put('menu', 'member');
|
||||
session()->put('title', 'Mitglied erstellen');
|
||||
|
||||
return \Inertia::render('member/VForm', [
|
||||
'data' => MemberResource::defaultModel(),
|
||||
return Inertia::render('member/VForm', [
|
||||
'mode' => 'create',
|
||||
'meta' => MemberResource::meta(),
|
||||
]);
|
||||
|
@ -52,7 +50,7 @@ class MemberController extends Controller
|
|||
session()->put('menu', 'member');
|
||||
session()->put('title', "Mitglied {$member->firstname} {$member->lastname} bearbeiten");
|
||||
|
||||
return \Inertia::render('member/VForm', [
|
||||
return Inertia::render('member/VForm', [
|
||||
'data' => new MemberResource($member),
|
||||
'mode' => 'edit',
|
||||
'conflict' => '1' === $request->query('conflict', '0'),
|
||||
|
|
|
@ -83,6 +83,7 @@ class MemberRequest extends FormRequest
|
|||
'other_country' => '',
|
||||
'salutation' => '',
|
||||
'comment' => '',
|
||||
'keepdata' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ class MemberResource extends JsonResource
|
|||
'lat' => $this->lat,
|
||||
'lon' => $this->lon,
|
||||
'group_name' => $this->group->name,
|
||||
'keepdata' => $this->keepdata,
|
||||
'links' => [
|
||||
'membership_index' => route('member.membership.index', ['member' => $this->getModel()]),
|
||||
'invoiceposition_index' => route('member.invoice-position.index', ['member' => $this->getModel()]),
|
||||
|
@ -162,54 +163,52 @@ class MemberResource extends JsonResource
|
|||
'activity_ids' => [],
|
||||
'subactivity_ids' => []
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function defaultModel(): array
|
||||
{
|
||||
return [
|
||||
'gender_id' => null,
|
||||
'salutation' => '',
|
||||
'nationality_id' => null,
|
||||
'firstname' => '',
|
||||
'lastname' => '',
|
||||
'address' => '',
|
||||
'further_address' => '',
|
||||
'zip' => '',
|
||||
'location' => '',
|
||||
'birthday' => '',
|
||||
'region_id' => null,
|
||||
'country_id' => Country::default(),
|
||||
'other_country' => '',
|
||||
'main_phone' => '',
|
||||
'mobile_phone' => '',
|
||||
'work_phone' => '',
|
||||
'children_phone' => '',
|
||||
'email' => '',
|
||||
'email_parents' => '',
|
||||
'fax' => '',
|
||||
'letter_address' => '',
|
||||
'bill_kind' => null,
|
||||
'subscription_id' => null,
|
||||
'has_nami' => false,
|
||||
'send_newspaper' => false,
|
||||
'joined_at' => now()->format('Y-m-d'),
|
||||
'comment' => '',
|
||||
'first_activity_id' => null,
|
||||
'first_subactivity_id' => null,
|
||||
'efz' => null,
|
||||
'ps_at' => null,
|
||||
'more_ps_at' => null,
|
||||
'without_education_at' => null,
|
||||
'recertified_at' => null,
|
||||
'without_efz_at' => null,
|
||||
'has_vk' => false,
|
||||
'has_svk' => false,
|
||||
'multiply_pv' => false,
|
||||
'multiply_more_pv' => false,
|
||||
'boolean_filter' => [
|
||||
['id' => true, 'name' => 'Ja'],
|
||||
['id' => false, 'name' => 'Nein'],
|
||||
],
|
||||
'default' => [
|
||||
'gender_id' => null,
|
||||
'salutation' => '',
|
||||
'nationality_id' => null,
|
||||
'firstname' => '',
|
||||
'lastname' => '',
|
||||
'address' => '',
|
||||
'further_address' => '',
|
||||
'zip' => '',
|
||||
'location' => '',
|
||||
'birthday' => '',
|
||||
'region_id' => null,
|
||||
'country_id' => Country::default(),
|
||||
'other_country' => '',
|
||||
'main_phone' => '',
|
||||
'mobile_phone' => '',
|
||||
'work_phone' => '',
|
||||
'children_phone' => '',
|
||||
'email' => '',
|
||||
'email_parents' => '',
|
||||
'fax' => '',
|
||||
'letter_address' => '',
|
||||
'bill_kind' => null,
|
||||
'subscription_id' => null,
|
||||
'has_nami' => false,
|
||||
'send_newspaper' => false,
|
||||
'joined_at' => now()->format('Y-m-d'),
|
||||
'comment' => '',
|
||||
'first_activity_id' => null,
|
||||
'first_subactivity_id' => null,
|
||||
'efz' => null,
|
||||
'ps_at' => null,
|
||||
'more_ps_at' => null,
|
||||
'without_education_at' => null,
|
||||
'recertified_at' => null,
|
||||
'without_efz_at' => null,
|
||||
'has_vk' => false,
|
||||
'has_svk' => false,
|
||||
'multiply_pv' => false,
|
||||
'multiply_more_pv' => false,
|
||||
'keepdata' => false,
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Activity;
|
|||
use App\Group;
|
||||
use App\Nami\HasNamiField;
|
||||
use App\Subactivity;
|
||||
use Database\Factories\Member\MembershipFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
@ -16,6 +17,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
*/
|
||||
class Membership extends Model
|
||||
{
|
||||
/** @use HasFactory<MembershipFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
|
||||
|
@ -30,7 +32,7 @@ class Membership extends Model
|
|||
];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Activity, self>
|
||||
* @return BelongsTo<Activity, $this>
|
||||
*/
|
||||
public function activity(): BelongsTo
|
||||
{
|
||||
|
@ -38,7 +40,7 @@ class Membership extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Group, self>
|
||||
* @return BelongsTo<Group, $this>
|
||||
*/
|
||||
public function group(): BelongsTo
|
||||
{
|
||||
|
@ -46,7 +48,7 @@ class Membership extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Subactivity, self>
|
||||
* @return BelongsTo<Subactivity, $this>
|
||||
*/
|
||||
public function subactivity(): BelongsTo
|
||||
{
|
||||
|
@ -54,7 +56,7 @@ class Membership extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Member, self>
|
||||
* @return BelongsTo<Member, $this>
|
||||
*/
|
||||
public function member(): BelongsTo
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@ class PsPendingBlock extends Block
|
|||
})
|
||||
->whereCurrentGroup()
|
||||
->orderByRaw('lastname, firstname')
|
||||
->whereHas('memberships', fn ($builder) => $builder->isLeader());
|
||||
->whereHas('memberships', fn ($builder) => $builder->isLeader()->active());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
namespace App;
|
||||
|
||||
use App\Nami\HasNamiField;
|
||||
use Database\Factories\NationalityFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Nationality extends Model
|
||||
{
|
||||
/** @use HasFactory<NationalityFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Payment;
|
||||
|
||||
use App\Fee;
|
||||
use Database\Factories\Payment\SubscriptionFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
@ -10,11 +11,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
|
||||
class Subscription extends Model
|
||||
{
|
||||
/** @use HasFactory<SubscriptionFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $fillable = ['name', 'fee_id'];
|
||||
|
||||
public function getAmount(): int
|
||||
|
@ -23,7 +22,7 @@ class Subscription extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Fee, self>
|
||||
* @return BelongsTo<Fee, $this>
|
||||
*/
|
||||
public function fee(): BelongsTo
|
||||
{
|
||||
|
@ -31,7 +30,7 @@ class Subscription extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<SubscriptionChild>
|
||||
* @return HasMany<SubscriptionChild, $this>
|
||||
*/
|
||||
public function children(): HasMany
|
||||
{
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
namespace App\Payment;
|
||||
|
||||
use Database\Factories\Payment\SubscriptionChildFactory;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SubscriptionChild extends Model
|
||||
{
|
||||
/** @use HasFactory<SubscriptionChildFactory> */
|
||||
use HasFactory;
|
||||
use HasUuids;
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ class AppServiceProvider extends ServiceProvider
|
|||
public function register()
|
||||
{
|
||||
JsonResource::withoutWrapping();
|
||||
Telescope::ignoreMigrations();
|
||||
|
||||
\Inertia::share('search', request()->query('search', ''));
|
||||
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
namespace App;
|
||||
|
||||
use Database\Factories\RegionFactory;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Region extends Model
|
||||
{
|
||||
/** @use HasFactory<RegionFactory> */
|
||||
use HasFactory;
|
||||
public $timestamps = false;
|
||||
|
||||
|
|
|
@ -4,19 +4,18 @@ namespace App;
|
|||
|
||||
use App\Nami\HasNamiField;
|
||||
use Cviebrock\EloquentSluggable\Sluggable;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Subactivity extends Model
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory;
|
||||
use HasNamiField;
|
||||
use Sluggable;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
public $fillable = ['is_age_group', 'is_filterable', 'slug', 'name', 'nami_id'];
|
||||
|
||||
/**
|
||||
|
@ -24,9 +23,6 @@ class Subactivity extends Model
|
|||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $casts = [
|
||||
'is_age_group' => 'boolean',
|
||||
'is_filterable' => 'boolean',
|
||||
|
@ -45,7 +41,7 @@ class Subactivity extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany<Activity>
|
||||
* @return BelongsToMany<Activity, $this>
|
||||
*/
|
||||
public function activities(): BelongsToMany
|
||||
{
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
namespace App;
|
||||
|
||||
use App\Auth\ResetPassword;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
echo "drop database scoutrobot;" | sudo mysql
|
||||
echo "create database scoutrobot;" | sudo mysql
|
||||
|
||||
ssh -l stammsilva zoomyboy.de "cd /usr/share/webapps/nami_silva && docker compose exec db mysqldump -udb -p$SCOUTROBOT_DB_PASSWORD db" > db.tmp
|
||||
sudo mysql scoutrobot < db.tmp
|
||||
ssh -l stamm-silva zoomyboy.de "cd /usr/share/webapps/adrema_silva && docker compose exec db mysqldump -udb -p$SCOUTROBOT_DB_PASSWORD db" > db.tmp
|
||||
sudo mysql adrema < db.tmp
|
||||
rm db.tmp
|
||||
|
||||
echo 'app(\App\Form\FormSettings::class)->fill(["registerUrl" => "http://stammsilva.test/anmeldung/{slug}/register", "clearCacheUrl" => "http://stammsilva.test/adrema/clear-cache"])->save();' | php artisan tinker
|
||||
|
|
20
bin/run
20
bin/run
|
@ -1,5 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
FILESHARE=false
|
||||
WEB=false
|
||||
|
||||
while getopts f opt; do
|
||||
case $opt in
|
||||
f) FILESHARE=true ;;
|
||||
w) WEB=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
tmux new-session -d -s test
|
||||
tmux send-keys -t test "a serve" Enter
|
||||
|
||||
|
@ -12,11 +24,11 @@ tmux send-keys -t test "SS start docker && duu socketi" Enter
|
|||
tmux new-window -t test
|
||||
tmux send-keys -t test "nrh" Enter
|
||||
|
||||
tmux new-window -t test
|
||||
tmux send-keys -t test "ggwdk && cd plugins/silva/adrema/assets/vendor/adrema-form && nrd" Enter
|
||||
$WEB && tmux new-window -t test
|
||||
$WEB && tmux send-keys -t test "ggwdk && cd plugins/silva/adrema/assets/vendor/adrema-form && nrd" Enter
|
||||
|
||||
tmux new-window -t test
|
||||
tmux send-keys -t test "cd tests/Fileshare && docker compose up" Enter
|
||||
$FILESHARE && tmux new-window -t test
|
||||
$FILESHARE && tmux send-keys -t test "cd tests/Fileshare && docker compose up" Enter
|
||||
|
||||
tmux attach-session -t test
|
||||
|
||||
|
|
|
@ -45,35 +45,34 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"php": "^8.3",
|
||||
"barryvdh/laravel-debugbar": "^3.9",
|
||||
"beyondcode/laravel-dump-server": "^1.8",
|
||||
"cviebrock/eloquent-sluggable": "^9.0",
|
||||
"cviebrock/eloquent-sluggable": "^11.0",
|
||||
"doctrine/dbal": "^3.1",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"inertiajs/inertia-laravel": "^0.6.9",
|
||||
"laravel/framework": "^9.0",
|
||||
"inertiajs/inertia-laravel": "^1.0",
|
||||
"laravel/framework": "^11.0",
|
||||
"laravel/horizon": "^5.0",
|
||||
"laravel/passport": "^11.8",
|
||||
"laravel/scout": "^9.8",
|
||||
"laravel/telescope": "^4.13",
|
||||
"laravel/pail": "^1.1",
|
||||
"laravel/passport": "^12.8",
|
||||
"laravel/scout": "^10.8",
|
||||
"laravel/telescope": "^5.0",
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/ui": "^3.0",
|
||||
"laravel/ui": "^4.0",
|
||||
"league/csv": "^9.9",
|
||||
"league/flysystem-webdav": "dev-master as 3.28.0",
|
||||
"lorisleiva/laravel-actions": "^2.4",
|
||||
"meilisearch/meilisearch-php": "^1.6",
|
||||
"monicahq/laravel-sabre": "^1.6",
|
||||
"nunomaduro/collision": "^6.1",
|
||||
"nunomaduro/collision": "^8.1",
|
||||
"phake/phake": "^4.2",
|
||||
"pusher/pusher-php-server": "^7.2",
|
||||
"spatie/laravel-data": "^3.0",
|
||||
"spatie/laravel-ignition": "^1.0",
|
||||
"spatie/laravel-medialibrary": "^10.0",
|
||||
"spatie/laravel-settings": "^2.2",
|
||||
"worksome/request-factories": "^2.5",
|
||||
"spatie/laravel-data": "^4.0",
|
||||
"spatie/laravel-ignition": "^2.0",
|
||||
"spatie/laravel-settings": "^3.0",
|
||||
"worksome/request-factories": "^3.0",
|
||||
"zoomyboy/laravel-nami": "dev-master",
|
||||
"zoomyboy/medialibrary-helper": "dev-master as 1.0",
|
||||
"league/flysystem-webdav": "dev-master as 3.28.0",
|
||||
"zoomyboy/osm": "1.0.3",
|
||||
"zoomyboy/phone": "^1.0",
|
||||
"zoomyboy/table-document": "dev-master as 1.0",
|
||||
|
@ -81,12 +80,12 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/envoy": "^2.8",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"larastan/larastan": "^2.0",
|
||||
"orchestra/testbench": "^7.0",
|
||||
"phpstan/phpstan-mockery": "^1.1",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
"orchestra/testbench": "^9.0",
|
||||
"pestphp/pest": "^3.0",
|
||||
"phpstan/phpstan-mockery": "^2.0"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue