2b148fa65a
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>
75 lines
2.3 KiB
PHP
75 lines
2.3 KiB
PHP
<?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(),
|
|
]);
|
|
}
|
|
}
|