feat: forced teamer data verification after configured due dates

addresses #869b33c7p
This commit is contained in:
Björn Fromme
2026-03-15 12:42:28 +01:00
parent 20da6a0afc
commit 518bc7339f
19 changed files with 357 additions and 14 deletions
@@ -0,0 +1,73 @@
<?php
namespace App\RequiredTeamerCheck;
use App\Entity\Teamer;
use App\Entity\Upload;
use App\Entity\User;
use App\RequiredTeamerCheck\RequiredTeamerCheckInterface;
class DriverLicenseRequiredCheck implements RequiredTeamerCheckInterface
{
public function getCode(): string
{
return 'driver_license';
}
public function appliesTo(User $user): bool
{
return true === $user->hasRole('ROLE_TEAMER');
}
public function isSatisfied(User $user): bool
{
$teamer = $user->getTeamer();
if (null === $teamer) {
return false;
}
$declaration = $teamer->getDriverLicenseDeclaration();
if (null === $declaration) {
return false;
}
if (false === $declaration) {
return true;
}
if (true === $teamer->isDriverLicenseVerified()) {
return true;
}
if (Teamer::DRIVER_LICENSE_STATUS_APPROVED === $teamer->getDriverLicenseStatus()) {
return true;
}
$upload = $teamer->getDriverLicenseUpload();
if (null === $upload) {
return false;
}
if (Upload::TYPE_DRIVER_LICENSE !== $upload->getType()) {
return false;
}
if (Teamer::DRIVER_LICENSE_STATUS_DENIED === $teamer->getDriverLicenseStatus()) {
return false;
}
if (Upload::STATUS_REJECTED === $upload->getStatus()) {
return false;
}
return true;
}
public function getRouteName(): string
{
return 'app_teamer_check_driver_license';
}
}
@@ -0,0 +1,77 @@
<?php
namespace App\RequiredTeamerCheck;
use App\Entity\User;
use Carbon\CarbonImmutable;
class PersonalDataVerificationRequiredCheck implements RequiredTeamerCheckInterface
{
public function __construct(
private readonly array $dataVerificationDeadlines = ['04-01', '10-01'],
) {
}
public function getCode(): string
{
return 'personal_data_verification';
}
public function appliesTo(User $user): bool
{
return true === $user->hasRole('ROLE_TEAMER');
}
public function isSatisfied(User $user): bool
{
$teamer = $user->getTeamer();
if (null === $teamer) {
return false;
}
return false === $this->isVerificationRequired($teamer->getDataVerifiedAt());
}
public function getRouteName(): string
{
return 'app_teamer_profile_index';
}
private function isVerificationRequired(?\DateTimeImmutable $dataVerifiedAt): bool
{
$deadline = $this->getCurrentDeadline(CarbonImmutable::now()->startOfDay());
if (null === $deadline) {
return false;
}
if (null === $dataVerifiedAt) {
return true;
}
return CarbonImmutable::instance($dataVerifiedAt) < $deadline;
}
private function getCurrentDeadline(CarbonImmutable $now): ?CarbonImmutable
{
$deadlines = array_filter(array_map('trim', $this->dataVerificationDeadlines));
rsort($deadlines);
foreach ($deadlines as $date) {
$deadline = CarbonImmutable::createFromFormat('Y-m-d', sprintf('%s-%s', $now->format('Y'), $date));
if (false === $deadline instanceof CarbonImmutable) {
continue;
}
$deadline = $deadline->startOfDay();
if ($deadline <= $now) {
return $deadline;
}
}
return null;
}
}
@@ -0,0 +1,16 @@
<?php
namespace App\RequiredTeamerCheck;
use App\Entity\User;
interface RequiredTeamerCheckInterface
{
public function getCode(): string;
public function appliesTo(User $user): bool;
public function isSatisfied(User $user): bool;
public function getRouteName(): string;
}
@@ -0,0 +1,31 @@
<?php
namespace App\RequiredTeamerCheck;
use App\Entity\User;
use App\RequiredTeamerCheck\RequiredTeamerCheckInterface;
class RequiredTeamerCheckRegistry
{
/**
* @param iterable<RequiredTeamerCheckInterface> $checks
*/
public function __construct(private readonly iterable $checks)
{
}
public function getFirstUnresolvedCheck(User $user): ?RequiredTeamerCheckInterface
{
foreach ($this->checks as $check) {
if (false === $check->appliesTo($user)) {
continue;
}
if (false === $check->isSatisfied($user)) {
return $check;
}
}
return null;
}
}