Compare commits

..

No commits in common. "ff40b9e8058cb6b610bd9d79fc5144e1c5d949c7" and "8ec4e4eb59b9aa57cf64ae5707b5282b0ce3fc2f" have entirely different histories.

28 changed files with 16 additions and 663 deletions

View File

@ -3,7 +3,6 @@
namespace App\Console; namespace App\Console;
use App\Actions\DbMaintainAction; use App\Actions\DbMaintainAction;
use App\Form\Actions\PreventionRememberAction;
use App\Initialize\InitializeMembers; use App\Initialize\InitializeMembers;
use App\Invoice\Actions\InvoiceSendAction; use App\Invoice\Actions\InvoiceSendAction;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;
@ -20,7 +19,6 @@ class Kernel extends ConsoleKernel
InvoiceSendAction::class, InvoiceSendAction::class,
InitializeMembers::class, InitializeMembers::class,
DbMaintainAction::class, DbMaintainAction::class,
PreventionRememberAction::class,
]; ];
/** /**
@ -32,7 +30,6 @@ class Kernel extends ConsoleKernel
{ {
$schedule->command(DbMaintainAction::class)->daily(); $schedule->command(DbMaintainAction::class)->daily();
$schedule->command(InitializeMembers::class)->dailyAt('03:00'); $schedule->command(InitializeMembers::class)->dailyAt('03:00');
$schedule->command(PreventionRememberAction::class)->dailyAt('11:00');
} }
/** /**

View File

@ -35,7 +35,6 @@ class FormStoreAction
'is_active' => 'boolean', 'is_active' => 'boolean',
'is_private' => 'boolean', 'is_private' => 'boolean',
'export' => 'nullable|array', 'export' => 'nullable|array',
'needs_prevention' => 'present|boolean',
]; ];
} }

View File

@ -34,7 +34,6 @@ class FormUpdateAction
'is_active' => 'boolean', 'is_active' => 'boolean',
'is_private' => 'boolean', 'is_private' => 'boolean',
'export' => 'nullable|array', 'export' => 'nullable|array',
'needs_prevention' => 'present|boolean',
]; ];
} }

View File

@ -1,34 +0,0 @@
<?php
namespace App\Form\Actions;
use App\Form\Models\Participant;
use App\Prevention\Mails\PreventionRememberMail;
use Illuminate\Support\Facades\Mail;
use Lorisleiva\Actions\Concerns\AsAction;
class PreventionRememberAction
{
use AsAction;
public string $commandSignature = 'prevention:remember';
public function handle(): void
{
$query = Participant::whereHas('form', fn ($form) => $form->where('needs_prevention', true))
->where(
fn ($q) => $q
->where('last_remembered_at', '<=', now()->subWeeks(2))
->orWhereNull('last_remembered_at')
);
foreach ($query->get() as $participant) {
if (count($participant->preventions()) === 0) {
return;
}
Mail::send(new PreventionRememberMail($participant));
$participant->update(['last_remembered_at' => now()]);
}
}
}

View File

@ -35,7 +35,6 @@ class Form extends Model implements HasMedia
'is_active' => 'boolean', 'is_active' => 'boolean',
'is_private' => 'boolean', 'is_private' => 'boolean',
'export' => ExportData::class, 'export' => ExportData::class,
'needs_prevention' => 'boolean',
]; ];
/** @var array<int, string> */ /** @var array<int, string> */

View File

@ -6,17 +6,14 @@ use App\Form\Data\FieldCollection;
use App\Form\Data\FormConfigData; use App\Form\Data\FormConfigData;
use App\Form\Mails\ConfirmRegistrationMail; use App\Form\Mails\ConfirmRegistrationMail;
use App\Form\Scopes\ParticipantFilterScope; use App\Form\Scopes\ParticipantFilterScope;
use App\Member\Member;
use App\Prevention\Contracts\Preventable;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use stdClass;
class Participant extends Model implements Preventable class Participant extends Model
{ {
use HasFactory; use HasFactory;
@ -24,7 +21,6 @@ class Participant extends Model implements Preventable
public $casts = [ public $casts = [
'data' => 'json', 'data' => 'json',
'last_remembered_at' => 'datetime',
]; ];
/** /**
@ -52,14 +48,6 @@ class Participant extends Model implements Preventable
return $filter->apply($query); return $filter->apply($query);
} }
/**
* @return BelongsTo<Member, self>
*/
public function member(): BelongsTo
{
return $this->belongsTo(Member::class);
}
public function getFields(): FieldCollection public function getFields(): FieldCollection
{ {
return FieldCollection::fromRequest($this->form, $this->data); return FieldCollection::fromRequest($this->form, $this->data);
@ -82,29 +70,6 @@ class Participant extends Model implements Preventable
return; return;
} }
Mail::to($this->getMailRecipient())->queue(new ConfirmRegistrationMail($this)); Mail::to($this->getFields()->getMailRecipient())->queue(new ConfirmRegistrationMail($this));
}
public function preventableLayout(): string
{
return 'mail.prevention.prevention-remember-participant';
}
/**
* @inheritdoc
*/
public function preventions(): array
{
return $this->member?->preventions($this->form->from) ?: [];
}
public function getMailRecipient(): stdClass
{
return $this->getFields()->getMailRecipient();
}
public function preventableSubject(): string
{
return 'Nachweise erforderlich für deine Anmeldung zu ' . $this->form->name;
} }
} }

View File

@ -49,7 +49,6 @@ class FormResource extends JsonResource
'is_private' => $this->is_private, 'is_private' => $this->is_private,
'has_nami_field' => $this->getFields()->hasNamiField(), 'has_nami_field' => $this->getFields()->hasNamiField(),
'export' => $this->export, 'export' => $this->export,
'needs_prevention' => $this->needs_prevention,
'links' => [ 'links' => [
'participant_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => null]), 'participant_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => null]),
'participant_root_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => -1]), 'participant_root_index' => route('form.participant.index', ['form' => $this->getModel(), 'parent' => -1]),

View File

@ -23,6 +23,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Searchable; use Laravel\Scout\Searchable;
use Sabre\VObject\Component\VCard; use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader; use Sabre\VObject\Reader;
@ -32,7 +33,6 @@ use Zoomyboy\Osm\Coordinate;
use Zoomyboy\Osm\Geolocatable; use Zoomyboy\Osm\Geolocatable;
use Zoomyboy\Osm\HasGeolocation; use Zoomyboy\Osm\HasGeolocation;
use Zoomyboy\Phone\HasPhoneNumbers; use Zoomyboy\Phone\HasPhoneNumbers;
use App\Prevention\Enums\Prevention;
/** /**
* @property string $subscription_name * @property string $subscription_name
@ -341,37 +341,6 @@ class Member extends Model implements Geolocatable
return $query->where('bill_kind', '!=', null)->where('subscription_id', '!=', null); return $query->where('bill_kind', '!=', null)->where('subscription_id', '!=', null);
} }
/**
* @return array<int, Prevention>
*/
public function preventions(?Carbon $date = null): array
{
$date = $date ?: now();
if (!$this->isLeader()) {
return [];
}
/** @var array<int, Prevention> */
$preventions = [];
if ($this->efz === null || $this->efz->diffInYears($date) >= 5) {
$preventions[] = Prevention::EFZ;
}
if ($this->more_ps_at === null) {
if ($this->ps_at === null || $this->ps_at->diffInYears($date) >= 5) {
$preventions[] = Prevention::PS;
}
} else {
if ($this->more_ps_at === null || $this->more_ps_at->diffInYears($date) >= 5) {
$preventions[] = Prevention::MOREPS;
}
}
return $preventions;
}
/** /**
* @param Builder<self> $query * @param Builder<self> $query
* *

View File

@ -1,19 +0,0 @@
<?php
namespace App\Prevention\Actions;
use Inertia\Inertia;
use Lorisleiva\Actions\Concerns\AsAction;
class PreventionIndexAction
{
use AsAction;
public function handle()
{
session()->put('menu', 'setting');
session()->put('title', 'Prävention');
return Inertia::render('setting/Prevention');
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace App\Prevention\Actions;
use App\Prevention\PreventionSettings;
use Illuminate\Http\JsonResponse;
use Lorisleiva\Actions\Concerns\AsAction;
class SettingApiAction
{
use AsAction;
public function handle(): JsonResponse
{
return response()->json([
'data' => app(PreventionSettings::class)->toArray(),
]);
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Prevention\Actions;
use App\Lib\Events\Succeeded;
use App\Prevention\PreventionSettings;
use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;
class SettingStoreAction
{
use AsAction;
public function rules(): array
{
return [
'formmail' => 'array',
];
}
public function handle(ActionRequest $request): void
{
app(PreventionSettings::class)->fill($request->validated())->save();
Succeeded::message('Einstellungen gespeichert.')->dispatch();
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace App\Prevention\Contracts;
use App\Prevention\Enums\Prevention;
use stdClass;
interface Preventable
{
public function preventableLayout(): string;
public function preventableSubject(): string;
/**
* @return array<int, Prevention>
*/
public function preventions(): array;
public function getMailRecipient(): stdClass;
}

View File

@ -1,19 +0,0 @@
<?php
namespace App\Prevention\Enums;
enum Prevention
{
case EFZ;
case PS;
case MOREPS;
public function text(): string
{
return match ($this) {
static::EFZ => 'erweitertes Führungszeugnis',
static::PS => 'Präventionsschulung Basis Plus',
static::MOREPS => 'Präventionsschulung (Auffrischung)',
};
}
}

View File

@ -1,63 +0,0 @@
<?php
namespace App\Prevention\Mails;
use App\Invoice\InvoiceSettings;
use App\Prevention\Contracts\Preventable;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class PreventionRememberMail extends Mailable
{
use Queueable, SerializesModels;
public InvoiceSettings $settings;
public string $documents;
/**
* Create a new message instance.
*/
public function __construct(public Preventable $preventable)
{
$this->settings = app(InvoiceSettings::class);
$this->documents = collect($preventable->preventions())->map(fn ($prevention) => "* {$prevention->text()}")->implode("\n");
}
/**
* Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
return (new Envelope(
subject: $this->preventable->preventableSubject(),
))->to($this->preventable->getMailRecipient()->email, $this->preventable->getMailRecipient()->name);
}
/**
* Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/
public function content()
{
return new Content(
markdown: $this->preventable->preventableLayout(),
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Prevention;
use App\Prevention\Actions\PreventionIndexAction;
use App\Setting\Contracts\Indexable;
use App\Setting\LocalSettings;
class PreventionSettings extends LocalSettings implements Indexable
{
public array $formmail;
public static function group(): string
{
return 'prevention';
}
public static function slug(): string
{
return 'prevention';
}
public static function indexAction(): string
{
return PreventionIndexAction::class;
}
public static function title(): string
{
return 'Prävention';
}
}

View File

@ -7,7 +7,6 @@ use App\Form\FormSettings;
use App\Invoice\InvoiceSettings; use App\Invoice\InvoiceSettings;
use App\Mailgateway\MailgatewaySettings; use App\Mailgateway\MailgatewaySettings;
use App\Module\ModuleSettings; use App\Module\ModuleSettings;
use App\Prevention\PreventionSettings;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
class SettingServiceProvider extends ServiceProvider class SettingServiceProvider extends ServiceProvider
@ -35,6 +34,5 @@ class SettingServiceProvider extends ServiceProvider
app(SettingFactory::class)->register(NamiSettings::class); app(SettingFactory::class)->register(NamiSettings::class);
app(SettingFactory::class)->register(FormSettings::class); app(SettingFactory::class)->register(FormSettings::class);
app(SettingFactory::class)->register(FileshareSettings::class); app(SettingFactory::class)->register(FileshareSettings::class);
app(SettingFactory::class)->register(PreventionSettings::class);
} }
} }

View File

@ -1,35 +0,0 @@
<?php
use App\Form\Models\Participant;
use App\Member\Member;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('participants', function (Blueprint $table) {
$table->datetime('last_remembered_at')->nullable();
});
Schema::table('forms', function (Blueprint $table) {
$table->boolean('needs_prevention')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
};

View File

@ -1,11 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
return new class extends SettingsMigration
{
public function up(): void
{
$this->migrator->add('prevention.formmail', ['time' => 1, 'blocks' => []]);
}
};

View File

@ -8,18 +8,22 @@
</div> </div>
</div> </div>
<ui-popup v-if="condition !== null" heading="Bedingungen" @close=" <ui-popup
v-if="condition !== null"
heading="Bedingungen"
@close="
condition.resolve(condition.data); condition.resolve(condition.data);
condition = null; condition = null;
"> "
>
<slot name="conditions" :data="condition.data" :resolve="condition.resolve" :reject="condition.reject"></slot> <slot name="conditions" :data="condition.data" :resolve="condition.resolve" :reject="condition.reject"></slot>
</ui-popup> </ui-popup>
</div> </div>
</template> </template>
<script setup> <script setup>
import { debounce } from 'lodash'; import {debounce} from 'lodash';
import { onMounted, ref } from 'vue'; import {onMounted, ref} from 'vue';
import EditorJS from '@editorjs/editorjs'; import EditorJS from '@editorjs/editorjs';
import Header from '@editorjs/header'; import Header from '@editorjs/header';
import Paragraph from '@editorjs/paragraph'; import Paragraph from '@editorjs/paragraph';
@ -28,7 +32,7 @@ import Alert from 'editorjs-alert';
import useFieldSize from '../../composables/useFieldSize.js'; import useFieldSize from '../../composables/useFieldSize.js';
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const { fieldAppearance, paddingX, paddingY, sizeClass } = useFieldSize(); const {fieldAppearance, paddingX, paddingY, sizeClass} = useFieldSize();
const props = defineProps({ const props = defineProps({
required: { required: {
@ -84,7 +88,7 @@ async function openPopup(data) {
} }
class ConditionTune { class ConditionTune {
constructor({ api, data, config, block }) { constructor({api, data, config, block}) {
this.api = api; this.api = api;
this.data = data || { this.data = data || {
mode: 'all', mode: 'all',

View File

@ -43,7 +43,6 @@
<f-text id="name" v-model="single.name" class="grow" label="Name" required></f-text> <f-text id="name" v-model="single.name" class="grow" label="Name" required></f-text>
<f-switch id="is_active" v-model="single.is_active" name="is_active" label="Aktiv"></f-switch> <f-switch id="is_active" v-model="single.is_active" name="is_active" label="Aktiv"></f-switch>
<f-switch id="is_private" v-model="single.is_private" name="is_private" label="Privat"></f-switch> <f-switch id="is_private" v-model="single.is_private" name="is_private" label="Privat"></f-switch>
<f-switch id="needs_prevention" v-model="single.needs_prevention" name="needs_prevention" label="Prävention"></f-switch>
</div> </div>
<f-singlefile <f-singlefile
id="header_image" id="header_image"

View File

@ -1,37 +0,0 @@
<template>
<page-layout>
<template #right>
<f-save-button form="preventionform"></f-save-button>
</template>
<setting-layout v-if="loaded">
<form id="preventionform" class="grow p-6" @submit.prevent="submit">
<div class="col-span-full text-gray-100 mb-3">
<p class="text-sm">Hier kannst du Einstellungen zu Prävention setzen.</p>
</div>
<div class="grid gap-4 mt-2">
<f-editor id="frommail" v-model="data.formmail" label="E-Mail für Veranstaltungs-TN"></f-editor>
</div>
</form>
</setting-layout>
</page-layout>
</template>
<script lang="js" setup>
import { ref } from 'vue';
import { useApiIndex } from '../../composables/useApiIndex.js';
import SettingLayout from '../setting/Layout.vue';
const { axios, data, reload } = useApiIndex('/api/prevention', 'prevention');
const loaded = ref(false);
async function load() {
await reload();
loaded.value = true;
}
async function submit() {
await axios.post('/api/prevention', { ...data.value });
}
load();
</script>

View File

@ -1,17 +0,0 @@
@component('mail::message')
# Hallo {{ $preventable->member->fullname }},
Du hast dich für die Veranstaltung __{{$preventable->form->name}}__ angemeldet.
Damit du an der Veranstaltung als leitende oder helfende Person teilnehmen kannst, ist noch folgendes einzureichen oder zu beachten.
{!! $documents !!}
@component('mail::subcopy')
Herzliche Grüße und gut Pfad
{{$settings->from_long}}
@endcomponent
@endcomponent

View File

@ -3,13 +3,9 @@
use App\Contribution\Actions\GenerateApiAction as ContributionGenerateApiAction; use App\Contribution\Actions\GenerateApiAction as ContributionGenerateApiAction;
use App\Form\Actions\FormApiListAction; use App\Form\Actions\FormApiListAction;
use App\Form\Actions\RegisterAction; use App\Form\Actions\RegisterAction;
use App\Prevention\Actions\SettingStoreAction as PreventionStoreAction;
use App\Group\Actions\GroupApiIndexAction; use App\Group\Actions\GroupApiIndexAction;
use App\Prevention\Actions\SettingApiAction;
Route::post('/contribution-generate', ContributionGenerateApiAction::class)->name('api.contribution.generate')->middleware('client:contribution-generate'); Route::post('/contribution-generate', ContributionGenerateApiAction::class)->name('api.contribution.generate')->middleware('client:contribution-generate');
Route::post('/form/{form}/register', RegisterAction::class)->name('form.register'); Route::post('/form/{form}/register', RegisterAction::class)->name('form.register');
Route::get('/group/{group?}', GroupApiIndexAction::class)->name('api.group'); Route::get('/group/{group?}', GroupApiIndexAction::class)->name('api.group');
Route::get('/form', FormApiListAction::class)->name('api.form.index'); Route::get('/form', FormApiListAction::class)->name('api.form.index');
Route::get('/prevention', SettingApiAction::class)->name('api.prevention.index');
Route::post('/prevention', PreventionStoreAction::class)->name('api.prevention.store');

View File

@ -52,7 +52,6 @@ class FormIndexActionTest extends FormTestCase
->assertInertiaPath('data.data.0.is_active', true) ->assertInertiaPath('data.data.0.is_active', true)
->assertInertiaPath('data.data.0.is_private', false) ->assertInertiaPath('data.data.0.is_private', false)
->assertInertiaPath('data.data.0.registration_from', '2023-05-06 04:00:00') ->assertInertiaPath('data.data.0.registration_from', '2023-05-06 04:00:00')
->assertInertiaPath('data.data.0.needs_prevention', false)
->assertInertiaPath('data.data.0.registration_until', '2023-04-01 05:00:00') ->assertInertiaPath('data.data.0.registration_until', '2023-04-01 05:00:00')
->assertInertiaPath('data.data.0.links.participant_index', route('form.participant.index', ['form' => $form])) ->assertInertiaPath('data.data.0.links.participant_index', route('form.participant.index', ['form' => $form]))
->assertInertiaPath('data.data.0.links.export', route('form.export', ['form' => $form])) ->assertInertiaPath('data.data.0.links.export', route('form.export', ['form' => $form]))

View File

@ -49,7 +49,6 @@ class FormRequest extends RequestFactory
'header_image' => $this->getHeaderImagePayload(str()->uuid() . '.jpg'), 'header_image' => $this->getHeaderImagePayload(str()->uuid() . '.jpg'),
'mailattachments' => [], 'mailattachments' => [],
'export' => ExportData::from([])->toArray(), 'export' => ExportData::from([])->toArray(),
'needs_prevention' => $this->faker->boolean(),
]; ];
} }

View File

@ -6,6 +6,7 @@ use App\Fileshare\Data\FileshareResourceData;
use App\Form\Data\ExportData; use App\Form\Data\ExportData;
use App\Form\Models\Form; use App\Form\Models\Form;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Http;
class FormUpdateActionTest extends FormTestCase class FormUpdateActionTest extends FormTestCase
{ {
@ -123,14 +124,4 @@ class FormUpdateActionTest extends FormTestCase
$this->patchJson(route('form.update', ['form' => $form]), $payload)->assertSessionDoesntHaveErrors()->assertOk(); $this->patchJson(route('form.update', ['form' => $form]), $payload)->assertSessionDoesntHaveErrors()->assertOk();
$this->assertEquals(['firstname', 'geb', 'lastname'], $form->fresh()->meta['active_columns']); $this->assertEquals(['firstname', 'geb', 'lastname'], $form->fresh()->meta['active_columns']);
} }
public function testItUpdatesPrevention(): void
{
$this->login()->loginNami()->withoutExceptionHandling();
$form = Form::factory()->create();
$payload = FormRequest::new()->state(['needs_prevention' => true])->create();
$this->patchJson(route('form.update', ['form' => $form]), $payload);
$this->assertTrue($form->fresh()->needs_prevention);
}
} }

View File

@ -1,184 +0,0 @@
<?php
namespace Tests\Feature\Member;
use App\Prevention\Enums\Prevention;
use App\Form\Actions\PreventionRememberAction;
use App\Form\Enums\NamiType;
use App\Form\Models\Form;
use App\Form\Models\Participant;
use App\Invoice\InvoiceSettings;
use App\Prevention\Mails\PreventionRememberMail;
use App\Member\Member;
use App\Member\Membership;
use Generator;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Mail;
use Tests\Lib\CreatesFormFields;
use Tests\TestCase;
class PreventionTest extends TestCase
{
use DatabaseTransactions;
use CreatesFormFields;
public function testItRemembersWhenNotRememberedYet(): void
{
Mail::fake();
$form = $this->createForm();
$participant = $this->createParticipant($form);
PreventionRememberAction::run();
$this->assertEquals(now()->format('Y-m-d'), $participant->fresh()->last_remembered_at->format('Y-m-d'));
}
public function testItRemembersWhenRememberIsDue(): void
{
Mail::fake();
$form = $this->createForm();
$participant = tap($this->createParticipant($form), fn ($p) => $p->update(['last_remembered_at' => now()->subWeeks(3)]));
PreventionRememberAction::run();
$this->assertEquals(now()->format('Y-m-d'), $participant->fresh()->last_remembered_at->format('Y-m-d'));
}
public function testItDoesntRememberWhenRememberingIsNotDue(): void
{
Mail::fake();
$form = $this->createForm();
$participant = tap($this->createParticipant($form), fn ($p) => $p->update(['last_remembered_at' => now()->subWeeks(1)]));
PreventionRememberAction::run();
$this->assertEquals(now()->subWeeks(1)->format('Y-m-d'), $participant->fresh()->last_remembered_at->format('Y-m-d'));
}
public function testItDoesntRememberWhenFormDoesntNeedPrevention(): void
{
Mail::fake();
$form = tap($this->createForm(), fn ($form) => $form->update(['needs_prevention' => false]));
$participant = $this->createParticipant($form);
PreventionRememberAction::run();
$this->assertNull($participant->fresh()->last_remembered_at);
}
public function testItDoesntRememberWhenParticipantDoesntHaveMember(): void
{
Mail::fake();
$form = $this->createForm();
$participant = $this->createParticipant($form);
$participant->member->delete();
PreventionRememberAction::run();
$this->assertNull($participant->fresh()->last_remembered_at);
}
public function testItDoesntRememberWhenMemberIsNotALeader(): void
{
Mail::fake();
$form = $this->createForm();
$participant = $this->createParticipant($form);
$participant->member->memberships->each->delete();
PreventionRememberAction::run();
$this->assertNull($participant->fresh()->last_remembered_at);
}
protected function attributes(): Generator
{
yield [
'attrs' => ['efz' => null, 'ps_at' => now()],
'preventions' => [Prevention::EFZ]
];
yield [
'attrs' => ['efz' => now(), 'ps_at' => null],
'preventions' => [Prevention::PS]
];
yield [
'attrs' => ['efz' => now()->subDay(), 'ps_at' => now()],
'preventions' => []
];
yield [
'attrs' => ['efz' => now(), 'ps_at' => now()->subDay()],
'preventions' => []
];
yield [
'attrs' => ['efz' => now()->subYears(5)->subDay(), 'ps_at' => now()],
'preventions' => [Prevention::EFZ]
];
yield [
'attrs' => ['efz' => now(), 'ps_at' => now()->subYears(5)->subDay()],
'preventions' => [Prevention::PS]
];
yield [
'attrs' => ['efz' => now(), 'ps_at' => now()->subYears(5)->subDay(), 'more_ps_at' => now()],
'preventions' => []
];
yield [
'attrs' => ['efz' => now(), 'ps_at' => now()->subYears(15), 'more_ps_at' => now()->subYears(5)->subDay()],
'preventions' => [Prevention::MOREPS],
];
}
/**
* @param array<int, Prevention> $preventions
* @param array<string, mixed> $memberAttributes
* @dataProvider attributes
*/
public function testItRemembersMember(array $memberAttributes, array $preventions): void
{
Mail::fake();
$form = $this->createForm();
$participant = $this->createParticipant($form);
$participant->member->update($memberAttributes);
PreventionRememberAction::run();
if (count($preventions)) {
Mail::assertSent(PreventionRememberMail::class, fn ($mail) => $mail->preventable->preventions() === $preventions);
$this->assertNotNull($participant->fresh()->last_remembered_at);
} else {
Mail::assertNotSent(PreventionRememberMail::class);
$this->assertNull($participant->fresh()->last_remembered_at);
}
}
public function testItRendersMail(): void
{
InvoiceSettings::fake(['from_long' => 'Stamm Beispiel']);
$form = $this->createForm();
$participant = $this->createParticipant($form);
(new PreventionRememberMail($participant))
->assertSeeInText($participant->member->firstname)
->assertSeeInText($participant->form->name)
->assertSeeInText('erweitertes Führungszeugnis')
->assertSeeInText('Stamm Beispiel')
->assertSeeInText($participant->member->lastname);
}
protected function createForm(): Form
{
return Form::factory()->fields([
$this->textField('vorname')->namiType(NamiType::FIRSTNAME),
])->create(['needs_prevention' => true]);
}
protected function createParticipant(Form $form): Participant
{
return Participant::factory()->for($form)->data(['vorname' => 'Max'])->for(Member::factory()->defaults()->has(Membership::factory()->inLocal('€ LeiterIn', 'Wölfling')))->create();
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace Tests\Feature\Prevention;
use App\Prevention\PreventionSettings;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\EditorRequestFactory;
use Tests\TestCase;
class SettingTest extends TestCase
{
use DatabaseTransactions;
public function testItOpensSettingsPage(): void
{
$this->login()->loginNami();
$this->get('/setting/prevention')->assertComponent('prevention/Index')->assertOk();
}
public function testItReceivesSettings(): void
{
$this->login()->loginNami();
$text = EditorRequestFactory::new()->text(50, 'lorem ipsum')->create();
app(PreventionSettings::class)->fill(['formmail' => $text])->save();
$this->get('/api/prevention')
->assertJsonPath('data.formmail.blocks.0.data.text', 'lorem ipsum');
}
public function testItStoresSettings(): void
{
$this->login()->loginNami();
$text = EditorRequestFactory::new()->text(50, 'new lorem')->create();
$this->post('/api/prevention', ['formmail' => $text])->assertOk();
$this->assertEquals($text, app(PreventionSettings::class)->formmail);
}
}