feat: provision a teamer from a received application
This commit is contained in:
1 parent
d5f4b12f0a
commit
b530679658
23 files changed
+2433
-148
No files matched your search
@@ -15,9 +15,22 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
* side without a release here, so nothing may switch the serializer's ALLOW_EXTRA_ATTRIBUTES
|
||||
* off: it would turn every later mapping change into a 400, silent because the finisher only
|
||||
* logs it.
|
||||
* - **Everything but the four basics is optional.** A form is configured without a field as
|
||||
* often as with it, so an absent key means "not part of this form", never "empty" - hence the
|
||||
* defaults, which also let the serializer construct the object from a payload of four keys.
|
||||
* - **Nine fields are required of every payload**: lastName, firstName, birthDate, email, gender,
|
||||
* eu, street, zipCode and city. They are what it takes to provision a teamer from a submission,
|
||||
* and BusPro's own mandatory fields for creating an address are among them (errors 885-891: Name,
|
||||
* Vorname, Geschlecht, EMail, PLZ, Straße) - a payload without those cannot become a teamer at
|
||||
* all, and asking a reviewer to retype them from the answers would be worse than refusing the
|
||||
* payload. The endpoint only ever receives application forms, so this holds for every payload and
|
||||
* there is no form-type discriminator
|
||||
* (.ddev/plans/application-form-teamer-provisioning.md).
|
||||
*
|
||||
* Everything beyond the nine - phone, mobile, nationality, bank details - stays optional and is
|
||||
* filled in by the teamer themselves afterwards, so an absent key there still means "not part of
|
||||
* this form", never "empty".
|
||||
*
|
||||
* A 400 costs a whole application, and the visitor sees their form submit normally either way.
|
||||
* That is accepted deliberately: the same people control the TYPO3 forms and this endpoint, the
|
||||
* response body names the offending field and the finisher logs it.
|
||||
*
|
||||
* The lengths are the storage limits of ApplicationFormSubmission, not an opinion about what a
|
||||
* visitor may type: they exist so an oversized value is a 400 the TYPO3 side logs, and not a
|
||||
@@ -25,6 +38,11 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
*/
|
||||
class ApplicationFormSubmissionDto
|
||||
{
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
public const GENDERS = ['F', 'M', 'D'];
|
||||
|
||||
public const MAX_DETAIL_LABEL_LENGTH = 255;
|
||||
|
||||
public const MAX_DETAIL_VALUE_LENGTH = 65535;
|
||||
@@ -47,10 +65,13 @@ class ApplicationFormSubmissionDto
|
||||
public readonly int|string|null $pageUid = null,
|
||||
#[Assert\Length(max: 2048, maxMessage: 'pageUrl must not exceed {{ limit }} characters.')]
|
||||
public readonly ?string $pageUrl = null,
|
||||
#[Assert\NotBlank(message: 'firstName must not be empty.')]
|
||||
#[Assert\Length(max: 255)]
|
||||
public readonly ?string $firstName = null,
|
||||
#[Assert\NotBlank(message: 'lastName must not be empty.')]
|
||||
#[Assert\Length(max: 255)]
|
||||
public readonly ?string $lastName = null,
|
||||
#[Assert\NotBlank(message: 'email must not be empty.')]
|
||||
#[Assert\Email(message: 'email is not a valid email address.')]
|
||||
#[Assert\Length(max: 255)]
|
||||
public readonly ?string $email = null,
|
||||
@@ -58,19 +79,37 @@ class ApplicationFormSubmissionDto
|
||||
public readonly ?string $phone = null,
|
||||
#[Assert\Length(max: 64)]
|
||||
public readonly ?string $mobile = null,
|
||||
#[Assert\Length(max: 64)]
|
||||
// The one field whose format is nailed down rather than stored as typed: provisioning
|
||||
// parses it as a date, and a free-text birthday would only fail later, in front of a
|
||||
// reviewer who cannot fix it.
|
||||
#[Assert\NotBlank(message: 'birthDate must not be empty.')]
|
||||
#[Assert\Date(message: 'birthDate must be a date in the format YYYY-MM-DD.')]
|
||||
public readonly ?string $birthDate = null,
|
||||
// The application's own vocabulary, not BusPro's: ApplicationFormSubmission::toPayload()
|
||||
// maps F to W on the way out, exactly as Teamer::toPayload() does.
|
||||
#[Assert\NotBlank(message: 'gender must not be empty.')]
|
||||
#[Assert\Choice(choices: self::GENDERS, message: 'gender must be one of F, M or D.')]
|
||||
public readonly ?string $gender = null,
|
||||
// The whole postal address, required together. Street and postcode are BusPro's own
|
||||
// Pflichtfelder for a new address (errors 891 and 890), so a submission without them is one
|
||||
// no teamer can be provisioned from; the town is not on that list, but an address carrying a
|
||||
// street and a postcode without one is not an address worth writing to the CRM either.
|
||||
#[Assert\NotBlank(message: 'street must not be empty.')]
|
||||
#[Assert\Length(max: 255)]
|
||||
public readonly ?string $street = null,
|
||||
#[Assert\NotBlank(message: 'zipCode must not be empty.')]
|
||||
#[Assert\Length(max: 32)]
|
||||
public readonly ?string $zipCode = null,
|
||||
#[Assert\NotBlank(message: 'city must not be empty.')]
|
||||
#[Assert\Length(max: 255)]
|
||||
public readonly ?string $city = null,
|
||||
// Whether the applicant holds an EU (incl. Switzerland) nationality - the one payload
|
||||
// field a rule depends on, hence mapped rather than left in $details. bool|string|int
|
||||
// for the same reason as pageUid: a form posts "1" or "" as readily as a JSON boolean,
|
||||
// and losing a whole application over the spelling would be absurd. Read it back
|
||||
// through isEu().
|
||||
// through isEu(). Required, because the receipt mail depends on it - but required as a
|
||||
// value, not as a truth: NotNull rather than NotBlank, or a lawful "no" would be a 400.
|
||||
#[Assert\NotNull(message: 'eu must not be omitted.')]
|
||||
public readonly bool|string|int|null $eu = null,
|
||||
public readonly array $details = [],
|
||||
) {
|
||||
|
||||
Reference in new issue
Block a user