306cfd34cd
LST-66 (2.3) backend. Behaviour-preserving move of the absences domain into
src/Module/Absence/. API operations, securities, routes and the 10 MCP tool
names are unchanged.
- 3 entities + 3 enums moved to Domain/{Entity,Enum}; user relations stay on
UserInterface. 3 repositories split into Domain/Repository interfaces +
Doctrine impls (bound in services.yaml); find() kept off interfaces
(findById instead).
- Pure services (AbsenceDayCalculator, PublicHolidayProvider) -> Domain/Service;
AbsenceBalanceService -> Application/Service; State (5), controllers (5),
10 MCP tools and AccrueLeaveCommand -> Infrastructure/.
- New LeaveProfileInterface contract (Shared) exposes the HR getters used by
AbsenceBalanceService/AccrueLeaveCommand; User implements it -> Absence no
longer imports the concrete Core User. MCP tools/command inject
UserRepositoryInterface (findById) instead of the concrete repository.
- Timestampable/Blamable added to AbsenceBalance and AbsencePolicy (additive
migration: created_at/updated_at + created_by/updated_by FK ON DELETE SET
NULL + COMMENT). AbsenceRequest untouched (already has createdAt/reviewedAt).
- AbsenceModule registered (id absence, 4 RBAC perms, not re-wired); doctrine
mapping added; team-absences sidebar item gated by the module.
161 tests green, mapping valid, no API route regression, cs-fixer clean.
76 lines
2.4 KiB
PHP
76 lines
2.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Module\Absence\Infrastructure\ApiPlatform\State;
|
|
|
|
use ApiPlatform\Metadata\Operation;
|
|
use ApiPlatform\State\ProviderInterface;
|
|
use App\Module\Absence\Domain\Entity\AbsenceBalance;
|
|
use App\Module\Absence\Domain\Repository\AbsenceBalanceRepositoryInterface;
|
|
use App\Shared\Domain\Contract\UserInterface;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
|
|
/**
|
|
* @implements ProviderInterface<AbsenceBalance>
|
|
*/
|
|
final readonly class AbsenceBalanceProvider implements ProviderInterface
|
|
{
|
|
public function __construct(
|
|
private EntityManagerInterface $entityManager,
|
|
private AbsenceBalanceRepositoryInterface $balanceRepository,
|
|
private Security $security,
|
|
) {}
|
|
|
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): AbsenceBalance|array|null
|
|
{
|
|
$user = $this->security->getUser();
|
|
assert($user instanceof UserInterface);
|
|
|
|
$isAdmin = $this->security->isGranted('ROLE_ADMIN');
|
|
|
|
if (isset($uriVariables['id'])) {
|
|
$balance = $this->balanceRepository->findById((int) $uriVariables['id']);
|
|
if (null === $balance) {
|
|
return null;
|
|
}
|
|
if (!$isAdmin && $balance->getUser() !== $user) {
|
|
return null;
|
|
}
|
|
|
|
return $balance;
|
|
}
|
|
|
|
$qb = $this->entityManager->getRepository(AbsenceBalance::class)
|
|
->createQueryBuilder('b')
|
|
->orderBy('b.type', 'ASC')
|
|
;
|
|
|
|
if (!$isAdmin) {
|
|
$qb->andWhere('b.user = :user')->setParameter('user', $user);
|
|
}
|
|
|
|
$filters = $context['filters'] ?? [];
|
|
|
|
if (isset($filters['type'])) {
|
|
$qb->andWhere('b.type = :type')->setParameter('type', $filters['type']);
|
|
}
|
|
if (isset($filters['period'])) {
|
|
$qb->andWhere('b.period = :period')->setParameter('period', $filters['period']);
|
|
}
|
|
if ($isAdmin && isset($filters['user'])) {
|
|
$qb->andWhere('b.user = :filterUser')
|
|
->setParameter('filterUser', self::extractId($filters['user']))
|
|
;
|
|
}
|
|
|
|
return $qb->getQuery()->getResult();
|
|
}
|
|
|
|
private static function extractId(string $value): int
|
|
{
|
|
return is_numeric($value) ? (int) $value : (int) basename($value);
|
|
}
|
|
}
|