refactor(core) : final legacy cleanup — app is 100% modular
LST-60 (3.3). Closes the modular-monolith migration. src/Entity was already
empty; this removes the last legacy residue.
- Doctrine: drop the legacy "App" mapping (empty src/Entity). resolve_target_
entities already targets modules only.
- MCP User tools (Reference/) -> Core/Infrastructure/Mcp/Tool; MCP Serializer
-> Shared/Infrastructure/Mcp (33 usages repointed).
- Controllers (mark-all-read, notification unread-count, regenerate-api-token,
user-avatar) -> Core/Infrastructure/Controller. TokenEncryptor -> Shared/
Infrastructure/Service (11 usages). AppVersion resource+provider -> Shared.
ContractType enum -> Core/Domain/Enum.
- src/{Entity,State,Controller,Service,Enum,ApiResource} now empty; routes,
MCP tool names and public API unchanged.
180 tests green, mapping valid, no route regression, cs-fixer clean.
Note: final Malio visual harmonisation (subjective) left to the PO.
This commit is contained in:
@@ -4,10 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Application\Service\AbsenceBalanceService;
|
||||
use App\Module\Absence\Domain\Enum\AbsenceStatus;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceRequestRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Application\Service\AbsenceBalanceService;
|
||||
use App\Module\Absence\Domain\Entity\AbsenceRequest;
|
||||
use App\Module\Absence\Domain\Enum\AbsenceStatus;
|
||||
@@ -14,6 +13,7 @@ use App\Module\Absence\Domain\Repository\AbsencePolicyRepositoryInterface;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceRequestRepositoryInterface;
|
||||
use App\Module\Absence\Domain\Service\AbsenceDayCalculator;
|
||||
use App\Module\Core\Domain\Repository\UserRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceRequestRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,10 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Domain\Enum\AbsenceType;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceBalanceRepositoryInterface;
|
||||
use App\Module\Core\Domain\Repository\UserRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Domain\Repository\AbsencePolicyRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Domain\Enum\AbsenceStatus;
|
||||
use App\Module\Absence\Domain\Enum\AbsenceType;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceRequestRepositoryInterface;
|
||||
use App\Module\Core\Domain\Repository\UserRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Application\Service\AbsenceBalanceService;
|
||||
use App\Module\Absence\Domain\Enum\AbsenceStatus;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceRequestRepositoryInterface;
|
||||
use App\Shared\Domain\Contract\UserInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Domain\Repository\AbsenceBalanceRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Absence\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Absence\Domain\Repository\AbsencePolicyRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -11,7 +11,7 @@ use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Enum\ContractType;
|
||||
use App\Module\Core\Domain\Enum\ContractType;
|
||||
use App\Module\Core\Infrastructure\ApiPlatform\State\MeProvider;
|
||||
use App\Module\Core\Infrastructure\ApiPlatform\State\Processor\UserRbacProcessor;
|
||||
use App\Module\Core\Infrastructure\ApiPlatform\State\UserPasswordHasherProcessor;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Domain\Enum;
|
||||
|
||||
enum ContractType: string
|
||||
{
|
||||
case Cdi = 'CDI';
|
||||
case Cdd = 'CDD';
|
||||
case Internship = 'STAGE';
|
||||
case Apprentice = 'ALTERNANCE';
|
||||
case Other = 'AUTRE';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Cdi => 'CDI',
|
||||
self::Cdd => 'CDD',
|
||||
self::Internship => 'Stage',
|
||||
self::Apprentice => 'Alternance',
|
||||
self::Other => 'Autre',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Controller;
|
||||
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineNotificationRepository;
|
||||
use App\Shared\Domain\Contract\UserInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class MarkAllReadController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DoctrineNotificationRepository $notificationRepository,
|
||||
) {}
|
||||
|
||||
#[Route('/api/notifications/mark-all-read', name: 'notification_mark_all_read', methods: ['POST'], priority: 1)]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function __invoke(): Response
|
||||
{
|
||||
/** @var UserInterface $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$this->notificationRepository->markAllReadByUser($user);
|
||||
|
||||
return new Response(null, Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Controller;
|
||||
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineNotificationRepository;
|
||||
use App\Shared\Domain\Contract\UserInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class NotificationUnreadCountController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DoctrineNotificationRepository $notificationRepository,
|
||||
) {}
|
||||
|
||||
#[Route('/api/notifications/unread-count', name: 'notification_unread_count', methods: ['GET'], priority: 1)]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function __invoke(): JsonResponse
|
||||
{
|
||||
/** @var UserInterface $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$count = $this->notificationRepository->countUnreadByUser($user);
|
||||
|
||||
return new JsonResponse(['count' => $count]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Controller;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
use function bin2hex;
|
||||
use function random_bytes;
|
||||
|
||||
class RegenerateApiTokenController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
#[Route('/api/me/regenerate-api-token', name: 'me_regenerate_api_token', methods: ['POST'], priority: 1)]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function __invoke(): JsonResponse
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$user->setApiToken($token);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(['apiToken' => $token]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Controller;
|
||||
|
||||
use App\Module\Core\Domain\Entity\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
class UserAvatarController extends AbstractController
|
||||
{
|
||||
private const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB
|
||||
private const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
|
||||
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly string $avatarUploadDir,
|
||||
) {}
|
||||
|
||||
#[Route('/api/users/{id}/avatar', name: 'user_avatar_upload', methods: ['POST'], priority: 1)]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function upload(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$user = $this->findUserOrFail($id);
|
||||
$this->assertCanManageAvatar($user);
|
||||
|
||||
$file = $request->files->get('file');
|
||||
|
||||
if (null === $file || !$file->isValid()) {
|
||||
throw new BadRequestHttpException('No valid file uploaded.');
|
||||
}
|
||||
|
||||
if ($file->getSize() > self::MAX_FILE_SIZE) {
|
||||
throw new BadRequestHttpException('File size exceeds 5 MB limit.');
|
||||
}
|
||||
|
||||
$mimeType = $file->getMimeType();
|
||||
|
||||
if (!in_array($mimeType, self::ALLOWED_MIME_TYPES, true)) {
|
||||
throw new BadRequestHttpException('Invalid file type. Allowed: JPEG, PNG, WebP, GIF.');
|
||||
}
|
||||
|
||||
// Delete previous avatar file if exists
|
||||
$this->deleteAvatarFile($user);
|
||||
|
||||
$extension = $file->guessExtension() ?? 'bin';
|
||||
$fileName = Uuid::v4()->toRfc4122().'.'.$extension;
|
||||
|
||||
if (!is_dir($this->avatarUploadDir)) {
|
||||
mkdir($this->avatarUploadDir, 0o775, true);
|
||||
}
|
||||
|
||||
$file->move($this->avatarUploadDir, $fileName);
|
||||
|
||||
$user->setAvatarFileName($fileName);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(['avatarUrl' => $user->getAvatarUrl()]);
|
||||
}
|
||||
|
||||
#[Route('/api/users/{id}/avatar', name: 'user_avatar_serve', methods: ['GET'], priority: 1)]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function serve(int $id): BinaryFileResponse
|
||||
{
|
||||
$user = $this->findUserOrFail($id);
|
||||
|
||||
if (null === $user->getAvatarFileName()) {
|
||||
throw new NotFoundHttpException('No avatar set.');
|
||||
}
|
||||
|
||||
$filePath = $this->avatarUploadDir.'/'.$user->getAvatarFileName();
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
throw new NotFoundHttpException('Avatar file not found on disk.');
|
||||
}
|
||||
|
||||
$response = new BinaryFileResponse($filePath);
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $user->getAvatarFileName());
|
||||
$extension = pathinfo($user->getAvatarFileName(), PATHINFO_EXTENSION);
|
||||
$mimeMap = ['jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', 'gif' => 'image/gif'];
|
||||
$response->headers->set('Content-Type', $mimeMap[$extension] ?? 'application/octet-stream');
|
||||
$response->headers->set('Cache-Control', 'public, max-age=86400');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
#[Route('/api/users/{id}/avatar', name: 'user_avatar_delete', methods: ['DELETE'], priority: 1)]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
public function delete(int $id): Response
|
||||
{
|
||||
$user = $this->findUserOrFail($id);
|
||||
$this->assertCanManageAvatar($user);
|
||||
|
||||
$this->deleteAvatarFile($user);
|
||||
$user->setAvatarFileName(null);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new Response(null, Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
private function findUserOrFail(int $id): User
|
||||
{
|
||||
$user = $this->entityManager->getRepository(User::class)->find($id);
|
||||
|
||||
if (null === $user) {
|
||||
throw new NotFoundHttpException('User not found.');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function assertCanManageAvatar(User $user): void
|
||||
{
|
||||
$currentUser = $this->getUser();
|
||||
|
||||
if ($currentUser !== $user && !$this->isGranted('ROLE_ADMIN')) {
|
||||
throw new AccessDeniedHttpException('You can only manage your own avatar.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteAvatarFile(User $user): void
|
||||
{
|
||||
if (null === $user->getAvatarFileName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filePath = $this->avatarUploadDir.'/'.$user->getAvatarFileName();
|
||||
|
||||
if (file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
#[McpTool(name: 'get-user', description: 'Get a user by ID with full HR profile (employee flag, hire/end date, contract, work-time ratio, leave entitlement, reference period, family situation).')]
|
||||
class GetUserTool
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DoctrineUserRepository $userRepository,
|
||||
private readonly Security $security,
|
||||
) {}
|
||||
|
||||
public function __invoke(int $id): string
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_ADMIN')) {
|
||||
throw new AccessDeniedException('Access denied: ROLE_ADMIN required.');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->find($id);
|
||||
if (null === $user) {
|
||||
throw new InvalidArgumentException(sprintf('User with ID %d not found.', $id));
|
||||
}
|
||||
|
||||
return json_encode(Serializer::userFull($user));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
#[McpTool(name: 'list-users', description: 'List all users with their IDs and usernames. Use this to discover valid user IDs for assignee or time entry parameters.')]
|
||||
class ListUsersTool
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DoctrineUserRepository $userRepository,
|
||||
private readonly Security $security,
|
||||
) {}
|
||||
|
||||
public function __invoke(): string
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_ADMIN')) {
|
||||
throw new AccessDeniedException('Access denied: ROLE_ADMIN required.');
|
||||
}
|
||||
|
||||
$users = $this->userRepository->findBy([], ['username' => 'ASC']);
|
||||
|
||||
return json_encode(array_map(fn ($user) => [
|
||||
'id' => $user->getId(),
|
||||
'username' => $user->getUsername(),
|
||||
], $users));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Core\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Module\Core\Domain\Enum\ContractType;
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
#[McpTool(name: 'update-user', description: 'Update a user HR/profile fields (admin). Does NOT change password or roles. contractType = CDI|CDD|STAGE|ALTERNANCE|AUTRE. hireDate/endDate as YYYY-MM-DD. referencePeriodStart as MM-DD (e.g. 06-01).')]
|
||||
class UpdateUserTool
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DoctrineUserRepository $userRepository,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly Security $security,
|
||||
) {}
|
||||
|
||||
public function __invoke(
|
||||
int $id,
|
||||
?bool $isEmployee = null,
|
||||
?string $hireDate = null,
|
||||
?string $endDate = null,
|
||||
?string $contractType = null,
|
||||
?float $workTimeRatio = null,
|
||||
?float $annualLeaveDays = null,
|
||||
?string $referencePeriodStart = null,
|
||||
?float $initialLeaveBalance = null,
|
||||
): string {
|
||||
if (!$this->security->isGranted('ROLE_ADMIN')) {
|
||||
throw new AccessDeniedException('Access denied: ROLE_ADMIN required.');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->find($id);
|
||||
if (null === $user) {
|
||||
throw new InvalidArgumentException(sprintf('User with ID %d not found.', $id));
|
||||
}
|
||||
|
||||
if (null !== $isEmployee) {
|
||||
$user->setIsEmployee($isEmployee);
|
||||
}
|
||||
if (null !== $hireDate) {
|
||||
$user->setHireDate(new DateTimeImmutable($hireDate));
|
||||
}
|
||||
if (null !== $endDate) {
|
||||
$user->setEndDate(new DateTimeImmutable($endDate));
|
||||
}
|
||||
if (null !== $contractType) {
|
||||
$user->setContractType(
|
||||
ContractType::tryFrom($contractType)
|
||||
?? throw new InvalidArgumentException(sprintf('Unknown contract type "%s".', $contractType)),
|
||||
);
|
||||
}
|
||||
if (null !== $workTimeRatio) {
|
||||
$user->setWorkTimeRatio($workTimeRatio);
|
||||
}
|
||||
if (null !== $annualLeaveDays) {
|
||||
$user->setAnnualLeaveDays($annualLeaveDays);
|
||||
}
|
||||
if (null !== $referencePeriodStart) {
|
||||
$user->setReferencePeriodStart($referencePeriodStart);
|
||||
}
|
||||
if (null !== $initialLeaveBalance) {
|
||||
$user->setInitialLeaveBalance($initialLeaveBalance);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return json_encode(Serializer::userFull($user));
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Entity\Client;
|
||||
use App\Module\Directory\Domain\Enum\ProspectStatus;
|
||||
use App\Module\Directory\Domain\Repository\ProspectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Entity\Client;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Entity\Prospect;
|
||||
use App\Module\Directory\Domain\Enum\ProspectStatus;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Repository\ClientRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Repository\ProspectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Enum\ProspectStatus;
|
||||
use App\Module\Directory\Domain\Repository\ProspectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Repository\ClientRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Directory\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Enum\ProspectStatus;
|
||||
use App\Module\Directory\Domain\Repository\ProspectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Integration\Domain\Entity\BookStackConfiguration;
|
||||
use App\Module\Integration\Domain\Repository\BookStackConfigurationRepositoryInterface;
|
||||
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackSettings;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final readonly class BookStackSettingsProcessor implements ProcessorInterface
|
||||
|
||||
@@ -9,7 +9,7 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Integration\Domain\Entity\GiteaConfiguration;
|
||||
use App\Module\Integration\Domain\Repository\GiteaConfigurationRepositoryInterface;
|
||||
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaSettings;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final readonly class GiteaSettingsProcessor implements ProcessorInterface
|
||||
|
||||
@@ -9,7 +9,7 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Integration\Domain\Entity\ShareConfiguration;
|
||||
use App\Module\Integration\Domain\Repository\ShareConfigurationRepositoryInterface;
|
||||
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ShareSettings;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final readonly class ShareSettingsProcessor implements ProcessorInterface
|
||||
|
||||
@@ -9,7 +9,7 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Integration\Domain\Entity\ZimbraConfiguration;
|
||||
use App\Module\Integration\Domain\Repository\ZimbraConfigurationRepositoryInterface;
|
||||
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ZimbraSettings;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final readonly class ZimbraSettingsProcessor implements ProcessorInterface
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Module\Integration\Infrastructure\Service;
|
||||
use App\Module\Integration\Domain\Entity\BookStackConfiguration;
|
||||
use App\Module\Integration\Domain\Exception\BookStackApiException;
|
||||
use App\Module\Integration\Domain\Repository\BookStackConfigurationRepositoryInterface;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
@@ -9,7 +9,7 @@ use App\Module\Integration\Domain\Exception\GiteaApiException;
|
||||
use App\Module\Integration\Domain\Repository\GiteaConfigurationRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Entity\Project;
|
||||
use App\Module\ProjectManagement\Domain\Entity\Task;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Symfony\Component\String\Slugger\AsciiSlugger;
|
||||
use Symfony\Component\String\Slugger\SluggerInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\Module\Integration\Domain\Service\FileEntry;
|
||||
use App\Module\Integration\Domain\Service\FileSource;
|
||||
use App\Module\Integration\Domain\Service\SharePathResolver;
|
||||
use App\Module\Integration\Domain\Service\ShareTestResult;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Icewind\SMB\BasicAuth;
|
||||
use Icewind\SMB\IFileInfo;
|
||||
use Icewind\SMB\IShare;
|
||||
|
||||
@@ -9,7 +9,7 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Mail\Domain\Entity\MailConfiguration;
|
||||
use App\Module\Mail\Domain\Repository\MailConfigurationRepositoryInterface;
|
||||
use App\Module\Mail\Infrastructure\ApiPlatform\Resource\MailSettings;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final readonly class MailSettingsProcessor implements ProcessorInterface
|
||||
|
||||
@@ -11,7 +11,7 @@ use App\Module\Mail\Application\Dto\MailMessageHeaderDto;
|
||||
use App\Module\Mail\Domain\Exception\MailProviderException;
|
||||
use App\Module\Mail\Domain\Provider\MailProviderInterface;
|
||||
use App\Module\Mail\Domain\Repository\MailConfigurationRepositoryInterface;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SodiumException;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Project;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Repository\ClientRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Entity\Project;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Project;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Project;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Project;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Directory\Domain\Repository\ClientRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Task;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use App\Module\ProjectManagement\Domain\Entity\Task;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
@@ -15,6 +14,7 @@ use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskStatusRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskTagRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Infrastructure\Service\CalDavService;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Task;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Task;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Task;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskEffortRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskGroupRepositoryInterface;
|
||||
@@ -13,6 +12,7 @@ use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskStatusRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskTagRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Infrastructure\Service\CalDavService;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\TaskMeta;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Entity\TaskGroup;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\TaskMeta;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskGroupRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\TaskMeta;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskGroupRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
@@ -8,7 +8,7 @@ use App\Module\Integration\Domain\Repository\ZimbraConfigurationRepositoryInterf
|
||||
use App\Module\ProjectManagement\Domain\Entity\Task;
|
||||
use App\Module\ProjectManagement\Domain\Entity\TaskRecurrence;
|
||||
use App\Module\ProjectManagement\Domain\Enum\RecurrenceType;
|
||||
use App\Service\TokenEncryptor;
|
||||
use App\Shared\Infrastructure\Service\TokenEncryptor;
|
||||
use DateTimeZone;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
|
||||
@@ -4,13 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\TimeTracking\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\Core\Infrastructure\Doctrine\DoctrineUserRepository;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskTagRepositoryInterface;
|
||||
use App\Module\TimeTracking\Domain\Entity\TimeEntry;
|
||||
use App\Module\TimeTracking\Domain\Repository\TimeEntryRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -4,8 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\TimeTracking\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\TimeTracking\Domain\Repository\TimeEntryRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Module\TimeTracking\Infrastructure\Mcp\Tool;
|
||||
|
||||
use App\Mcp\Tool\Serializer;
|
||||
use App\Module\ProjectManagement\Domain\Repository\ProjectRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
|
||||
use App\Module\ProjectManagement\Domain\Repository\TaskTagRepositoryInterface;
|
||||
use App\Module\TimeTracking\Domain\Repository\TimeEntryRepositoryInterface;
|
||||
use App\Shared\Infrastructure\Mcp\Serializer;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
Reference in New Issue
Block a user