Compare commits
17 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
1d575a8476 | |
|
|
5117360c03 | |
|
|
502ba20b6d | |
|
|
56af6f4cbf | |
|
|
ac88df2cad | |
|
|
adc78a65e7 | |
|
|
c7618b0545 | |
|
|
e5c2599846 | |
|
|
3a80e3bcee | |
|
|
4abdac75f6 | |
|
|
1e4361c709 | |
|
|
8924774ed0 | |
|
|
f0ed9185ba | |
|
|
f9deb56d22 | |
|
|
8b8f598507 | |
|
|
28a4e70929 | |
|
|
28b47a2910 |
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,5 +1,18 @@
|
||||||
# Letzte Änderungen
|
# Letzte Änderungen
|
||||||
|
|
||||||
|
### 1.12.23
|
||||||
|
|
||||||
|
- Veranstaltungs-Teilnehmer*innen können nun abgemeldet statt vollständig gelöscht werden.
|
||||||
|
- Beim Excel-Export wird eine Spalte "ID" angezeigt mit der ID des TNs
|
||||||
|
|
||||||
|
### 1.12.22
|
||||||
|
|
||||||
|
- Bei Mitgliedern wird nun auch die geschäftliche tel-nr aktualisiert
|
||||||
|
|
||||||
|
### 1.12.21
|
||||||
|
|
||||||
|
- Reply-To-Header bei Versand von Prävention-und-Veranstaltungs-Mails kann nun eingestellt werden
|
||||||
|
|
||||||
### 1.12.20
|
### 1.12.20
|
||||||
|
|
||||||
- Nachnelde-Link kann nun erstellt werden für Veranstaltungen
|
- Nachnelde-Link kann nun erstellt werden für Veranstaltungen
|
||||||
|
|
|
||||||
|
|
@ -31,25 +31,27 @@ class CreateExcelDocumentAction
|
||||||
private function allSheet(Collection $participants): TableDocumentData
|
private function allSheet(Collection $participants): TableDocumentData
|
||||||
{
|
{
|
||||||
$document = TableDocumentData::from(['title' => 'Anmeldungen für ' . $this->form->name, 'sheets' => []]);
|
$document = TableDocumentData::from(['title' => 'Anmeldungen für ' . $this->form->name, 'sheets' => []]);
|
||||||
$headers = $this->form->getFields()->map(fn ($field) => $field->name)->toArray();
|
$headers = $this->form->getFields()->names()->push('Abgemeldet am')->prepend('ID')->toArray();
|
||||||
|
[$activeParticipants, $cancelledParticipants] = $participants->partition(fn ($participant) => $participant->cancelled_at === null);
|
||||||
|
|
||||||
$document->addSheet(SheetData::from([
|
$document->addSheet(SheetData::from([
|
||||||
'header' => $headers,
|
'header' => $headers,
|
||||||
'data' => $participants
|
'data' => $this->rowsFor($activeParticipants),
|
||||||
->map(fn ($participant) => $this->form->getFields()->map(fn ($field) => $participant->getFields()->find($field)->presentRaw())->toArray())
|
|
||||||
->toArray(),
|
|
||||||
'name' => 'Alle',
|
'name' => 'Alle',
|
||||||
]));
|
]));
|
||||||
|
$document->addSheet(SheetData::from([
|
||||||
|
'header' => $headers,
|
||||||
|
'data' => $this->rowsFor($cancelledParticipants),
|
||||||
|
'name' => 'Abgemeldet',
|
||||||
|
]));
|
||||||
|
|
||||||
if ($this->form->export->groupBy) {
|
if ($this->form->export->groupBy) {
|
||||||
$groups = $participants->groupBy(fn ($participant) => $participant->getFields()->findByKey($this->form->export->groupBy)->presentRaw());
|
$groups = $activeParticipants->groupBy(fn ($participant) => $participant->getFields()->findByKey($this->form->export->groupBy)->presentRaw());
|
||||||
|
|
||||||
foreach ($groups as $name => $participants) {
|
foreach ($groups as $name => $groupedParticipants) {
|
||||||
$document->addSheet(SheetData::from([
|
$document->addSheet(SheetData::from([
|
||||||
'header' => $headers,
|
'header' => $headers,
|
||||||
'data' => $participants
|
'data' => $this->rowsFor($groupedParticipants),
|
||||||
->map(fn ($participant) => $this->form->getFields()->map(fn ($field) => $participant->getFields()->find($field)->presentRaw())->toArray())
|
|
||||||
->toArray(),
|
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +66,17 @@ class CreateExcelDocumentAction
|
||||||
return $document;
|
return $document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection<int, Participant> $participants
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
public function rowsFor(Collection $participants): array {
|
||||||
|
return $participants->map(fn ($participant) => $participant->getFields()->presentValues()
|
||||||
|
->put('Abgemeldet am', $participant->cancelled_at?->format('d.m.Y H:i:s') ?: '')
|
||||||
|
->prepend((string) $participant->id, 'ID')
|
||||||
|
)->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
private function tempPath(): string
|
private function tempPath(): string
|
||||||
{
|
{
|
||||||
return sys_get_temp_dir() . '/' . str()->uuid()->toString();
|
return sys_get_temp_dir() . '/' . str()->uuid()->toString();
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class FormIndexAction
|
||||||
*/
|
*/
|
||||||
public function handle(string $filter): LengthAwarePaginator
|
public function handle(string $filter): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
return FormFilterScope::fromRequest($filter)->getQuery()->query(fn ($query) => $query->withCount('participants'))->paginate(15);
|
return FormFilterScope::fromRequest($filter)->getQuery()->query(fn ($query) => $query->withCount(['participants' => fn ($q) => $q->whereNull('cancelled_at')]))->paginate(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function asController(ActionRequest $request): Response
|
public function asController(ActionRequest $request): Response
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use App\Form\Models\Participant;
|
||||||
use App\Lib\JobMiddleware\JobChannels;
|
use App\Lib\JobMiddleware\JobChannels;
|
||||||
use App\Lib\JobMiddleware\WithJobState;
|
use App\Lib\JobMiddleware\WithJobState;
|
||||||
use App\Lib\Queue\TracksJob;
|
use App\Lib\Queue\TracksJob;
|
||||||
|
use Lorisleiva\Actions\ActionRequest;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class ParticipantDestroyAction
|
class ParticipantDestroyAction
|
||||||
|
|
@ -13,14 +14,20 @@ class ParticipantDestroyAction
|
||||||
use AsAction;
|
use AsAction;
|
||||||
use TracksJob;
|
use TracksJob;
|
||||||
|
|
||||||
public function handle(int $participantId): void
|
public function handle(int $participantId, bool $force): void
|
||||||
{
|
{
|
||||||
Participant::findOrFail($participantId)->delete();
|
$participant = Participant::findOrFail($participantId);
|
||||||
|
|
||||||
|
if ($force) {
|
||||||
|
$participant->delete();
|
||||||
|
} else {
|
||||||
|
$participant->update(['cancelled_at' => now()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function asController(Participant $participant): void
|
public function asController(ActionRequest $request, Participant $participant): void
|
||||||
{
|
{
|
||||||
$this->startJob($participant->id);
|
$this->startJob($participant->id, $request->header('X-Force') === '1');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ class UpdateParticipantSearchIndexAction
|
||||||
$form->searchableUsing()->updateIndexSettings(
|
$form->searchableUsing()->updateIndexSettings(
|
||||||
$form->participantsSearchableAs(),
|
$form->participantsSearchableAs(),
|
||||||
[
|
[
|
||||||
'filterableAttributes' => [...$form->getFields()->filterables()->getKeys(), 'parent-id'],
|
'filterableAttributes' => [...$form->getFields()->filterables()->getKeys(), 'parent-id', 'cancelled_at'],
|
||||||
'searchableAttributes' => $form->getFields()->searchables()->getKeys(),
|
'searchableAttributes' => $form->getFields()->searchables()->getKeys(),
|
||||||
'sortableAttributes' => [...$form->getFields()->sortables()->getKeys(), 'id', 'created_at'],
|
'sortableAttributes' => [...$form->getFields()->sortables()->getKeys(), 'id', 'created_at'],
|
||||||
'displayedAttributes' => [...$form->getFields()->filterables()->getKeys(), ...$form->getFields()->searchables()->getKeys(), 'id'],
|
'displayedAttributes' => [...$form->getFields()->filterables()->getKeys(), ...$form->getFields()->searchables()->getKeys(), 'id', 'cancelled_at'],
|
||||||
'pagination' => [
|
'pagination' => [
|
||||||
'maxTotalHits' => 1000000,
|
'maxTotalHits' => 1000000,
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -99,19 +99,19 @@ class FieldCollection extends Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, string>
|
* @return Collection<int, string>
|
||||||
*/
|
*/
|
||||||
public function names(): array
|
public function names(): Collection
|
||||||
{
|
{
|
||||||
return $this->map(fn ($field) => $field->name)->toArray();
|
return $this->map(fn ($field) => $field->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, string>
|
* @return Collection<string, string>
|
||||||
*/
|
*/
|
||||||
public function presentValues(): array
|
public function presentValues(): Collection
|
||||||
{
|
{
|
||||||
return $this->map(fn ($field) => $field->presentRaw())->toArray();
|
return $this->mapWithKeys(fn ($field) => [$field->name => $field->presentRaw()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasSpecialType(SpecialType $specialType): bool
|
public function hasSpecialType(SpecialType $specialType): bool
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\Form\Actions\SettingStoreAction;
|
|
||||||
use App\Setting\Contracts\Storeable;
|
use App\Setting\Contracts\Storeable;
|
||||||
use App\Setting\LocalSettings;
|
use App\Setting\LocalSettings;
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
use Lorisleiva\Actions\ActionRequest;
|
||||||
|
|
@ -11,6 +10,7 @@ class FormSettings extends LocalSettings implements Storeable
|
||||||
{
|
{
|
||||||
public string $registerUrl;
|
public string $registerUrl;
|
||||||
public string $clearCacheUrl;
|
public string $clearCacheUrl;
|
||||||
|
public ?string $replyToMail;
|
||||||
|
|
||||||
public static function group(): string
|
public static function group(): string
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +19,7 @@ class FormSettings extends LocalSettings implements Storeable
|
||||||
|
|
||||||
public static function title(): string
|
public static function title(): string
|
||||||
{
|
{
|
||||||
return 'Formulare';
|
return 'Veranstaltungen';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -30,6 +30,7 @@ class FormSettings extends LocalSettings implements Storeable
|
||||||
return [
|
return [
|
||||||
'registerUrl' => 'present|string',
|
'registerUrl' => 'present|string',
|
||||||
'clearCacheUrl' => 'present|string',
|
'clearCacheUrl' => 'present|string',
|
||||||
|
'replyToMail' => 'nullable|string|email',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,6 +48,7 @@ class FormSettings extends LocalSettings implements Storeable
|
||||||
'data' => [
|
'data' => [
|
||||||
'registerUrl' => $this->registerUrl,
|
'registerUrl' => $this->registerUrl,
|
||||||
'clearCacheUrl' => $this->clearCacheUrl,
|
'clearCacheUrl' => $this->clearCacheUrl,
|
||||||
|
'replyToMail' => $this->replyToMail,
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Form\Mails;
|
||||||
|
|
||||||
use App\Form\Data\FormConfigData;
|
use App\Form\Data\FormConfigData;
|
||||||
use App\Form\Editor\FormConditionResolver;
|
use App\Form\Editor\FormConditionResolver;
|
||||||
|
use App\Form\FormSettings;
|
||||||
use App\Form\Models\Participant;
|
use App\Form\Models\Participant;
|
||||||
use App\Lib\Editor\Condition;
|
use App\Lib\Editor\Condition;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
|
@ -24,6 +25,8 @@ class ConfirmRegistrationMail extends Mailable
|
||||||
/** @var array<int, mixed> */
|
/** @var array<int, mixed> */
|
||||||
public array $bottomText;
|
public array $bottomText;
|
||||||
|
|
||||||
|
public FormSettings $formSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
|
|
@ -32,6 +35,7 @@ class ConfirmRegistrationMail extends Mailable
|
||||||
public function __construct(public Participant $participant)
|
public function __construct(public Participant $participant)
|
||||||
{
|
{
|
||||||
$conditionResolver = app(FormConditionResolver::class)->forParticipant($participant);
|
$conditionResolver = app(FormConditionResolver::class)->forParticipant($participant);
|
||||||
|
$this->formSettings = app(FormSettings::class);
|
||||||
$this->fullname = $participant->getFields()->getFullname();
|
$this->fullname = $participant->getFields()->getFullname();
|
||||||
$this->config = $participant->getConfig();
|
$this->config = $participant->getConfig();
|
||||||
$this->topText = $conditionResolver->makeBlocks($participant->form->mail_top);
|
$this->topText = $conditionResolver->makeBlocks($participant->form->mail_top);
|
||||||
|
|
@ -45,9 +49,15 @@ class ConfirmRegistrationMail extends Mailable
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope()
|
||||||
{
|
{
|
||||||
return new Envelope(
|
$envelope = new Envelope(
|
||||||
subject: 'Deine Anmeldung zu ' . $this->participant->form->name,
|
subject: 'Deine Anmeldung zu ' . $this->participant->form->name,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($this->formSettings->replyToMail !== null) {
|
||||||
|
$envelope->replyTo($this->formSettings->replyToMail);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $envelope;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ class Participant extends Model implements Preventable
|
||||||
public $casts = [
|
public $casts = [
|
||||||
'data' => 'json',
|
'data' => 'json',
|
||||||
'last_remembered_at' => 'datetime',
|
'last_remembered_at' => 'datetime',
|
||||||
|
'cancelled_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -108,7 +109,12 @@ class Participant extends Model implements Preventable
|
||||||
/** @return array<string, mixed> */
|
/** @return array<string, mixed> */
|
||||||
public function toSearchableArray(): array
|
public function toSearchableArray(): array
|
||||||
{
|
{
|
||||||
return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp];
|
return [
|
||||||
|
...$this->data,
|
||||||
|
'parent-id' => $this->parent_id,
|
||||||
|
'created_at' => $this->created_at->timestamp,
|
||||||
|
'cancelled_at' => $this->cancelled_at
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function matchesCondition(Condition $condition): bool {
|
public function matchesCondition(Condition $condition): bool {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ class ParticipantFilterScope extends ScoutFilter
|
||||||
public string $search = '',
|
public string $search = '',
|
||||||
public array $options = [],
|
public array $options = [],
|
||||||
public ?int $parent = null,
|
public ?int $parent = null,
|
||||||
public ?Sorting $sort = null
|
public ?Sorting $sort = null,
|
||||||
|
public bool $showCancelled = false,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,6 +55,12 @@ class ParticipantFilterScope extends ScoutFilter
|
||||||
$filter->push('parent-id IS NULL');
|
$filter->push('parent-id IS NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->showCancelled) {
|
||||||
|
$filter->push('cancelled_at IS NOT NULL');
|
||||||
|
} else {
|
||||||
|
$filter->push('cancelled_at IS NULL');
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->parent !== null && $this->parent !== -1) {
|
if ($this->parent !== null && $this->parent !== -1) {
|
||||||
$filter->push('parent-id = ' . $this->parent);
|
$filter->push('parent-id = ' . $this->parent);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ class MemberRequest extends FormRequest
|
||||||
'send_newspaper' => 'boolean',
|
'send_newspaper' => 'boolean',
|
||||||
'main_phone' => ['nullable', new ValidPhoneRule('Telefon (Eltern)')],
|
'main_phone' => ['nullable', new ValidPhoneRule('Telefon (Eltern)')],
|
||||||
'mobile_phone' => ['nullable', new ValidPhoneRule('Handy (Eltern)')],
|
'mobile_phone' => ['nullable', new ValidPhoneRule('Handy (Eltern)')],
|
||||||
|
'work_phone' => ['nullable', new ValidPhoneRule('Tel geschäftlich')],
|
||||||
'invoice_address' => '',
|
'invoice_address' => '',
|
||||||
'gender_id' => 'nullable|exists:genders,id',
|
'gender_id' => 'nullable|exists:genders,id',
|
||||||
'region_id' => 'nullable|exists:regions,id',
|
'region_id' => 'nullable|exists:regions,id',
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class SettingStoreAction
|
||||||
'weeks' => 'required|numeric|gte:0',
|
'weeks' => 'required|numeric|gte:0',
|
||||||
'freshRememberInterval' => 'required|numeric|gte:0',
|
'freshRememberInterval' => 'required|numeric|gte:0',
|
||||||
'active' => 'boolean',
|
'active' => 'boolean',
|
||||||
|
'replyToMail' => 'nullable|string|email',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ class SettingStoreAction
|
||||||
$settings->formmail = EditorData::from($request->formmail);
|
$settings->formmail = EditorData::from($request->formmail);
|
||||||
$settings->yearlymail = EditorData::from($request->yearlymail);
|
$settings->yearlymail = EditorData::from($request->yearlymail);
|
||||||
$settings->weeks = $request->weeks;
|
$settings->weeks = $request->weeks;
|
||||||
|
$settings->replyToMail = $request->replyToMail;
|
||||||
$settings->freshRememberInterval = $request->freshRememberInterval;
|
$settings->freshRememberInterval = $request->freshRememberInterval;
|
||||||
$settings->active = $request->active;
|
$settings->active = $request->active;
|
||||||
$settings->yearlyMemberFilter = FilterScope::from($request->yearlyMemberFilter);
|
$settings->yearlyMemberFilter = FilterScope::from($request->yearlyMemberFilter);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use App\Invoice\InvoiceSettings;
|
||||||
use App\Lib\Editor\EditorData;
|
use App\Lib\Editor\EditorData;
|
||||||
use App\Prevention\Contracts\Preventable;
|
use App\Prevention\Contracts\Preventable;
|
||||||
use App\Prevention\Data\PreventionData;
|
use App\Prevention\Data\PreventionData;
|
||||||
|
use App\Prevention\PreventionSettings;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Attachment;
|
use Illuminate\Mail\Attachment;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
|
|
@ -19,6 +20,7 @@ class YearlyMail extends Mailable
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
public InvoiceSettings $settings;
|
public InvoiceSettings $settings;
|
||||||
|
public PreventionSettings $preventionSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
|
|
@ -27,6 +29,7 @@ class YearlyMail extends Mailable
|
||||||
public function __construct(public Preventable $preventable, public EditorData $bodyText, public Collection $preventions)
|
public function __construct(public Preventable $preventable, public EditorData $bodyText, public Collection $preventions)
|
||||||
{
|
{
|
||||||
$this->settings = app(InvoiceSettings::class);
|
$this->settings = app(InvoiceSettings::class);
|
||||||
|
$this->preventionSettings = app(PreventionSettings::class);
|
||||||
$this->bodyText = $this->bodyText
|
$this->bodyText = $this->bodyText
|
||||||
->replaceWithList('wanted', $preventions->map(fn($prevention) => $prevention->text())->toArray());
|
->replaceWithList('wanted', $preventions->map(fn($prevention) => $prevention->text())->toArray());
|
||||||
}
|
}
|
||||||
|
|
@ -38,9 +41,15 @@ class YearlyMail extends Mailable
|
||||||
*/
|
*/
|
||||||
public function envelope()
|
public function envelope()
|
||||||
{
|
{
|
||||||
return (new Envelope(
|
$envelope = (new Envelope(
|
||||||
subject: $this->preventable->preventableSubject(),
|
subject: $this->preventable->preventableSubject(),
|
||||||
))->to($this->preventable->getMailRecipient()->email, $this->preventable->getMailRecipient()->name);
|
))->to($this->preventable->getMailRecipient()->email, $this->preventable->getMailRecipient()->name);
|
||||||
|
|
||||||
|
if ($this->preventionSettings->replyToMail !== null) {
|
||||||
|
$envelope->replyTo($this->preventionSettings->replyToMail);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $envelope;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class PreventionSettings extends LocalSettings
|
||||||
public int $freshRememberInterval;
|
public int $freshRememberInterval;
|
||||||
public bool $active;
|
public bool $active;
|
||||||
public FilterScope $yearlyMemberFilter;
|
public FilterScope $yearlyMemberFilter;
|
||||||
|
public ?string $replyToMail;
|
||||||
/**
|
/**
|
||||||
* @var array<int, string>
|
* @var array<int, string>
|
||||||
* @todo Create collection cast to Collection of enums
|
* @todo Create collection cast to Collection of enums
|
||||||
|
|
@ -49,6 +50,7 @@ class PreventionSettings extends LocalSettings
|
||||||
...$this->toArray(),
|
...$this->toArray(),
|
||||||
'weeks' => (string) $this->weeks,
|
'weeks' => (string) $this->weeks,
|
||||||
'freshRememberInterval' => (string) $this->freshRememberInterval,
|
'freshRememberInterval' => (string) $this->freshRememberInterval,
|
||||||
|
'replyToMail' => $this->replyToMail,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@ class ParticipantFactory extends Factory
|
||||||
return $this->state(['data' => $data]);
|
return $this->state(['data' => $data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function cancelled(): self {
|
||||||
|
return $this->state(['cancelled_at' => now()->subWeek()]);
|
||||||
|
}
|
||||||
|
|
||||||
public function nr(int $number): self
|
public function nr(int $number): self
|
||||||
{
|
{
|
||||||
return $this->state(['member_id' => $number]);
|
return $this->state(['member_id' => $number]);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('participants', function (Blueprint $table) {
|
||||||
|
$table->datetime('cancelled_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('participants', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('cancelled_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Spatie\LaravelSettings\Migrations\SettingsMigration;
|
||||||
|
|
||||||
|
return new class extends SettingsMigration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$this->migrator->add('form.replyToMail', '');
|
||||||
|
$this->migrator->add('prevention.replyToMail', '');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7304963370ff64fb5accf08da4864981cc424301
|
Subproject commit f7b04591830ebdeaddf76236e4cbc87a8b3eec8f
|
||||||
|
|
@ -47,8 +47,10 @@ export function useApiIndex(firstUrl, siteName = null) {
|
||||||
single.value = null;
|
single.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function remove(model) {
|
async function remove(model, force = true) {
|
||||||
await axios.delete(model.links.destroy);
|
await axios.delete(model.links.destroy, {
|
||||||
|
headers: { 'X-Force': force ? '1' : '0' }
|
||||||
|
});
|
||||||
await reload();
|
await reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,10 @@
|
||||||
<ui-popup v-if="assigning !== null" heading="Mitglied zuweisen" closeable @close="assigning = null">
|
<ui-popup v-if="assigning !== null" heading="Mitglied zuweisen" closeable @close="assigning = null">
|
||||||
<member-assign @assign="assign" />
|
<member-assign @assign="assign" />
|
||||||
</ui-popup>
|
</ui-popup>
|
||||||
<ui-popup v-if="deleting !== null" heading="Teilnehmer*in löschen?" @close="deleting = null">
|
<ui-popup v-if="deleting !== null" heading="Teilnehmer*in abmelden?" @close="deleting = null">
|
||||||
<div>
|
<div>
|
||||||
<p class="mt-4">Den*Die Teilnehmer*in löschen?</p>
|
<p class="mt-4">Den*Die Teilnehmer*in abmelden?</p>
|
||||||
|
<f-switch class="mt-2" v-model="deleting.force" name="force_delete" id="force_delete" label="löschen statt abmelden (permanent)" size="sm" />
|
||||||
<div class="grid grid-cols-2 gap-3 mt-6">
|
<div class="grid grid-cols-2 gap-3 mt-6">
|
||||||
<a href="#" class="text-center btn btn-danger" @click.prevent="handleDelete">Mitglied loschen</a>
|
<a href="#" class="text-center btn btn-danger" @click.prevent="handleDelete">Mitglied loschen</a>
|
||||||
<a href="#" class="text-center btn btn-primary" @click.prevent="deleting = null">Abbrechen</a>
|
<a href="#" class="text-center btn btn-primary" @click.prevent="deleting = null">Abbrechen</a>
|
||||||
|
|
@ -29,6 +30,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #fields>
|
<template #fields>
|
||||||
|
<f-switch id="show_cancelled" v-model="innerFilter.show_cancelled" label="Abgemeldete zeigen" size="sm" name="show_cancelled" />
|
||||||
<template v-for="(filter, index) in meta.filters">
|
<template v-for="(filter, index) in meta.filters">
|
||||||
<f-select v-if="filter.base_type === 'CheckboxField'"
|
<f-select v-if="filter.base_type === 'CheckboxField'"
|
||||||
:id="`filter-field-${index}`"
|
:id="`filter-field-${index}`"
|
||||||
|
|
@ -95,7 +97,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a v-tooltip="`Bearbeiten`" href="#" class="ml-2 inline-flex btn btn-warning btn-sm" @click.prevent="editReal(participant)"><ui-sprite src="pencil" /></a>
|
<a v-tooltip="`Bearbeiten`" href="#" class="ml-2 inline-flex btn btn-warning btn-sm" @click.prevent="editReal(participant)"><ui-sprite src="pencil" /></a>
|
||||||
<a v-tooltip="`Löschen`" href="#" class="ml-2 inline-flex btn btn-danger btn-sm" @click.prevent="deleting = participant"><ui-sprite src="trash" /></a>
|
<a v-tooltip="`Abmelden`" href="#" class="ml-2 inline-flex btn btn-danger btn-sm" @click.prevent="deleting = {model: participant, force: false}"><ui-sprite src="trash" /></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-for="child in childrenOf(participant.id)" :key="child.id">
|
<template v-for="child in childrenOf(participant.id)" :key="child.id">
|
||||||
|
|
@ -114,7 +116,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a v-tooltip="`Bearbeiten`" href="#" class="ml-2 inline-flex btn btn-warning btn-sm" @click.prevent="editReal(child)"><ui-sprite src="pencil" /></a>
|
<a v-tooltip="`Bearbeiten`" href="#" class="ml-2 inline-flex btn btn-warning btn-sm" @click.prevent="editReal(child)"><ui-sprite src="pencil" /></a>
|
||||||
<a v-tooltip="`Löschen`" href="#" class="ml-2 inline-flex btn btn-danger btn-sm" @click.prevent="deleting = child"><ui-sprite src="trash" /></a>
|
<a v-tooltip="`Abmelden`" href="#" class="ml-2 inline-flex btn btn-danger btn-sm" @click.prevent="deleting = {model: child, force: false}"><ui-sprite src="trash" /></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -209,7 +211,7 @@ const sortingConfig = computed({
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleDelete() {
|
async function handleDelete() {
|
||||||
await remove(deleting.value);
|
await remove(deleting.value.model, deleting.value.force);
|
||||||
deleting.value = null;
|
deleting.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<f-text id="register_url" v-model="inner.registerUrl" label="Formular-Link"></f-text>
|
<f-text id="register_url" v-model="inner.registerUrl" label="Formular-Link"></f-text>
|
||||||
<f-text id="clear_cache_url" v-model="inner.clearCacheUrl" label="Frontend-Cache-Url"></f-text>
|
<f-text id="clear_cache_url" v-model="inner.clearCacheUrl" label="Frontend-Cache-Url"></f-text>
|
||||||
|
<f-text id="reply_to_mail" v-model="inner.replyToMail" label="Reply-To-Adresse"></f-text>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</setting-layout>
|
</setting-layout>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
<f-editor v-if="active === 1" id="yearlymail" v-model="data.yearlymail" label="Jährliche Präventions-Erinnerung"></f-editor>
|
<f-editor v-if="active === 1" id="yearlymail" v-model="data.yearlymail" label="Jährliche Präventions-Erinnerung"></f-editor>
|
||||||
<f-member-filter id="yearly_member_filter" v-model="data.yearlyMemberFilter" label="nur für folgende Mitglieder erlauben" />
|
<f-member-filter id="yearly_member_filter" v-model="data.yearlyMemberFilter" label="nur für folgende Mitglieder erlauben" />
|
||||||
<f-multipleselect id="prevent_against" v-model="data.preventAgainst" :options="meta.preventAgainsts" label="An diese Dokumente erinnern" size="sm"></f-multipleselect>
|
<f-multipleselect id="prevent_against" v-model="data.preventAgainst" :options="meta.preventAgainsts" label="An diese Dokumente erinnern" size="sm"></f-multipleselect>
|
||||||
|
<f-text id="reply_to_mail" v-model="data.replyToMail" label="Reply-To-Adresse"></f-text>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</setting-layout>
|
</setting-layout>
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,18 @@ it('testItDisplaysForms', function () {
|
||||||
->assertInertiaPath('data.meta.default.location', '');
|
->assertInertiaPath('data.meta.default.location', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('displays participants count', function () {
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
Form::factory()
|
||||||
|
->has(Participant::factory()->count(2))
|
||||||
|
->has(Participant::factory()->cancelled()->count(3))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
$this->get(route('form.index'))
|
||||||
|
->assertInertiaPath('data.data.0.participants_count', 2);
|
||||||
|
});
|
||||||
|
|
||||||
it('testFormtemplatesHaveData', function () {
|
it('testFormtemplatesHaveData', function () {
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
Formtemplate::factory()->name('tname')->sections([FormtemplateSectionRequest::new()->name('sname')->fields([
|
Formtemplate::factory()->name('tname')->sections([FormtemplateSectionRequest::new()->name('sname')->fields([
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ it('testItShowsEmptyFilters', function () {
|
||||||
$this->callFilter('form.participant.index', ['data' => ['check' => null]], ['form' => $form])->assertHasJsonPath('meta.filter.data.check')->assertJsonPath('meta.filter.data.check', null);
|
$this->callFilter('form.participant.index', ['data' => ['check' => null]], ['form' => $form])->assertHasJsonPath('meta.filter.data.check')->assertJsonPath('meta.filter.data.check', null);
|
||||||
$this->callFilter('form.participant.index', ['data' => ['check' => 'A']], ['form' => $form])->assertJsonPath('meta.filter.data.check', 'A');
|
$this->callFilter('form.participant.index', ['data' => ['check' => 'A']], ['form' => $form])->assertJsonPath('meta.filter.data.check', 'A');
|
||||||
$this->callFilter('form.participant.index', ['data' => []], ['form' => $form])->assertJsonPath('meta.filter.data.check', ParticipantFilterScope::$nan);
|
$this->callFilter('form.participant.index', ['data' => []], ['form' => $form])->assertJsonPath('meta.filter.data.check', ParticipantFilterScope::$nan);
|
||||||
|
$this->callFilter('form.participant.index', ['data' => []], ['form' => $form])->assertJsonPath('meta.filter.show_cancelled', false);
|
||||||
|
$this->callFilter('form.participant.index', ['show_cancelled' => true], ['form' => $form])->assertJsonPath('meta.filter.show_cancelled', true);
|
||||||
|
$this->callFilter('form.participant.index', ['show_cancelled' => false], ['form' => $form])->assertJsonPath('meta.filter.show_cancelled', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sorts by active colums sorting by default', function (array $sorting, string $by, bool $direction) {
|
it('sorts by active colums sorting by default', function (array $sorting, string $by, bool $direction) {
|
||||||
|
|
@ -186,6 +189,22 @@ it('testItFiltersParticipantsByRadioValue', function () {
|
||||||
->assertJsonCount(4, 'data');
|
->assertJsonCount(4, 'data');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('filters participants by cancelled at', function () {
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
$form = Form::factory()
|
||||||
|
->has(Participant::factory()->count(1))
|
||||||
|
->has(Participant::factory()->cancelled()->count(2))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
sleep(2);
|
||||||
|
$this->callFilter('form.participant.index', [], ['form' => $form])
|
||||||
|
->assertJsonCount(1, 'data');
|
||||||
|
$this->callFilter('form.participant.index', ['show_cancelled' => false], ['form' => $form])
|
||||||
|
->assertJsonCount(1, 'data');
|
||||||
|
$this->callFilter('form.participant.index', ['show_cancelled' => true], ['form' => $form])
|
||||||
|
->assertJsonCount(2, 'data');
|
||||||
|
});
|
||||||
|
|
||||||
it('testItPresentsNamiField', function () {
|
it('testItPresentsNamiField', function () {
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
$form = Form::factory()
|
$form = Form::factory()
|
||||||
|
|
@ -269,6 +288,15 @@ it('testItShowsPreventionState', function () {
|
||||||
->assertJsonPath('data.0.prevention_items.0.tooltip', 'erweitertes Führungszeugnis nicht vorhanden');
|
->assertJsonPath('data.0.prevention_items.0.tooltip', 'erweitertes Führungszeugnis nicht vorhanden');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('doesnt show cancelled participants', function () {
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
$participant = Participant::factory()->for(Form::factory())->create(['cancelled_at' => now()]);
|
||||||
|
|
||||||
|
sleep(2);
|
||||||
|
$this->callFilter('form.participant.index', [], ['form' => $participant->form])
|
||||||
|
->assertJsonCount(0, 'data');
|
||||||
|
});
|
||||||
|
|
||||||
it('test it orders participants by value', function (array $values, array $sorting, array $expected) {
|
it('test it orders participants by value', function (array $values, array $sorting, array $expected) {
|
||||||
list($key, $direction) = $sorting;
|
list($key, $direction) = $sorting;
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,17 @@ it('notices a few weeks before', function ($date, bool $shouldSend) {
|
||||||
[fn() => now()->subYears(5)->addWeeks(2)->subDay(), false],
|
[fn() => now()->subYears(5)->addWeeks(2)->subDay(), false],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
it('sets reply to mail', function () {
|
||||||
|
Mail::fake();
|
||||||
|
app(PreventionSettings::class)->fill(['replyToMail' => 'admin@example.com'])->save();
|
||||||
|
createMember(['has_vk' => false]);
|
||||||
|
|
||||||
|
sleep(2);
|
||||||
|
YearlyRememberAction::run();
|
||||||
|
|
||||||
|
Mail::assertSent(YearlyMail::class, fn ($message) => $message->hasReplyTo('admin@example.com'));
|
||||||
|
});
|
||||||
|
|
||||||
it('remembers members yearly', function ($date, $shouldSend) {
|
it('remembers members yearly', function ($date, $shouldSend) {
|
||||||
Mail::fake();
|
Mail::fake();
|
||||||
createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]);
|
createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace Tests\Feature\Form;
|
||||||
|
|
||||||
use App\Form\Enums\NamiType;
|
use App\Form\Enums\NamiType;
|
||||||
use App\Form\Enums\SpecialType;
|
use App\Form\Enums\SpecialType;
|
||||||
|
use App\Form\FormSettings;
|
||||||
use App\Form\Mails\ConfirmRegistrationMail;
|
use App\Form\Mails\ConfirmRegistrationMail;
|
||||||
use App\Form\Models\Form;
|
use App\Form\Models\Form;
|
||||||
use App\Group;
|
use App\Group;
|
||||||
|
|
@ -308,6 +309,22 @@ it('testItSendsEmailToParticipant', function () {
|
||||||
Mail::assertQueued(ConfirmRegistrationMail::class, fn($message) => $message->hasTo('example@test.test', 'Lala GG') && $message->hasSubject('Deine Anmeldung zu Ver2'));
|
Mail::assertQueued(ConfirmRegistrationMail::class, fn($message) => $message->hasTo('example@test.test', 'Lala GG') && $message->hasSubject('Deine Anmeldung zu Ver2'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets reply to in email', function () {
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
app(FormSettings::class)->fill(['replyToMail' => 'reply@example.com'])->save();
|
||||||
|
$form = Form::factory()->name('Ver2')->fields([
|
||||||
|
$this->textField('vorname')->specialType(SpecialType::FIRSTNAME),
|
||||||
|
$this->textField('nachname')->specialType(SpecialType::LASTNAME),
|
||||||
|
$this->textField('email')->specialType(SpecialType::EMAIL),
|
||||||
|
])
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$this->register($form, ['vorname' => 'Lala', 'nachname' => 'GG', 'email' => 'example@test.test'])
|
||||||
|
->assertOk();
|
||||||
|
|
||||||
|
Mail::assertQueued(ConfirmRegistrationMail::class, fn($message) => $message->hasReplyTo('reply@example.com'));
|
||||||
|
});
|
||||||
|
|
||||||
it('testItDoesntSendEmailWhenNoMailFieldGiven', function () {
|
it('testItDoesntSendEmailWhenNoMailFieldGiven', function () {
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
$form = Form::factory()->fields([
|
$form = Form::factory()->fields([
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ beforeEach(function () {
|
||||||
test()->setUpForm();
|
test()->setUpForm();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('testItCanDestroyAParticipant', function () {
|
it('cancels a participant', function () {
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
$form = Form::factory()
|
$form = Form::factory()
|
||||||
->has(Participant::factory())
|
->has(Participant::factory())
|
||||||
|
|
@ -24,5 +24,22 @@ it('testItCanDestroyAParticipant', function () {
|
||||||
$this->deleteJson(route('participant.destroy', ['participant' => $form->participants->first()]))
|
$this->deleteJson(route('participant.destroy', ['participant' => $form->participants->first()]))
|
||||||
->assertOk();
|
->assertOk();
|
||||||
|
|
||||||
|
$this->assertDatabaseCount('participants', 1);
|
||||||
|
$this->assertDatabaseHas('participants', [
|
||||||
|
'cancelled_at' => now(),
|
||||||
|
'id' => $form->participants->first()->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('testItCanDestroyAParticipant', function () {
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
$form = Form::factory()
|
||||||
|
->has(Participant::factory())
|
||||||
|
->sections([])
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$this->deleteJson(route('participant.destroy', ['participant' => $form->participants->first()]), [], ['X-Force' => '1'])
|
||||||
|
->assertOk();
|
||||||
|
|
||||||
$this->assertDatabaseCount('participants', 0);
|
$this->assertDatabaseCount('participants', 0);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Tests\Feature\Form;
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
|
use DB;
|
||||||
use App\Form\Models\Form;
|
use App\Form\Models\Form;
|
||||||
use App\Form\Models\Participant;
|
use App\Form\Models\Participant;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
|
@ -20,15 +21,14 @@ it('testItShowsParticipantsAndColumns', function () {
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
$form = Form::factory()
|
$form = Form::factory()
|
||||||
->has(Participant::factory()->data(['stufe' => 'Pfadfinder', 'vorname' => 'Max', 'select' => ['A', 'B']]))
|
->has(Participant::factory()->data(['stufe' => 'Pfadfinder', 'vorname' => 'Max', 'select' => ['A', 'B']]))
|
||||||
->sections([
|
->fields([
|
||||||
FormtemplateSectionRequest::new()->fields([
|
$this->textField('vorname')->name('Vorname'),
|
||||||
$this->textField('vorname')->name('Vorname'),
|
$this->checkboxesField('select')->name('Abcselect')->options(['A', 'B', 'C']),
|
||||||
$this->checkboxesField('select')->name('Abcselect')->options(['A', 'B', 'C']),
|
$this->dropdownField('stufe')->name('Stufe')->options(['Wölfling', 'Jungpfadfinder', 'Pfadfinder']),
|
||||||
$this->dropdownField('stufe')->name('Stufe')->options(['Wölfling', 'Jungpfadfinder', 'Pfadfinder']),
|
|
||||||
]),
|
|
||||||
])
|
])
|
||||||
->name('ZEM 2024')
|
->name('ZEM 2024')
|
||||||
->create();
|
->create();
|
||||||
|
DB::table('participants')->where('id', $form->participants->first()->id)->update(['id' => 9909]);
|
||||||
|
|
||||||
$this->get(route('form.export', ['form' => $form]))->assertDownload('tn-zem-2024.xlsx');
|
$this->get(route('form.export', ['form' => $form]))->assertDownload('tn-zem-2024.xlsx');
|
||||||
$contents = Storage::disk('temp')->get('tn-zem-2024.xlsx');
|
$contents = Storage::disk('temp')->get('tn-zem-2024.xlsx');
|
||||||
|
|
@ -37,4 +37,17 @@ it('testItShowsParticipantsAndColumns', function () {
|
||||||
$this->assertExcelContent('Pfadfinder', $contents);
|
$this->assertExcelContent('Pfadfinder', $contents);
|
||||||
$this->assertExcelContent('Stufe', $contents);
|
$this->assertExcelContent('Stufe', $contents);
|
||||||
$this->assertExcelContent('Abcselect', $contents);
|
$this->assertExcelContent('Abcselect', $contents);
|
||||||
|
$this->assertExcelContent('9909', $contents);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows cancelled at', function () {
|
||||||
|
Storage::fake('temp');
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
$form = Form::factory()->name('ZEM 2024')
|
||||||
|
->has(Participant::factory()->state(['cancelled_at' => now()->subWeek()]))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$this->get(route('form.export', ['form' => $form]))->assertDownload('tn-zem-2024.xlsx');
|
||||||
|
$contents = Storage::disk('temp')->get('tn-zem-2024.xlsx');
|
||||||
|
$this->assertExcelContent(now()->subWeek()->format('d.m.Y'), $contents);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,18 @@ it('testItSetsLocationToNull', function () {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('updates work phone', function () {
|
||||||
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
|
$member = factory()->notInNami()->create();
|
||||||
|
fakeRequest();
|
||||||
|
NamiPutMemberAction::allowToRun();
|
||||||
|
|
||||||
|
$this->patch("/member/{$member->id}", MemberUpdateRequestFactory::new()->noNami()->create([
|
||||||
|
'work_phone' => '+49 212 1353688',
|
||||||
|
]));
|
||||||
|
test()->assertDatabaseHas('members', ['work_phone' => '+49 212 1353688']);
|
||||||
|
});
|
||||||
|
|
||||||
it('testItUpdatesContact', function () {
|
it('testItUpdatesContact', function () {
|
||||||
$this->withoutExceptionHandling()->login()->loginNami();
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
$member = factory()->notInNami()->create();
|
$member = factory()->notInNami()->create();
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,9 @@
|
||||||
|
|
||||||
namespace Tests\Feature\Nami;
|
namespace Tests\Feature\Nami;
|
||||||
|
|
||||||
use App\Invoice\InvoiceSettings;
|
|
||||||
use App\Setting\NamiSettings;
|
use App\Setting\NamiSettings;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Tests\TestCase;
|
|
||||||
use Zoomyboy\LaravelNami\Authentication\Auth;
|
use Zoomyboy\LaravelNami\Authentication\Auth;
|
||||||
use Zoomyboy\LaravelNami\Nami;
|
|
||||||
|
|
||||||
uses(DatabaseTransactions::class);
|
uses(DatabaseTransactions::class);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ it('receives settings', function () {
|
||||||
'weeks' => 9,
|
'weeks' => 9,
|
||||||
'freshRememberInterval' => 11,
|
'freshRememberInterval' => 11,
|
||||||
'active' => true,
|
'active' => true,
|
||||||
|
'replyToMail' => 'admin@example.com',
|
||||||
'preventAgainst' => [Prevention::MOREPS->name],
|
'preventAgainst' => [Prevention::MOREPS->name],
|
||||||
'yearlyMemberFilter' => FilterScope::from([
|
'yearlyMemberFilter' => FilterScope::from([
|
||||||
'memberships' => [['group_ids' => [33]]],
|
'memberships' => [['group_ids' => [33]]],
|
||||||
|
|
@ -41,6 +42,7 @@ it('receives settings', function () {
|
||||||
->assertJsonPath('data.weeks', '9')
|
->assertJsonPath('data.weeks', '9')
|
||||||
->assertJsonPath('data.active', true)
|
->assertJsonPath('data.active', true)
|
||||||
->assertJsonPath('data.freshRememberInterval', '11')
|
->assertJsonPath('data.freshRememberInterval', '11')
|
||||||
|
->assertJsonPath('data.replyToMail', 'admin@example.com')
|
||||||
->assertJsonPath('data.yearlyMemberFilter.search', 'searchstring')
|
->assertJsonPath('data.yearlyMemberFilter.search', 'searchstring')
|
||||||
->assertJsonPath('data.yearlyMemberFilter.memberships.0.group_ids.0', 33)
|
->assertJsonPath('data.yearlyMemberFilter.memberships.0.group_ids.0', 33)
|
||||||
->assertJsonPath('data.preventAgainst', ['MOREPS'])
|
->assertJsonPath('data.preventAgainst', ['MOREPS'])
|
||||||
|
|
@ -58,6 +60,7 @@ it('testItStoresSettings', function () {
|
||||||
'freshRememberInterval' => 11,
|
'freshRememberInterval' => 11,
|
||||||
'active' => true,
|
'active' => true,
|
||||||
'preventAgainst' => ['EFZ'],
|
'preventAgainst' => ['EFZ'],
|
||||||
|
'replyToMail' => 'admin@example.com',
|
||||||
'yearlyMemberFilter' => [
|
'yearlyMemberFilter' => [
|
||||||
'memberships' => [['group_ids' => 33]],
|
'memberships' => [['group_ids' => 33]],
|
||||||
'search' => 'searchstring',
|
'search' => 'searchstring',
|
||||||
|
|
@ -66,6 +69,7 @@ it('testItStoresSettings', function () {
|
||||||
test()->assertTrue(app(PreventionSettings::class)->formmail->hasAll(['new lorem']));
|
test()->assertTrue(app(PreventionSettings::class)->formmail->hasAll(['new lorem']));
|
||||||
test()->assertTrue(app(PreventionSettings::class)->yearlymail->hasAll(['lala dd']));
|
test()->assertTrue(app(PreventionSettings::class)->yearlymail->hasAll(['lala dd']));
|
||||||
test()->assertEquals(9, app(PreventionSettings::class)->weeks);
|
test()->assertEquals(9, app(PreventionSettings::class)->weeks);
|
||||||
|
test()->assertEquals('admin@example.com', app(PreventionSettings::class)->replyToMail);
|
||||||
test()->assertEquals(11, app(PreventionSettings::class)->freshRememberInterval);
|
test()->assertEquals(11, app(PreventionSettings::class)->freshRememberInterval);
|
||||||
test()->assertTrue(app(PreventionSettings::class)->active);
|
test()->assertTrue(app(PreventionSettings::class)->active);
|
||||||
test()->assertEquals([['group_ids' => 33]], app(PreventionSettings::class)->yearlyMemberFilter->memberships);
|
test()->assertEquals([['group_ids' => 33]], app(PreventionSettings::class)->yearlyMemberFilter->memberships);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Illuminate\Testing;
|
|
||||||
|
|
||||||
namespace Spatie\LaravelSettings;
|
namespace Spatie\LaravelSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue