feat: fold purchase and promo voucher fields into one code field

This commit is contained in:
fromme committed 2026-10-06 10:41:30 +02:00
1 parent 93512b8345
commit 21d756a2e3
21 files changed
+801 -432

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Enum\VoucherCodeKind;
/**
* Tells promo codes and purchase codes apart, so the booking form needs only one voucher field.
*
* Promo codes are prefixed "R-". Purchase codes (Kauf and Kulanz) consist of uppercase letters only
* and never contain a hyphen, so the prefix cannot collide with them. Promo codes issued before the
* prefix was introduced are recognised from `legacy_promo_voucher_codes` until their date has passed.
*/
class VoucherCodeClassifier
{
public const PROMO_PREFIX = 'R-';
private const MAX_LENGTH = 50;
/** @var array<string, \DateTimeImmutable> Normalised code => last day the code is recognised */
private readonly array $legacyPromoCodes;
/**
* @param array<string, string> $legacyPromoCodes Code => last day it is accepted (Y-m-d)
*/
public function __construct(array $legacyPromoCodes = [])
{
$codes = [];
foreach ($legacyPromoCodes as $code => $lastDay) {
$codes[$this->normalize((string) $code)] = new \DateTimeImmutable($lastDay);
}
$this->legacyPromoCodes = $codes;
}
/**
* Codes are matched case-insensitively; BusPro receives them in uppercase.
*/
public function normalize(string $code): string
{
return mb_strtoupper(trim($code));
}
public function classify(string $code, \DateTimeImmutable $today = new \DateTimeImmutable('today')): VoucherCodeKind
{
$code = $this->normalize($code);
if ('' === $code || mb_strlen($code) > self::MAX_LENGTH || 1 !== preg_match('/^[A-Z0-9\-]+$/', $code)) {
return VoucherCodeKind::Malformed;
}
if (str_starts_with($code, self::PROMO_PREFIX)) {
return VoucherCodeKind::Promo;
}
$lastDay = $this->legacyPromoCodes[$code] ?? null;
if (null !== $lastDay && $lastDay >= $today) {
return VoucherCodeKind::Promo;
}
if (str_contains($code, '-')) {
return VoucherCodeKind::Malformed;
}
return VoucherCodeKind::Purchase;
}
}