152 lines
5.4 KiB
PHP
152 lines
5.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Form\Model;
|
|
|
|
use App\BusProNet\Model\Address;
|
|
use App\BusProNet\Model\PersonalData;
|
|
use App\BusProNet\Model\VoucherPaymentMethod;
|
|
use App\BusProNet\Model\VoucherTemplate;
|
|
use App\Validator\Constraints as AppAssert;
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
/**
|
|
* Session state of the voucher purchase flow (/gutschein).
|
|
*
|
|
* The fields are exactly what the BusProNet voucher shop collects, with its mandatory flags rather
|
|
* than those of ADRESSFELDERGUTSCHEIN (which would also demand a phone number and a gender). There
|
|
* is no Zusatztext: GUTSCHEINBUCHUNG is sent an empty <text/>, as the live shop does.
|
|
*
|
|
* Step 2 is validated with the groups ['voucher_step_2', 'personal_data']: the buyer address reuses
|
|
* the Address model, whose constraints live in the personal_data group.
|
|
*/
|
|
class VoucherPurchaseDto
|
|
{
|
|
public const string SALUTATION_MR = 'Herr';
|
|
public const string SALUTATION_MS = 'Frau';
|
|
|
|
public int $currentStep = 1;
|
|
|
|
// BusPro's bounds from GUTSCHEINVORLAGEN at the start of the flow. BusPro re-checks them on
|
|
// every GUTSCHEINBUCHUNG (607/608), so a change in between still surfaces as a step 1 error.
|
|
public int $minAmount = 0;
|
|
public int $maxAmount = 0;
|
|
|
|
// Fixed at the start of the flow from GUTSCHEINZAHLUNGSARTEN; transfer is the only one offered.
|
|
public ?VoucherPaymentMethod $paymentMethod = null;
|
|
|
|
// Step 1: voucher
|
|
#[Assert\NotNull(message: 'Bitte wähle ein Motiv', groups: ['voucher_step_1'])]
|
|
public ?VoucherTemplate $template = null;
|
|
|
|
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['voucher_step_1'])]
|
|
public ?string $from = null;
|
|
|
|
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['voucher_step_1'])]
|
|
public ?string $to = null;
|
|
|
|
#[Assert\NotNull(message: 'Bitte gib einen Gutscheinwert an', groups: ['voucher_step_1'])]
|
|
#[Assert\Range(
|
|
notInRangeMessage: 'Der Gutscheinwert muss zwischen {{ min }} und {{ max }} Euro liegen',
|
|
minPropertyPath: 'minAmount',
|
|
maxPropertyPath: 'maxAmount',
|
|
groups: ['voucher_step_1'],
|
|
)]
|
|
public ?int $amount = null;
|
|
|
|
// Step 2: buyer (rechnungsempfänger)
|
|
#[Assert\Choice(choices: [self::SALUTATION_MR, self::SALUTATION_MS], groups: ['voucher_step_2'])]
|
|
public ?string $salutation = null;
|
|
|
|
public ?string $title = null;
|
|
|
|
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['voucher_step_2'])]
|
|
public ?string $lastName = null;
|
|
|
|
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['voucher_step_2'])]
|
|
public ?string $firstName = null;
|
|
|
|
#[Assert\Valid]
|
|
public Address $address;
|
|
|
|
#[Assert\When(
|
|
expression: 'this.isPhoneNewlyEntered()',
|
|
constraints: [new AppAssert\PhoneNumber()],
|
|
groups: ['voucher_step_2'],
|
|
)]
|
|
public ?string $phone = null;
|
|
|
|
#[Assert\When(
|
|
expression: 'this.isMobileNewlyEntered()',
|
|
constraints: [new AppAssert\PhoneNumber()],
|
|
groups: ['voucher_step_2'],
|
|
)]
|
|
public ?string $mobile = null;
|
|
|
|
// The numbers prefilled from BusPro, which are exempt from PhoneNumber.
|
|
public ?string $loadedPhone = null;
|
|
public ?string $loadedMobile = null;
|
|
|
|
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['voucher_step_2'])]
|
|
#[Assert\Email(message: 'Bitte eine gültige Adresse angeben', mode: 'strict', groups: ['voucher_step_2'])]
|
|
public ?string $email = null;
|
|
|
|
// Step 3: confirmation, reset on every visit so a stale tick never carries over
|
|
#[Assert\IsTrue(message: 'Bitte bestätige', groups: ['voucher_step_3'])]
|
|
public bool $termsAccepted = false;
|
|
|
|
#[Assert\IsTrue(message: 'Bitte bestätige', groups: ['voucher_step_3'])]
|
|
public bool $purchaseConfirmed = false;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->address = new Address();
|
|
$this->address->country = 'D';
|
|
}
|
|
|
|
public function isPhoneNewlyEntered(): bool
|
|
{
|
|
return AppAssert\PhoneNumber::isNewlyEntered($this->phone, $this->loadedPhone);
|
|
}
|
|
|
|
public function isMobileNewlyEntered(): bool
|
|
{
|
|
return AppAssert\PhoneNumber::isNewlyEntered($this->mobile, $this->loadedMobile);
|
|
}
|
|
|
|
public function getBuyerFullName(): string
|
|
{
|
|
return trim(sprintf('%s %s', $this->firstName ?? '', $this->lastName ?? ''));
|
|
}
|
|
|
|
public function hasBuyerData(): bool
|
|
{
|
|
return null !== $this->lastName && '' !== $this->lastName;
|
|
}
|
|
|
|
/**
|
|
* Copies the buyer fields from the signed-in account's BusPro record.
|
|
*
|
|
* The e-mail is the one the account signed in with, not the record's communication address:
|
|
* a BusPro address can carry several e-mails for different persons.
|
|
*/
|
|
public function prefillBuyer(PersonalData $personalData, ?string $loginEmail): void
|
|
{
|
|
$this->salutation = \in_array($personalData->salutation, [self::SALUTATION_MR, self::SALUTATION_MS], true)
|
|
? $personalData->salutation
|
|
: null;
|
|
$this->title = $personalData->title;
|
|
$this->lastName = $personalData->name;
|
|
$this->firstName = $personalData->firstName;
|
|
$this->address = clone $personalData->address;
|
|
$this->address->normalize();
|
|
$this->address->country ??= 'D';
|
|
$this->phone = $personalData->communication->phone;
|
|
$this->mobile = $personalData->communication->mobile;
|
|
$this->loadedPhone = $this->phone;
|
|
$this->loadedMobile = $this->mobile;
|
|
$this->email = $loginEmail;
|
|
}
|
|
}
|