Add condition for leader contribution form
continuous-integration/drone/push Build is passing Details

This commit is contained in:
philipp lang 2025-07-09 22:51:28 +02:00
parent 5fe1004871
commit 6c3c8b5703
7 changed files with 122 additions and 35 deletions

View File

@ -69,10 +69,8 @@ class ConfirmRegistrationMail extends Mailable
*/ */
public function attachments() public function attachments()
{ {
$conditionResolver = app(FormConditionResolver::class)->forParticipant($this->participant);
return $this->participant->form->getMedia('mailattachments') return $this->participant->form->getMedia('mailattachments')
->filter(fn ($media) => $conditionResolver->filterCondition(Condition::fromMedia($media))) ->filter(fn ($media) => $this->participant->matchesCondition(Condition::fromMedia($media)))
->map(fn ($media) => Attachment::fromStorageDisk($media->disk, $media->getPathRelativeToRoot())) ->map(fn ($media) => Attachment::fromStorageDisk($media->disk, $media->getPathRelativeToRoot()))
->all(); ->all();
} }

View File

@ -49,7 +49,8 @@ class Form extends Model implements HasMedia
'to' => 'datetime', 'to' => 'datetime',
'registration_from' => 'datetime', 'registration_from' => 'datetime',
'registration_until' => 'datetime', 'registration_until' => 'datetime',
'country' => Country::class 'country' => Country::class,
'leader_condition' => Condition::class,
]; ];
/** /**

View File

@ -4,12 +4,12 @@ namespace App\Form\Models;
use App\Form\Data\FieldCollection; use App\Form\Data\FieldCollection;
use App\Form\Data\FormConfigData; use App\Form\Data\FormConfigData;
use App\Form\Editor\FormConditionResolver;
use App\Form\Mails\ConfirmRegistrationMail; use App\Form\Mails\ConfirmRegistrationMail;
use App\Form\Scopes\ParticipantFilterScope; use App\Lib\Editor\Condition;
use App\Member\Member; use App\Member\Member;
use App\Prevention\Contracts\Preventable; use App\Prevention\Contracts\Preventable;
use Database\Factories\Form\Models\ParticipantFactory; use Database\Factories\Form\Models\ParticipantFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -110,4 +110,8 @@ class Participant extends Model implements Preventable
{ {
return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp]; return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp];
} }
public function matchesCondition(Condition $condition): bool {
return app(FormConditionResolver::class)->forParticipant($this)->filterCondition($condition);
}
} }

View File

@ -6,6 +6,7 @@ use App\Contribution\Contracts\HasContributionData;
use App\Contribution\Data\MemberData; use App\Contribution\Data\MemberData;
use App\Contribution\Documents\ContributionDocument; use App\Contribution\Documents\ContributionDocument;
use App\Country; use App\Country;
use App\Form\Editor\FormConditionResolver;
use App\Form\Enums\SpecialType; use App\Form\Enums\SpecialType;
use App\Form\Models\Form; use App\Form\Models\Form;
use Carbon\Carbon; use Carbon\Carbon;
@ -22,7 +23,7 @@ class FormCompileRequest extends Data implements HasContributionData {
*/ */
public function type(): string public function type(): string
{ {
$payload = json_decode(rawurldecode(base64_decode(request()->input('payload', ''))), true); $payload = json_decode(rawurldecode(base64_decode(request()->input('payload'))), true);
return $payload['type']; return $payload['type'];
} }
@ -54,10 +55,10 @@ class FormCompileRequest extends Data implements HasContributionData {
[SpecialType::FIRSTNAME, 'firstname'], [SpecialType::FIRSTNAME, 'firstname'],
[SpecialType::LASTNAME, 'lastname'], [SpecialType::LASTNAME, 'lastname'],
[SpecialType::BIRTHDAY, 'birthday'], [SpecialType::BIRTHDAY, 'birthday'],
[SpecialType::GENDER, 'gender'],
[SpecialType::ADDRESS, 'address'], [SpecialType::ADDRESS, 'address'],
[SpecialType::ZIP, 'zip'], [SpecialType::ZIP, 'zip'],
[SpecialType::LOCATION, 'location'], [SpecialType::LOCATION, 'location'],
[SpecialType::GENDER, 'gender']
]; ];
foreach ($this->form->participants as $participant) { foreach ($this->form->participants as $participant) {
@ -71,7 +72,7 @@ class FormCompileRequest extends Data implements HasContributionData {
} }
$members[] = [ $members[] = [
'is_leader' => false, 'is_leader' => $participant->matchesCondition($participant->form->leader_condition),
'gender' => 'weiblich', 'gender' => 'weiblich',
...$member, ...$member,
]; ];

View File

@ -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('forms', function (Blueprint $table) {
$table->json('leader_condition')->after('name')->default(json_encode(['mode' => 'all', 'ifs' => []]));
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('forms', function (Blueprint $table) {
$table->dropColumn('leader_condition');
});
}
};

View File

@ -12,6 +12,7 @@ use App\Form\Requests\FormCompileRequest;
use App\Gender; use App\Gender;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\Lib\CreatesFormFields; use Tests\Lib\CreatesFormFields;
use Tests\RequestFactories\ConditionRequestFactory;
use Zoomyboy\Tex\Tex; use Zoomyboy\Tex\Tex;
uses(DatabaseTransactions::class); uses(DatabaseTransactions::class);
@ -23,11 +24,11 @@ beforeEach(function() {
Country::factory()->create(); Country::factory()->create();
Gender::factory()->male()->create(); Gender::factory()->male()->create();
Gender::factory()->female()->create(); Gender::factory()->female()->create();
Tex::spy();
$this->login()->loginNami();
}); });
it('doesnt create document when no special fields given', function (array $fields, string $field, string $message, string $type) { it('doesnt create document when no special fields given', function (array $fields, string $field, string $message, string $type) {
$this->login()->loginNami();
$form = Form::factory() $form = Form::factory()
->fields($fields) ->fields($fields)
->has(Participant::factory()) ->has(Participant::factory())
@ -44,8 +45,6 @@ it('doesnt create document when no special fields given', function (array $field
])->with('contribution-documents'); ])->with('contribution-documents');
it('validates special types of each document', function (string $type, array $fields, string $field, string $message) { it('validates special types of each document', function (string $type, array $fields, string $field, string $message) {
$this->login()->loginNami();
$form = Form::factory()->fields([ $form = Form::factory()->fields([
test()->textField('f')->specialType(SpecialType::FIRSTNAME), test()->textField('f')->specialType(SpecialType::FIRSTNAME),
test()->textField('l')->specialType(SpecialType::LASTNAME), test()->textField('l')->specialType(SpecialType::LASTNAME),
@ -65,8 +64,6 @@ it('validates special types of each document', function (string $type, array $fi
]); ]);
it('throws error when not validating but fields are not present', function () { it('throws error when not validating but fields are not present', function () {
$this->login()->loginNami();
$form = Form::factory()->fields([]) $form = Form::factory()->fields([])
->has(Participant::factory()) ->has(Participant::factory())
->create(); ->create();
@ -75,8 +72,6 @@ it('throws error when not validating but fields are not present', function () {
}); });
it('throws error when form doesnt have meta', function () { it('throws error when form doesnt have meta', function () {
$this->login()->loginNami();
$form = Form::factory()->fields([]) $form = Form::factory()->fields([])
->has(Participant::factory()) ->has(Participant::factory())
->zip('') ->zip('')
@ -90,37 +85,73 @@ it('throws error when form doesnt have meta', function () {
}); });
it('throws error when form doesnt have participants', function () { it('throws error when form doesnt have participants', function () {
$this->login()->loginNami();
$form = Form::factory()->fields([])->create(); $form = Form::factory()->fields([])->create();
generate(CitySolingenDocument::class, $form, true)->assertJsonValidationErrors(['participants' => 'Veranstaltung besitzt noch keine Teilnehmer*innen.']); generate(CitySolingenDocument::class, $form, true)->assertJsonValidationErrors(['participants' => 'Veranstaltung besitzt noch keine Teilnehmer*innen.']);
}); });
it('creates document when fields are present', function () { dataset('default-form-contribution', fn () => [
Tex::spy(); [
$this->login()->loginNami(); ['fn' => 'Baum', 'ln' => 'Muster', 'bd' => '1991-05-06', 'zip' => '33333', 'loc' => 'Musterstadt', 'add' => 'Laastr 4', 'gen' => 'weiblich'],
fn () => [
$form = Form::factory()->fields([
test()->textField('fn')->specialType(SpecialType::FIRSTNAME), test()->textField('fn')->specialType(SpecialType::FIRSTNAME),
test()->textField('ln')->specialType(SpecialType::LASTNAME), test()->textField('ln')->specialType(SpecialType::LASTNAME),
test()->dateField('bd')->specialType(SpecialType::BIRTHDAY), test()->dateField('bd')->specialType(SpecialType::BIRTHDAY),
test()->dateField('zip')->specialType(SpecialType::ZIP), test()->dateField('zip')->specialType(SpecialType::ZIP),
test()->dateField('loc')->specialType(SpecialType::LOCATION), test()->dateField('loc')->specialType(SpecialType::LOCATION),
test()->dateField('add')->specialType(SpecialType::ADDRESS), test()->dateField('add')->specialType(SpecialType::ADDRESS),
test()->dateField('gen')->specialType(SpecialType::GENDER),
]
]
]);
dataset('form-contributions', fn () => [
[
[],
[],
CitySolingenDocument::class,
['Baum', 'Muster', '1991', 'Musterstadt', 'Laastr 4', '33333'],
],
[
['gen' => 'männlich'],
[],
RdpNrwDocument::class,
['{m}'],
],
[
['gen' => 'weiblich'],
[],
RdpNrwDocument::class,
['{w}'],
],
]);
it('creates document with participant data', function (array $defaultData, array $defaultFields, array $newData, array $newFields, string $document, array $expected) {
$form = Form::factory()->fields([
...$defaultFields,
...$newFields,
]) ])
->has(Participant::factory()->data(['fn' => 'Baum', 'ln' => 'Muster', 'bd' => '1991-05-06', 'zip' => '33333', 'loc' => 'Musterstadt', 'add' => 'Laastr 4'])) ->has(Participant::factory()->data([...$defaultData, ...$newData]))
->create(); ->create();
generate(CitySolingenDocument::class, $form, false)->assertOk(); generate($document, $form, false)->assertOk();
Tex::assertCompiled(CitySolingenDocument::class, fn($document) => $document->hasAllContent(['Baum', 'Muster', '1991', 'Musterstadt', 'Laastr 4', '33333'])); Tex::assertCompiled($document, fn($document) => $document->hasAllContent($expected));
}); })->with('default-form-contribution')->with('form-contributions');
it('creates document with is leader', function (array $defaultData, array $fields) {
$form = Form::factory()->fields([
...$fields,
test()->dropdownField('leader')->options(['L', 'NL'])->specialType(SpecialType::LEADER),
])
->has(Participant::factory()->data([...$defaultData, 'leader' => 'L']))
->leaderCondition(ConditionRequestFactory::new()->whenField('leader', 'L')->create())
->create();
generate(RdpNrwDocument::class, $form, false)->assertOk();
Tex::assertCompiled(RdpNrwDocument::class, fn($document) => $document->hasAllContent(['{L}']));
})->with('default-form-contribution');
it('creates document with form meta', function () { it('creates document with form meta', function () {
Tex::spy();
$this->login()->loginNami();
$form = Form::factory()->fields([ $form = Form::factory()->fields([
test()->textField('fn')->specialType(SpecialType::FIRSTNAME), test()->textField('fn')->specialType(SpecialType::FIRSTNAME),
test()->textField('ln')->specialType(SpecialType::LASTNAME), test()->textField('ln')->specialType(SpecialType::LASTNAME),

View File

@ -0,0 +1,24 @@
<?php
namespace Tests\RequestFactories;
use Worksome\RequestFactories\RequestFactory;
class ConditionRequestFactory extends RequestFactory
{
public function definition(): array
{
return [
'mode' => 'all',
'ifs' => [],
];
}
public function whenField(string $field, string $value): self {
return $this->state([
'ifs' => [
['field' => $field, 'comparator' => 'isEqual', 'value' => $value]
],
]);
}
}