feat : ajout du nouveau système de contrat et ajout de filtre d'impression
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s
This commit is contained in:
@@ -18,6 +18,8 @@ use App\State\AbsencePrintProvider;
|
||||
new QueryParameter(key: 'from', required: true),
|
||||
new QueryParameter(key: 'to', required: true),
|
||||
new QueryParameter(key: 'sites', required: false),
|
||||
new QueryParameter(key: 'contractNatures', required: false),
|
||||
new QueryParameter(key: 'workContracts', required: false),
|
||||
],
|
||||
security: "is_granted('ROLE_ADMIN')"
|
||||
),
|
||||
|
||||
@@ -26,6 +26,7 @@ final class WorkHourDayContext
|
||||
* @var list<array{
|
||||
* employeeId:int,
|
||||
* absenceLabel:?string,
|
||||
* absenceColor:?string,
|
||||
* absenceHalf:?string,
|
||||
* absentMorning:bool,
|
||||
* absentAfternoon:bool,
|
||||
|
||||
@@ -10,6 +10,7 @@ final class DayContextRow
|
||||
public int $employeeId,
|
||||
public bool $hasContractAtDate = true,
|
||||
public ?string $absenceLabel = null,
|
||||
public ?string $absenceColor = null,
|
||||
public ?string $absenceHalf = null,
|
||||
public bool $absentMorning = false,
|
||||
public bool $absentAfternoon = false,
|
||||
@@ -19,6 +20,7 @@ final class DayContextRow
|
||||
|
||||
public function addAbsence(
|
||||
?string $label,
|
||||
?string $color,
|
||||
bool $morning,
|
||||
bool $afternoon,
|
||||
int $creditedMinutes,
|
||||
@@ -35,6 +37,14 @@ final class DayContextRow
|
||||
$this->absenceLabel = 'Absences multiples';
|
||||
}
|
||||
|
||||
// Si plusieurs types d'absence différents sont fusionnés sur la même journée,
|
||||
// on retire la couleur métier spécifique.
|
||||
if (null === $this->absenceColor) {
|
||||
$this->absenceColor = $color;
|
||||
} elseif ($color !== $this->absenceColor) {
|
||||
$this->absenceColor = null;
|
||||
}
|
||||
|
||||
// AM/PM seulement pour les demi-journées, null pour journée complète.
|
||||
$this->absenceHalf = $this->resolveHalfLabel($this->absentMorning, $this->absentAfternoon);
|
||||
// Cumule les minutes créditées par les absences "comptées comme travaillées".
|
||||
@@ -48,6 +58,7 @@ final class DayContextRow
|
||||
* employeeId:int,
|
||||
* hasContractAtDate:bool,
|
||||
* absenceLabel:?string,
|
||||
* absenceColor:?string,
|
||||
* absenceHalf:?string,
|
||||
* absentMorning:bool,
|
||||
* absentAfternoon:bool,
|
||||
@@ -61,6 +72,7 @@ final class DayContextRow
|
||||
'employeeId' => $this->employeeId,
|
||||
'hasContractAtDate' => $this->hasContractAtDate,
|
||||
'absenceLabel' => $this->absenceLabel,
|
||||
'absenceColor' => $this->absenceColor,
|
||||
'absenceHalf' => $this->absenceHalf,
|
||||
'absentMorning' => $this->absentMorning,
|
||||
'absentAfternoon' => $this->absentAfternoon,
|
||||
|
||||
@@ -6,9 +6,12 @@ namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Repository\EmployeeRepository;
|
||||
use App\State\EmployeeWriteProcessor;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
@@ -56,9 +59,25 @@ class Employee
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private DateTimeImmutable $createdAt;
|
||||
|
||||
/**
|
||||
* @var Collection<int, EmployeeContractPeriod>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'employee', targetEntity: EmployeeContractPeriod::class)]
|
||||
private Collection $contractPeriods;
|
||||
|
||||
#[Groups(['employee:write'])]
|
||||
private ?string $contractNature = null;
|
||||
|
||||
#[Groups(['employee:write'])]
|
||||
private ?string $contractStartDate = null;
|
||||
|
||||
#[Groups(['employee:write'])]
|
||||
private ?string $contractEndDate = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
$this->contractPeriods = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
@@ -130,4 +149,81 @@ class Employee
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContractNature(): ?string
|
||||
{
|
||||
return $this->contractNature;
|
||||
}
|
||||
|
||||
public function setContractNature(?string $contractNature): self
|
||||
{
|
||||
$this->contractNature = $contractNature;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContractStartDate(): ?string
|
||||
{
|
||||
return $this->contractStartDate;
|
||||
}
|
||||
|
||||
public function setContractStartDate(?string $contractStartDate): self
|
||||
{
|
||||
$this->contractStartDate = $contractStartDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContractEndDate(): ?string
|
||||
{
|
||||
return $this->contractEndDate;
|
||||
}
|
||||
|
||||
public function setContractEndDate(?string $contractEndDate): self
|
||||
{
|
||||
$this->contractEndDate = $contractEndDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Groups(['employee:read'])]
|
||||
public function getCurrentContractNature(): string
|
||||
{
|
||||
return $this->resolveCurrentContractPeriod()?->getContractNatureEnum()->value ?? ContractNature::CDI->value;
|
||||
}
|
||||
|
||||
#[Groups(['employee:read'])]
|
||||
public function getCurrentContractStartDate(): ?string
|
||||
{
|
||||
return $this->resolveCurrentContractPeriod()?->getStartDate()->format('Y-m-d');
|
||||
}
|
||||
|
||||
#[Groups(['employee:read'])]
|
||||
public function getCurrentContractEndDate(): ?string
|
||||
{
|
||||
return $this->resolveCurrentContractPeriod()?->getEndDate()?->format('Y-m-d');
|
||||
}
|
||||
|
||||
private function resolveCurrentContractPeriod(): ?EmployeeContractPeriod
|
||||
{
|
||||
$today = new DateTimeImmutable('today');
|
||||
$current = null;
|
||||
|
||||
foreach ($this->contractPeriods as $period) {
|
||||
if ($period->getStartDate() > $today) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$endDate = $period->getEndDate();
|
||||
if (null !== $endDate && $endDate < $today) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null === $current || $period->getStartDate() > $current->getStartDate()) {
|
||||
$current = $period;
|
||||
}
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Enum\ContractNature;
|
||||
use App\Repository\EmployeeContractPeriodRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
@@ -19,7 +20,7 @@ class EmployeeContractPeriod
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Employee::class)]
|
||||
#[ORM\ManyToOne(targetEntity: Employee::class, inversedBy: 'contractPeriods')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private ?Employee $employee = null;
|
||||
|
||||
@@ -33,6 +34,9 @@ class EmployeeContractPeriod
|
||||
#[ORM\Column(type: 'date_immutable', nullable: true)]
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 20, options: ['default' => ContractNature::CDI->value])]
|
||||
private string $contractNature = ContractNature::CDI->value;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private DateTimeImmutable $createdAt;
|
||||
|
||||
@@ -95,6 +99,24 @@ class EmployeeContractPeriod
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContractNature(): string
|
||||
{
|
||||
return $this->contractNature;
|
||||
}
|
||||
|
||||
public function getContractNatureEnum(): ContractNature
|
||||
{
|
||||
return ContractNature::tryFrom($this->contractNature) ?? ContractNature::CDI;
|
||||
}
|
||||
|
||||
public function setContractNature(ContractNature|string $contractNature): self
|
||||
{
|
||||
$value = $contractNature instanceof ContractNature ? $contractNature->value : $contractNature;
|
||||
$this->contractNature = ContractNature::tryFrom($value)?->value ?? ContractNature::CDI->value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
|
||||
17
src/Enum/ContractNature.php
Normal file
17
src/Enum/ContractNature.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enum;
|
||||
|
||||
enum ContractNature: string
|
||||
{
|
||||
case CDI = 'CDI';
|
||||
case CDD = 'CDD';
|
||||
case INTERIM = 'INTERIM';
|
||||
|
||||
public function requiresEndDate(): bool
|
||||
{
|
||||
return self::CDD === $this || self::INTERIM === $this;
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,8 @@ final class EmployeeRepository extends ServiceEntityRepository implements Employ
|
||||
$qb = $this->createQueryBuilder('e')
|
||||
->leftJoin('e.site', 's')
|
||||
->addSelect('s')
|
||||
->leftJoin('e.contract', 'c')
|
||||
->addSelect('c')
|
||||
->orderBy('s.displayOrder', 'ASC')
|
||||
->addOrderBy('s.name', 'ASC')
|
||||
->addOrderBy('e.displayOrder', 'ASC')
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Service\Contracts;
|
||||
|
||||
use App\Entity\Contract;
|
||||
use App\Entity\Employee;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Repository\EmployeeContractPeriodRepository;
|
||||
use DateTimeImmutable;
|
||||
|
||||
@@ -22,6 +23,13 @@ readonly class EmployeeContractResolver
|
||||
return $period?->getContract();
|
||||
}
|
||||
|
||||
public function resolveNatureForEmployeeAndDate(Employee $employee, DateTimeImmutable $date): ContractNature
|
||||
{
|
||||
$period = $this->periodRepository->findOneCoveringDate($employee, $date);
|
||||
|
||||
return $period?->getContractNatureEnum() ?? ContractNature::CDI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Employee> $employees
|
||||
* @param list<string> $days
|
||||
@@ -68,4 +76,51 @@ readonly class EmployeeContractResolver
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Employee> $employees
|
||||
* @param list<string> $days
|
||||
*
|
||||
* @return array<int, array<string, ContractNature>>
|
||||
*/
|
||||
public function resolveNaturesForEmployeesAndDays(array $employees, array $days): array
|
||||
{
|
||||
$resolved = [];
|
||||
if ([] === $employees || [] === $days) {
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
foreach ($employees as $employee) {
|
||||
$employeeId = $employee->getId();
|
||||
if (!$employeeId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($days as $day) {
|
||||
$resolved[$employeeId][$day] = ContractNature::CDI;
|
||||
}
|
||||
}
|
||||
|
||||
$from = new DateTimeImmutable(min($days));
|
||||
$to = new DateTimeImmutable(max($days));
|
||||
$periods = $this->periodRepository->findByEmployeesAndDateRange($employees, $from, $to);
|
||||
foreach ($periods as $period) {
|
||||
$employeeId = $period->getEmployee()?->getId();
|
||||
if (!$employeeId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$start = $period->getStartDate()->format('Y-m-d');
|
||||
$end = $period->getEndDate()?->format('Y-m-d') ?? '9999-12-31';
|
||||
$nature = $period->getContractNatureEnum();
|
||||
foreach ($days as $day) {
|
||||
if ($day < $start || $day > $end) {
|
||||
continue;
|
||||
}
|
||||
$resolved[$employeeId][$day] = $nature;
|
||||
}
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Enum\HalfDay;
|
||||
use App\Repository\AbsenceRepository;
|
||||
use App\Repository\EmployeeRepository;
|
||||
@@ -53,9 +54,11 @@ class AbsencePrintProvider implements ProviderInterface
|
||||
$fromDate = DateTimeImmutable::createFromFormat('Y-m-d', $from);
|
||||
$toDate = DateTimeImmutable::createFromFormat('Y-m-d', $to);
|
||||
|
||||
$siteIds = $this->parseIds($request->query->get('sites'));
|
||||
$siteIds = $this->parseIds($request->query->get('sites'));
|
||||
$workContractIds = $this->parseIds($request->query->get('workContracts'));
|
||||
$contractNatures = $this->parseContractNatures($request->query->get('contractNatures'));
|
||||
|
||||
$employees = $this->loadEmployees($siteIds);
|
||||
$employees = $this->loadEmployees($siteIds, $contractNatures, $workContractIds);
|
||||
$absences = $this->loadAbsences($fromDate, $toDate, $employees);
|
||||
|
||||
$days = $this->buildDays($fromDate, $toDate);
|
||||
@@ -108,9 +111,19 @@ class AbsencePrintProvider implements ProviderInterface
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
private function loadEmployees(array $siteIds): array
|
||||
private function loadEmployees(array $siteIds, array $contractNatures, array $workContractIds): array
|
||||
{
|
||||
return $this->employeeRepository->findForPrintBySiteIds($siteIds);
|
||||
$employees = $this->employeeRepository->findForPrintBySiteIds($siteIds);
|
||||
|
||||
return array_values(array_filter($employees, static function ($employee) use ($contractNatures, $workContractIds): bool {
|
||||
$employeeNature = (string) $employee->getCurrentContractNature();
|
||||
$employeeContractId = $employee->getContract()?->getId();
|
||||
|
||||
$natureMatches = [] === $contractNatures || in_array($employeeNature, $contractNatures, true);
|
||||
$contractMatches = [] === $workContractIds || (null !== $employeeContractId && in_array($employeeContractId, $workContractIds, true));
|
||||
|
||||
return $natureMatches && $contractMatches;
|
||||
}));
|
||||
}
|
||||
|
||||
private function loadAbsences(DateTimeImmutable $from, DateTimeImmutable $to, array $employees): array
|
||||
@@ -209,4 +222,24 @@ class AbsencePrintProvider implements ProviderInterface
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function parseContractNatures(?string $value): array
|
||||
{
|
||||
if (null === $value || '' === trim($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$values = [];
|
||||
foreach (explode(',', $value) as $part) {
|
||||
$nature = strtoupper(trim($part));
|
||||
if (null !== ContractNature::tryFrom($nature)) {
|
||||
$values[] = $nature;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($values));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Entity\Contract;
|
||||
use App\Entity\Employee;
|
||||
use App\Entity\EmployeeContractPeriod;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Repository\EmployeeContractPeriodRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
final readonly class EmployeeWriteProcessor implements ProcessorInterface
|
||||
{
|
||||
@@ -49,27 +51,46 @@ final readonly class EmployeeWriteProcessor implements ProcessorInterface
|
||||
return $result;
|
||||
}
|
||||
|
||||
$today = new DateTimeImmutable('today');
|
||||
$today = new DateTimeImmutable('today');
|
||||
$requestedContractNature = $this->resolveContractNature($data->getContractNature());
|
||||
$requestedStartDate = $this->parseOptionalYmd($data->getContractStartDate(), 'contractStartDate');
|
||||
$requestedEndDate = $this->parseOptionalYmd($data->getContractEndDate(), 'contractEndDate');
|
||||
|
||||
if ($isNew) {
|
||||
$this->ensureContractPeriodExists($data, $currentContract, new DateTimeImmutable('1970-01-01'));
|
||||
$startDate = $requestedStartDate ?? new DateTimeImmutable('1970-01-01');
|
||||
$nature = $requestedContractNature ?? ContractNature::CDI;
|
||||
$this->assertPeriodDates($startDate, $requestedEndDate, $nature);
|
||||
$this->ensureContractPeriodExists($data, $currentContract, $startDate, $requestedEndDate, $nature);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($this->isSameContract($previousContract, $currentContract)) {
|
||||
$hasPeriodChangeRequest = null !== $requestedContractNature || null !== $requestedStartDate || null !== $requestedEndDate;
|
||||
if ($this->isSameContract($previousContract, $currentContract) && !$hasPeriodChangeRequest) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$startDate = $requestedStartDate ?? $today;
|
||||
$todayPeriod = $this->periodRepository->findOneCoveringDate($data, $today);
|
||||
if (null !== $todayPeriod && null === $todayPeriod->getEndDate() && $todayPeriod->getStartDate()->format('Y-m-d') === $today->format('Y-m-d')) {
|
||||
$nature = $requestedContractNature ?? $todayPeriod?->getContractNatureEnum() ?? ContractNature::CDI;
|
||||
$endDate = $requestedEndDate;
|
||||
$this->assertPeriodDates($startDate, $endDate, $nature);
|
||||
|
||||
if (
|
||||
null !== $todayPeriod
|
||||
&& null === $todayPeriod->getEndDate()
|
||||
&& $todayPeriod->getStartDate()->format('Y-m-d') === $startDate->format('Y-m-d')
|
||||
) {
|
||||
$todayPeriod->setContract($currentContract);
|
||||
$todayPeriod->setContractNature($nature);
|
||||
$todayPeriod->setEndDate($endDate);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->periodRepository->closeOpenPeriods($data, $today->modify('-1 day'));
|
||||
$this->createPeriod($data, $currentContract, $today);
|
||||
$this->periodRepository->closeOpenPeriods($data, $startDate->modify('-1 day'));
|
||||
$this->createPeriod($data, $currentContract, $startDate, $endDate, $nature);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $result;
|
||||
@@ -96,26 +117,80 @@ final readonly class EmployeeWriteProcessor implements ProcessorInterface
|
||||
return $first->getId() === $second->getId();
|
||||
}
|
||||
|
||||
private function ensureContractPeriodExists(Employee $employee, Contract $contract, DateTimeImmutable $startDate): void
|
||||
{
|
||||
private function ensureContractPeriodExists(
|
||||
Employee $employee,
|
||||
Contract $contract,
|
||||
DateTimeImmutable $startDate,
|
||||
?DateTimeImmutable $endDate,
|
||||
ContractNature $nature,
|
||||
): void {
|
||||
$covered = $this->periodRepository->findOneCoveringDate($employee, $startDate);
|
||||
if (null !== $covered) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->createPeriod($employee, $contract, $startDate);
|
||||
$this->createPeriod($employee, $contract, $startDate, $endDate, $nature);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
private function createPeriod(Employee $employee, Contract $contract, DateTimeImmutable $startDate): void
|
||||
{
|
||||
private function createPeriod(
|
||||
Employee $employee,
|
||||
Contract $contract,
|
||||
DateTimeImmutable $startDate,
|
||||
?DateTimeImmutable $endDate,
|
||||
ContractNature $nature,
|
||||
): void {
|
||||
$period = new EmployeeContractPeriod()
|
||||
->setEmployee($employee)
|
||||
->setContract($contract)
|
||||
->setStartDate($startDate)
|
||||
->setEndDate(null)
|
||||
->setEndDate($endDate)
|
||||
->setContractNature($nature)
|
||||
;
|
||||
|
||||
$this->entityManager->persist($period);
|
||||
}
|
||||
|
||||
private function resolveContractNature(?string $raw): ?ContractNature
|
||||
{
|
||||
if (null === $raw || '' === trim($raw)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ContractNature::tryFrom(trim($raw))
|
||||
?? throw new UnprocessableEntityHttpException('contractNature must be one of CDI, CDD, INTERIM.');
|
||||
}
|
||||
|
||||
private function parseOptionalYmd(?string $raw, string $field): ?DateTimeImmutable
|
||||
{
|
||||
if (null === $raw || '' === trim($raw)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = trim($raw);
|
||||
$date = DateTimeImmutable::createFromFormat('Y-m-d', $value);
|
||||
if (!$date || $date->format('Y-m-d') !== $value) {
|
||||
throw new UnprocessableEntityHttpException(sprintf('%s must use Y-m-d format.', $field));
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
private function assertPeriodDates(
|
||||
DateTimeImmutable $startDate,
|
||||
?DateTimeImmutable $endDate,
|
||||
ContractNature $nature
|
||||
): void {
|
||||
if (null !== $endDate && $endDate < $startDate) {
|
||||
throw new UnprocessableEntityHttpException('contractEndDate cannot be before contractStartDate.');
|
||||
}
|
||||
|
||||
if ($nature->requiresEndDate() && null === $endDate) {
|
||||
throw new UnprocessableEntityHttpException('contractEndDate is required for CDD and INTERIM.');
|
||||
}
|
||||
|
||||
if (ContractNature::CDI === $nature && null !== $endDate) {
|
||||
throw new UnprocessableEntityHttpException('contractEndDate must be empty for CDI.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ final readonly class WorkHourDayContextProvider implements ProviderInterface
|
||||
$creditedPresenceUnits = $this->workedHoursCreditPolicy->computeCreditedPresenceUnits($absence, $dateKey, $absentMorning, $absentAfternoon);
|
||||
$rowsByEmployeeId[$employeeId]->addAbsence(
|
||||
label: $absence->getType()?->getLabel(),
|
||||
color: $absence->getType()?->getColor(),
|
||||
morning: $absentMorning,
|
||||
afternoon: $absentAfternoon,
|
||||
creditedMinutes: $creditedMinutes,
|
||||
|
||||
@@ -15,6 +15,7 @@ use App\Entity\Contract;
|
||||
use App\Entity\Employee;
|
||||
use App\Entity\User;
|
||||
use App\Entity\WorkHour;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Enum\ContractType;
|
||||
use App\Enum\TrackingMode;
|
||||
use App\Repository\Contract\AbsenceReadRepositoryInterface;
|
||||
@@ -113,8 +114,9 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
||||
*/
|
||||
private function buildRows(array $employees, array $workHours, array $absences, array $days, string $anchorDateYmd): array
|
||||
{
|
||||
$contractsByEmployeeDate = $this->contractResolver->resolveForEmployeesAndDays($employees, $days);
|
||||
$metricsByEmployeeDate = [];
|
||||
$contractsByEmployeeDate = $this->contractResolver->resolveForEmployeesAndDays($employees, $days);
|
||||
$contractNaturesByEmployeeDate = $this->contractResolver->resolveNaturesForEmployeesAndDays($employees, $days);
|
||||
$metricsByEmployeeDate = [];
|
||||
foreach ($workHours as $workHour) {
|
||||
$employeeId = $workHour->getEmployee()?->getId();
|
||||
if (!$employeeId) {
|
||||
@@ -182,6 +184,9 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
||||
$weekAnchorContract = $contractsByEmployeeDate[$employeeId][$anchorDateYmd]
|
||||
?? $contractsByEmployeeDate[$employeeId][$days[0]]
|
||||
?? null;
|
||||
$weekAnchorContractNature = $contractNaturesByEmployeeDate[$employeeId][$anchorDateYmd]
|
||||
?? $contractNaturesByEmployeeDate[$employeeId][$days[0]]
|
||||
?? ContractNature::CDI;
|
||||
$employeeContractsByDate = [];
|
||||
foreach ($days as $date) {
|
||||
$employeeContractsByDate[$date] = $contractsByEmployeeDate[$employeeId][$date] ?? null;
|
||||
@@ -223,7 +228,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
||||
}
|
||||
|
||||
$isWeekPresenceTracking = TrackingMode::PRESENCE->value === $weekAnchorContract?->getTrackingMode();
|
||||
$disableOvertimeBonuses = $this->hasDisabledOvertimeBonuses($weekAnchorContract);
|
||||
$disableOvertimeBonuses = $this->hasDisabledOvertimeBonuses($weekAnchorContract, $weekAnchorContractNature);
|
||||
$overtimeReferenceMinutes = $this->computeWeeklyOvertimeReferenceMinutes($days, $employeeContractsByDate);
|
||||
$overtime25StartMinutes = $this->computeWeeklyOvertime25StartMinutes($days, $employeeContractsByDate);
|
||||
$weeklyOvertimeTotalMinutes = $isWeekPresenceTracking
|
||||
@@ -407,8 +412,12 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
||||
return (int) round($trancheMinutes * 0.5);
|
||||
}
|
||||
|
||||
private function hasDisabledOvertimeBonuses(?Contract $contract): bool
|
||||
private function hasDisabledOvertimeBonuses(?Contract $contract, ContractNature $contractNature): bool
|
||||
{
|
||||
if (ContractNature::INTERIM === $contractNature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$type = ContractType::resolve(
|
||||
$contract?->getName(),
|
||||
$contract?->getTrackingMode(),
|
||||
|
||||
Reference in New Issue
Block a user