feat: voucher purchase flow
This commit is contained in:
1 parent
a628e96d06
commit
f3dd122678
70 files changed
+3672
-122
No files matched your search
@@ -0,0 +1,124 @@
|
||||
<?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 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;
|
||||
|
||||
public ?string $phone = null;
|
||||
|
||||
public ?string $mobile = 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 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->email = $loginEmail;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user