[#SIRH-17] Ajouter un système de log des actions utilisateurs (#9)
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #9 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #9.
This commit is contained in:
@@ -13,6 +13,7 @@ use App\Entity\User;
|
||||
use App\Enum\HalfDay;
|
||||
use App\Repository\Contract\AbsenceReadRepositoryInterface;
|
||||
use App\Repository\Contract\WorkHourReadRepositoryInterface;
|
||||
use App\Service\AuditLogger;
|
||||
use App\Service\PublicHolidayServiceInterface;
|
||||
use DateInterval;
|
||||
use DatePeriod;
|
||||
@@ -33,6 +34,7 @@ final readonly class AbsenceWriteProcessor implements ProcessorInterface
|
||||
private WorkHourReadRepositoryInterface $workHourRepository,
|
||||
private Security $security,
|
||||
private PublicHolidayServiceInterface $publicHolidayService,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
|
||||
@@ -54,6 +56,21 @@ final readonly class AbsenceWriteProcessor implements ProcessorInterface
|
||||
throw new ConflictHttpException('Impossible de modifier une absence sur une période validée.');
|
||||
}
|
||||
|
||||
$typeName = $data->getType()?->getLabel() ?? 'inconnu';
|
||||
$startDate = $data->getStartDate()->format('d/m/Y');
|
||||
$endDate = $data->getEndDate()->format('d/m/Y');
|
||||
$empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
||||
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
'delete',
|
||||
'absence',
|
||||
$data->getId(),
|
||||
sprintf('Absence %s supprimée pour %s du %s au %s', $typeName, $empName, $startDate, $endDate),
|
||||
['old' => ['type' => $typeName, 'start' => $startDate, 'end' => $endDate, 'startHalf' => $data->getStartHalf()->value, 'endHalf' => $data->getEndHalf()->value, 'comment' => $data->getComment()]],
|
||||
DateTimeImmutable::createFromInterface($data->getStartDate()),
|
||||
);
|
||||
|
||||
$this->entityManager->remove($data);
|
||||
$this->entityManager->flush();
|
||||
|
||||
@@ -110,6 +127,21 @@ final readonly class AbsenceWriteProcessor implements ProcessorInterface
|
||||
$this->entityManager->persist($absence);
|
||||
}
|
||||
|
||||
$typeName = $data->getType()?->getLabel() ?? 'inconnu';
|
||||
$startDate = $data->getStartDate()->format('d/m/Y');
|
||||
$endDate = $data->getEndDate()->format('d/m/Y');
|
||||
$empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
||||
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
'create',
|
||||
'absence',
|
||||
null,
|
||||
sprintf('Absence %s créée pour %s du %s au %s', $typeName, $empName, $startDate, $endDate),
|
||||
['new' => ['type' => $typeName, 'start' => $startDate, 'end' => $endDate, 'startHalf' => $data->getStartHalf()->value, 'endHalf' => $data->getEndHalf()->value, 'comment' => $data->getComment()]],
|
||||
DateTimeImmutable::createFromInterface($data->getStartDate()),
|
||||
);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $data;
|
||||
|
||||
73
src/State/AuditLogProvider.php
Normal file
73
src/State/AuditLogProvider.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Repository\AuditLogRepository;
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class AuditLogProvider implements ProviderInterface
|
||||
{
|
||||
private const PER_PAGE = 50;
|
||||
|
||||
public function __construct(
|
||||
private readonly RequestStack $requestStack,
|
||||
private readonly AuditLogRepository $auditLogRepository,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): JsonResponse
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
if (!$request) {
|
||||
return new JsonResponse(['items' => [], 'total' => 0]);
|
||||
}
|
||||
|
||||
$employeeId = $request->query->get('employeeId');
|
||||
$from = $request->query->get('from');
|
||||
$to = $request->query->get('to');
|
||||
$entityType = $request->query->get('entityType');
|
||||
$page = max(1, (int) $request->query->get('page', '1'));
|
||||
|
||||
$empId = $employeeId ? (int) $employeeId : null;
|
||||
$fromDt = $from ? new DateTimeImmutable($from) : null;
|
||||
$toDt = $to ? new DateTimeImmutable($to) : null;
|
||||
$type = $entityType ?: null;
|
||||
$offset = ($page - 1) * self::PER_PAGE;
|
||||
|
||||
$total = $this->auditLogRepository->countByFilters($empId, $fromDt, $toDt, $type);
|
||||
$logs = $this->auditLogRepository->findByFilters($empId, $fromDt, $toDt, $type, self::PER_PAGE, $offset);
|
||||
|
||||
$items = [];
|
||||
foreach ($logs as $log) {
|
||||
$employee = $log->getEmployee();
|
||||
$employeeName = $employee
|
||||
? trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''))
|
||||
: null;
|
||||
|
||||
$items[] = [
|
||||
'id' => $log->getId(),
|
||||
'employeeName' => $employeeName,
|
||||
'employeeId' => $employee?->getId(),
|
||||
'username' => $log->getUsername(),
|
||||
'action' => $log->getAction(),
|
||||
'entityType' => $log->getEntityType(),
|
||||
'description' => $log->getDescription(),
|
||||
'changes' => $log->getChanges(),
|
||||
'affectedDate' => $log->getAffectedDate()?->format('Y-m-d'),
|
||||
'createdAt' => $log->getCreatedAt()->format('Y-m-d H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'perPage' => self::PER_PAGE,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Entity\ContractSuspension;
|
||||
use App\Entity\EmployeeContractPeriod;
|
||||
use App\Service\AuditLogger;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
@@ -19,6 +20,7 @@ final readonly class ContractSuspensionWriteProcessor implements ProcessorInterf
|
||||
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
|
||||
private ProcessorInterface $persistProcessor,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(
|
||||
@@ -46,7 +48,26 @@ final readonly class ContractSuspensionWriteProcessor implements ProcessorInterf
|
||||
|
||||
$this->validate($data, $period);
|
||||
|
||||
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
|
||||
$isNew = null === $data->getId();
|
||||
$employee = $period->getEmployee();
|
||||
$empName = $employee ? trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? '')) : '';
|
||||
$start = $data->getStartDate()->format('d/m/Y');
|
||||
$end = $data->getEndDate()?->format('d/m/Y') ?? 'indéfinie';
|
||||
|
||||
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
|
||||
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
$isNew ? 'create' : 'update',
|
||||
'contract_suspension',
|
||||
$data->getId(),
|
||||
sprintf('Suspension %s pour %s du %s au %s', $isNew ? 'créée' : 'modifiée', $empName, $start, $end),
|
||||
['new' => ['start' => $start, 'end' => $end]],
|
||||
DateTimeImmutable::createFromInterface($data->getStartDate()),
|
||||
);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function validate(ContractSuspension $suspension, EmployeeContractPeriod $period): void
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Enum\ContractType;
|
||||
use App\Enum\LeaveRuleCode;
|
||||
use App\Repository\EmployeeLeaveBalanceRepository;
|
||||
use App\Repository\EmployeeRepository;
|
||||
use App\Service\AuditLogger;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
@@ -24,6 +25,7 @@ final readonly class EmployeeFractionedDaysProcessor implements ProcessorInterfa
|
||||
private EmployeeRepository $employeeRepository,
|
||||
private EmployeeLeaveBalanceRepository $leaveBalanceRepository,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): EmployeeFractionedDaysInput
|
||||
@@ -57,6 +59,17 @@ final readonly class EmployeeFractionedDaysProcessor implements ProcessorInterfa
|
||||
|
||||
$balance->setFractionedDays($data->fractionedDays);
|
||||
$balance->touch();
|
||||
|
||||
$empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
'update',
|
||||
'fractioned_days',
|
||||
$balance->getId(),
|
||||
sprintf('Jours fractionnés modifiés pour %s (année %d) : %s', $empName, $year, (string) $data->fractionedDays),
|
||||
['new' => ['fractionedDays' => $data->fractionedDays, 'year' => $year]],
|
||||
);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
$data->year = $year;
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Entity\Employee;
|
||||
use App\Entity\EmployeeRttPayment;
|
||||
use App\Repository\EmployeeRepository;
|
||||
use App\Repository\EmployeeRttPaymentRepository;
|
||||
use App\Service\AuditLogger;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
@@ -22,6 +23,7 @@ final readonly class EmployeeRttPaymentProcessor implements ProcessorInterface
|
||||
private EmployeeRepository $employeeRepository,
|
||||
private EmployeeRttPaymentRepository $rttPaymentRepository,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): EmployeeRttPaymentInput
|
||||
@@ -61,6 +63,17 @@ final readonly class EmployeeRttPaymentProcessor implements ProcessorInterface
|
||||
$payment->setBase50Minutes($data->base50Minutes);
|
||||
$payment->setBonus50Minutes($data->bonus50Minutes);
|
||||
$payment->touch();
|
||||
|
||||
$empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
'update',
|
||||
'rtt_payment',
|
||||
$payment->getId(),
|
||||
sprintf('Paiement RTT modifié pour %s (%02d/%d)', $empName, $data->month, $year),
|
||||
['new' => ['month' => $data->month, 'year' => $year, 'base25' => $data->base25Minutes, 'bonus25' => $data->bonus25Minutes, 'base50' => $data->base50Minutes, 'bonus50' => $data->bonus50Minutes]],
|
||||
);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
$data->year = $year;
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Entity\Contract;
|
||||
use App\Entity\Employee;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Repository\Contract\EmployeeContractPeriodReadRepositoryInterface;
|
||||
use App\Service\AuditLogger;
|
||||
use App\Service\Contracts\EmployeeContractChangeRequestFactory;
|
||||
use App\Service\Contracts\EmployeeContractPeriodManagerInterface;
|
||||
use DateTimeImmutable;
|
||||
@@ -29,6 +30,7 @@ final readonly class EmployeeWriteProcessor implements ProcessorInterface
|
||||
private EmployeeContractPeriodReadRepositoryInterface $periodRepository,
|
||||
private EmployeeContractChangeRequestFactory $changeRequestFactory,
|
||||
private EmployeeContractPeriodManagerInterface $periodManager,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(
|
||||
@@ -72,6 +74,17 @@ final readonly class EmployeeWriteProcessor implements ProcessorInterface
|
||||
$data->setEntryDate($startDate);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$empName = trim(($data->getLastName() ?? '').' '.($data->getFirstName() ?? ''));
|
||||
$this->auditLogger->log(
|
||||
$data,
|
||||
'create',
|
||||
'employee',
|
||||
$data->getId(),
|
||||
sprintf('Employé %s créé (contrat: %s)', $empName, $currentContract->getName() ?? ''),
|
||||
['new' => ['name' => $empName, 'contract' => $currentContract->getName(), 'nature' => $nature->value, 'startDate' => $startDate->format('d/m/Y')]],
|
||||
);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -79,6 +92,17 @@ final readonly class EmployeeWriteProcessor implements ProcessorInterface
|
||||
return $result;
|
||||
}
|
||||
|
||||
$empName = trim(($data->getLastName() ?? '').' '.($data->getFirstName() ?? ''));
|
||||
$this->auditLogger->log(
|
||||
$data,
|
||||
'update',
|
||||
'employee',
|
||||
$data->getId(),
|
||||
sprintf('Contrat modifié pour %s : %s → %s', $empName, $previousContract?->getName() ?? 'aucun', $currentContract->getName() ?? ''),
|
||||
['old' => ['contract' => $previousContract?->getName()], 'new' => ['contract' => $currentContract->getName()]],
|
||||
);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$todayPeriod = $this->periodRepository->findOneCoveringDate($data, $today);
|
||||
$effectivePeriod = $todayPeriod ?? $this->periodRepository->findLatestPeriod($data);
|
||||
$currentPeriodContract = $effectivePeriod?->getContract();
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Entity\WorkHour;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Repository\WorkHourRepository;
|
||||
use App\Security\EmployeeScopeService;
|
||||
use App\Service\AuditLogger;
|
||||
use App\Service\WorkHours\WorkHourBulkValidationExecutor;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -30,6 +31,7 @@ final readonly class WorkHourBulkSiteValidationProcessor implements ProcessorInt
|
||||
private UserRepository $userRepository,
|
||||
private EmployeeScopeService $employeeScopeService,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(
|
||||
@@ -61,6 +63,21 @@ final readonly class WorkHourBulkSiteValidationProcessor implements ProcessorInt
|
||||
}
|
||||
);
|
||||
|
||||
if ($result->updated > 0) {
|
||||
$workDate = DateTimeImmutable::createFromFormat('Y-m-d', $data->workDate);
|
||||
$action = $data->isSiteValid ? 'validé' : 'dévalidé';
|
||||
|
||||
$this->auditLogger->log(
|
||||
null,
|
||||
'site_validate',
|
||||
'work_hour',
|
||||
null,
|
||||
sprintf('Validation site %s pour %d employé(s) le %s', $action, $result->updated, $data->workDate),
|
||||
['employeeIds' => $data->employeeIds, 'isSiteValid' => $data->isSiteValid],
|
||||
$workDate ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
if ($data->isSiteValid && $result->updated > 0) {
|
||||
$this->createNotificationsIfSiteFullyValidated($user, $data->workDate);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Enum\TrackingMode;
|
||||
use App\Repository\Contract\AbsenceReadRepositoryInterface;
|
||||
use App\Repository\EmployeeRepository;
|
||||
use App\Repository\WorkHourRepository;
|
||||
use App\Service\AuditLogger;
|
||||
use App\Service\Contracts\EmployeeContractResolver;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -31,6 +32,7 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
||||
private WorkHourRepository $workHourRepository,
|
||||
private AbsenceReadRepositoryInterface $absenceRepository,
|
||||
private EmployeeContractResolver $contractResolver,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(
|
||||
@@ -137,9 +139,20 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
||||
|
||||
$is4hContract = 4 === $contract->getWeeklyHours();
|
||||
|
||||
$empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
||||
|
||||
if ($this->isEntryEmpty($normalized)) {
|
||||
// Convention choisie: une ligne vide supprime l'enregistrement existant.
|
||||
if ($existing) {
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
'delete',
|
||||
'work_hour',
|
||||
$existing->getId(),
|
||||
sprintf('Heures supprimées pour %s le %s', $empName, $data->workDate),
|
||||
['old' => $this->snapshotWorkHour($existing)],
|
||||
$workDate,
|
||||
);
|
||||
$this->entityManager->remove($existing);
|
||||
++$result->deleted;
|
||||
} elseif (($absenceByEmployeeId[$employeeId] ?? false) === true || $is4hContract) {
|
||||
@@ -163,9 +176,11 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
||||
}
|
||||
|
||||
if ($existing) {
|
||||
$workHour = $existing;
|
||||
$oldSnapshot = $this->snapshotWorkHour($existing);
|
||||
$workHour = $existing;
|
||||
++$result->updated;
|
||||
} else {
|
||||
$oldSnapshot = null;
|
||||
// Upsert: création si aucune ligne n'existe pour (employé, date).
|
||||
$workHour = new WorkHour()
|
||||
->setEmployee($employee)
|
||||
@@ -179,6 +194,23 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
||||
if (!$isAdmin) {
|
||||
$workHour->setUpdatedAt(new DateTimeImmutable());
|
||||
}
|
||||
|
||||
$newSnapshot = $this->snapshotWorkHour($workHour);
|
||||
$action = null !== $oldSnapshot ? 'update' : 'create';
|
||||
$changes = null !== $oldSnapshot
|
||||
? ['old' => $oldSnapshot, 'new' => $newSnapshot]
|
||||
: ['new' => $newSnapshot];
|
||||
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
$action,
|
||||
'work_hour',
|
||||
$workHour->getId(),
|
||||
sprintf('Heures %s pour %s le %s', null !== $oldSnapshot ? 'modifiées' : 'créées', $empName, $data->workDate),
|
||||
$changes,
|
||||
$workDate,
|
||||
);
|
||||
|
||||
++$result->processed;
|
||||
}
|
||||
|
||||
@@ -446,6 +478,30 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function snapshotWorkHour(WorkHour $wh): array
|
||||
{
|
||||
return [
|
||||
'morningFrom' => $wh->getMorningFrom(),
|
||||
'morningTo' => $wh->getMorningTo(),
|
||||
'afternoonFrom' => $wh->getAfternoonFrom(),
|
||||
'afternoonTo' => $wh->getAfternoonTo(),
|
||||
'eveningFrom' => $wh->getEveningFrom(),
|
||||
'eveningTo' => $wh->getEveningTo(),
|
||||
'isPresentMorning' => $wh->getIsPresentMorning(),
|
||||
'isPresentAfternoon' => $wh->getIsPresentAfternoon(),
|
||||
'dayHoursMinutes' => $wh->getDayHoursMinutes(),
|
||||
'nightHoursMinutes' => $wh->getNightHoursMinutes(),
|
||||
'workshopHoursMinutes' => $wh->getWorkshopHoursMinutes(),
|
||||
'hasBreakfast' => $wh->getHasBreakfast(),
|
||||
'hasLunch' => $wh->getHasLunch(),
|
||||
'hasDinner' => $wh->getHasDinner(),
|
||||
'hasOvernight' => $wh->getHasOvernight(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* morningFrom:?string,
|
||||
|
||||
@@ -10,7 +10,9 @@ use App\ApiResource\WorkHourBulkValidation;
|
||||
use App\ApiResource\WorkHourBulkValidationResult;
|
||||
use App\Entity\User;
|
||||
use App\Entity\WorkHour;
|
||||
use App\Service\AuditLogger;
|
||||
use App\Service\WorkHours\WorkHourBulkValidationExecutor;
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
@@ -20,6 +22,7 @@ final readonly class WorkHourBulkValidationProcessor implements ProcessorInterfa
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private WorkHourBulkValidationExecutor $executor,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(
|
||||
@@ -41,7 +44,7 @@ final readonly class WorkHourBulkValidationProcessor implements ProcessorInterfa
|
||||
throw new AccessDeniedHttpException('Only admins can bulk validate work hours.');
|
||||
}
|
||||
|
||||
return $this->executor->execute(
|
||||
$result = $this->executor->execute(
|
||||
user: $user,
|
||||
workDateValue: $data->workDate,
|
||||
employeeIds: $data->employeeIds,
|
||||
@@ -50,5 +53,22 @@ final readonly class WorkHourBulkValidationProcessor implements ProcessorInterfa
|
||||
$workHour->setIsValid($data->isValid);
|
||||
}
|
||||
);
|
||||
|
||||
if ($result->updated > 0) {
|
||||
$workDate = DateTimeImmutable::createFromFormat('Y-m-d', $data->workDate);
|
||||
$action = $data->isValid ? 'validé' : 'dévalidé';
|
||||
|
||||
$this->auditLogger->log(
|
||||
null,
|
||||
'validate',
|
||||
'work_hour',
|
||||
null,
|
||||
sprintf('Validation RH %s pour %d employé(s) le %s', $action, $result->updated, $data->workDate),
|
||||
['employeeIds' => $data->employeeIds, 'isValid' => $data->isValid],
|
||||
$workDate ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ use App\Entity\WorkHour;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Repository\WorkHourRepository;
|
||||
use App\Security\EmployeeScopeService;
|
||||
use App\Service\AuditLogger;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
@@ -24,6 +26,7 @@ final readonly class WorkHourSiteValidationProcessor implements ProcessorInterfa
|
||||
private WorkHourRepository $workHourRepository,
|
||||
private UserRepository $userRepository,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private AuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): WorkHour
|
||||
@@ -59,6 +62,23 @@ final readonly class WorkHourSiteValidationProcessor implements ProcessorInterfa
|
||||
&& false === $changeSet['isSiteValid'][0]
|
||||
&& true === $changeSet['isSiteValid'][1];
|
||||
|
||||
if (isset($changeSet['isSiteValid'])) {
|
||||
$employee = $data->getEmployee();
|
||||
$empName = $employee ? trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? '')) : '';
|
||||
$workDate = $data->getWorkDate();
|
||||
$newVal = $changeSet['isSiteValid'][1] ? 'validé' : 'dévalidé';
|
||||
|
||||
$this->auditLogger->log(
|
||||
$employee,
|
||||
'site_validate',
|
||||
'work_hour',
|
||||
$data->getId(),
|
||||
sprintf('Validation site %s pour %s le %s', $newVal, $empName, $workDate->format('d/m/Y')),
|
||||
['old' => ['isSiteValid' => $changeSet['isSiteValid'][0]], 'new' => ['isSiteValid' => $changeSet['isSiteValid'][1]]],
|
||||
$workDate instanceof DateTimeImmutable ? $workDate : DateTimeImmutable::createFromInterface($workDate),
|
||||
);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
// Notification uniquement quand la dernière ligne du site est validée pour la date.
|
||||
|
||||
Reference in New Issue
Block a user