feat(absences) : outils MCP CRUD pour les absences
Expose le module Absences via le serveur MCP et comble les trous CRUD existants (projets, groupes, métadonnées de tâches, clients, users RH). Absences (réutilise AbsenceDayCalculator + AbsenceBalanceService pour ne pas contourner la logique de soldes) : - list/get/create/review/cancel/delete-absence-request - list/update-absence-policy, list/update-absence-balance - create-absence-request prend un userId explicite (agir au nom d'un employé) ; review/cancel maintiennent les soldes (pending/taken) cohérents - AbsenceRequestRepository::findFiltered pour les filtres de liste Trous CRUD comblés : - delete-project, delete-group - CRUD tag, effort, priority - CRUD status (couplé au workflow, avec category) - CRUD client, get/update-user (champs RH, sans password ni roles) Sérialisation centralisée (Serializer::absenceRequest/Policy/Balance/client/userFull). Instructions MCP (mcp.yaml) mises à jour : statuts par workflow + domaine absences. Tests : tests/Functional/Mcp/AbsenceRequestLifecycleTest (création / approbation / annulation admin) vérifient le cycle complet et la cohérence des soldes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Mcp\Tool\TaskMeta;
|
||||
|
||||
use App\Enum\StatusCategory;
|
||||
use App\Repository\TaskStatusRepository;
|
||||
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-status', description: 'Update a task status (admin). Only provided fields change. category = todo|in_progress|blocked|review|done.')]
|
||||
class UpdateStatusTool
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TaskStatusRepository $statusRepository,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly Security $security,
|
||||
) {}
|
||||
|
||||
public function __invoke(
|
||||
int $id,
|
||||
?string $label = null,
|
||||
?string $category = null,
|
||||
?string $color = null,
|
||||
?int $position = null,
|
||||
?bool $isFinal = null,
|
||||
): string {
|
||||
if (!$this->security->isGranted('ROLE_ADMIN')) {
|
||||
throw new AccessDeniedException('Access denied: ROLE_ADMIN required.');
|
||||
}
|
||||
|
||||
$status = $this->statusRepository->find($id);
|
||||
if (null === $status) {
|
||||
throw new InvalidArgumentException(sprintf('TaskStatus with ID %d not found.', $id));
|
||||
}
|
||||
|
||||
if (null !== $label) {
|
||||
$status->setLabel($label);
|
||||
}
|
||||
if (null !== $category) {
|
||||
$status->setCategory(
|
||||
StatusCategory::tryFrom($category)
|
||||
?? throw new InvalidArgumentException(sprintf('Unknown status category "%s".', $category)),
|
||||
);
|
||||
}
|
||||
if (null !== $color) {
|
||||
$status->setColor($color);
|
||||
}
|
||||
if (null !== $position) {
|
||||
$status->setPosition($position);
|
||||
}
|
||||
if (null !== $isFinal) {
|
||||
$status->setIsFinal($isFinal);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return json_encode([
|
||||
'id' => $status->getId(),
|
||||
'label' => $status->getLabel(),
|
||||
'color' => $status->getColor(),
|
||||
'position' => $status->getPosition(),
|
||||
'isFinal' => $status->getIsFinal(),
|
||||
'category' => $status->getCategory()->value,
|
||||
'workflowId' => $status->getWorkflow()?->getId(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user