pour prod
This commit is contained in:
209
app/Models/QuotaModel.php
Normal file
209
app/Models/QuotaModel.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Suivi du quota PTA sur l'année CIVILE.
|
||||
*
|
||||
* Le PTA est planifié du 1er janvier au 31 décembre de l'année concernée.
|
||||
* La date de début du contrat reste une information RH utile, mais elle ne sert
|
||||
* plus d'ancre pour décaler la période annuelle du quota.
|
||||
*/
|
||||
final class QuotaModel extends BaseModel
|
||||
{
|
||||
public const REFERENCE_MINUTES = 1607 * 60;
|
||||
|
||||
public function createForAgent(int $agentId, int $year, float $rate): void
|
||||
{
|
||||
$target = (int) round(self::REFERENCE_MINUTES * ($rate / 100));
|
||||
$stmt = $this->pdo->prepare(
|
||||
'INSERT INTO quota_agent_annuel (
|
||||
id_agent, annee, quotite_travail, quota_reference_minutes, quota_cible_minutes, commentaire
|
||||
) VALUES (
|
||||
:agent_id, :annee, :quotite, :quota_reference, :quota_cible, :commentaire
|
||||
)'
|
||||
);
|
||||
$stmt->execute([
|
||||
'agent_id' => $agentId,
|
||||
'annee' => $year,
|
||||
'quotite' => $rate,
|
||||
'quota_reference' => self::REFERENCE_MINUTES,
|
||||
'quota_cible' => $target,
|
||||
'commentaire' => 'Quota PTA de référence pour l’année civile',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le quota de l'année civile contenant la date fournie.
|
||||
*
|
||||
* Exemple : une date de contexte au 15/09/2026 renvoie toujours la période
|
||||
* 01/01/2026 -> 31/12/2026, quelle que soit la date de début du contrat.
|
||||
*/
|
||||
public function forDate(int $agentId, string $contextDate): array
|
||||
{
|
||||
$period = $this->referencePeriod($agentId, $contextDate);
|
||||
$quota = $this->quotaProfile($agentId, (int) $period['annee_periode']);
|
||||
|
||||
$usedStmt = $this->pdo->prepare(
|
||||
"SELECT
|
||||
COALESCE(SUM(CASE WHEN p.statut = 'VALIDE' THEN
|
||||
TIMESTAMPDIFF(MINUTE, CONCAT(c.date_jour, ' ', c.heure_debut), CONCAT(c.date_jour, ' ', c.heure_fin))
|
||||
ELSE 0 END), 0) AS minutes_valides,
|
||||
COALESCE(SUM(CASE WHEN p.statut = 'BROUILLON' THEN
|
||||
TIMESTAMPDIFF(MINUTE, CONCAT(c.date_jour, ' ', c.heure_debut), CONCAT(c.date_jour, ' ', c.heure_fin))
|
||||
ELSE 0 END), 0) AS minutes_brouillon
|
||||
FROM planning p
|
||||
INNER JOIN creneau_horaire c ON c.id_planning = p.id_planning
|
||||
INNER JOIN motif_planning m ON m.id_motif = c.id_motif
|
||||
WHERE p.id_agent = :agent_id
|
||||
AND c.date_jour BETWEEN :date_debut AND :date_fin
|
||||
AND m.compte_dans_quota = TRUE"
|
||||
);
|
||||
$usedStmt->execute([
|
||||
'agent_id' => $agentId,
|
||||
'date_debut' => $period['date_debut_periode'],
|
||||
'date_fin' => $period['date_fin_periode'],
|
||||
]);
|
||||
$used = $usedStmt->fetch() ?: ['minutes_valides' => 0, 'minutes_brouillon' => 0];
|
||||
|
||||
$validated = (int) $used['minutes_valides'];
|
||||
$draft = (int) $used['minutes_brouillon'];
|
||||
$target = (int) $quota['quota_cible_minutes'];
|
||||
$allocated = $validated + $draft;
|
||||
|
||||
return array_merge($period, [
|
||||
'annee' => (int) $period['annee_periode'],
|
||||
'quotite_travail' => (float) $quota['quotite_travail'],
|
||||
'quota_reference_minutes' => (int) $quota['quota_reference_minutes'],
|
||||
'quota_cible_minutes' => $target,
|
||||
'quota_source_annee' => (int) $quota['quota_source_annee'],
|
||||
'minutes_valides' => $validated,
|
||||
'minutes_brouillon' => $draft,
|
||||
'minutes_affectees' => $allocated,
|
||||
'minutes_restantes' => $target - $allocated,
|
||||
'taux_consommation' => $target > 0 ? round(($allocated / $target) * 100, 2) : 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function forDateForApi(int $agentId, string $contextDate): array
|
||||
{
|
||||
return $this->enrich($this->forDate($agentId, $contextDate));
|
||||
}
|
||||
|
||||
public function annual(int $agentId, int $year): array
|
||||
{
|
||||
return $this->forDate($agentId, sprintf('%04d-01-01', $year));
|
||||
}
|
||||
|
||||
public function annualForApi(int $agentId, int $year): array
|
||||
{
|
||||
return $this->forDateForApi($agentId, sprintf('%04d-01-01', $year));
|
||||
}
|
||||
|
||||
/**
|
||||
* Période de référence PTA : toujours l'année civile.
|
||||
* La date de contrat est retournée uniquement à titre informatif.
|
||||
*/
|
||||
public function referencePeriod(int $agentId, string $contextDate): array
|
||||
{
|
||||
$context = DateTimeImmutable::createFromFormat('!Y-m-d', $contextDate);
|
||||
if (!$context || $context->format('Y-m-d') !== $contextDate) {
|
||||
throw new InvalidArgumentException('Date de référence du quota invalide.');
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare(
|
||||
'SELECT date_debut_contrat FROM agent WHERE id_agent = :agent_id LIMIT 1'
|
||||
);
|
||||
$stmt->execute(['agent_id' => $agentId]);
|
||||
$contractStartValue = $stmt->fetchColumn();
|
||||
|
||||
$year = (int) $context->format('Y');
|
||||
$start = new DateTimeImmutable(sprintf('%04d-01-01', $year));
|
||||
$end = new DateTimeImmutable(sprintf('%04d-12-31', $year));
|
||||
|
||||
return [
|
||||
'date_reference' => $context->format('Y-m-d'),
|
||||
'date_debut_contrat' => $contractStartValue ? (string) $contractStartValue : null,
|
||||
'date_debut_periode' => $start->format('Y-m-d'),
|
||||
'date_fin_periode' => $end->format('Y-m-d'),
|
||||
'annee_periode' => $year,
|
||||
'periode_libelle' => sprintf('année civile %d · du %s au %s', $year, $start->format('d/m/Y'), $end->format('d/m/Y')),
|
||||
'periode_contractuelle' => false,
|
||||
'periode_civile' => true,
|
||||
// Conservé pour compatibilité avec les anciens écrans : la date de
|
||||
// contrat n'est plus requise pour calculer la période du quota.
|
||||
'date_contrat_manquante' => false,
|
||||
];
|
||||
}
|
||||
|
||||
public function enrich(array $quota): array
|
||||
{
|
||||
return array_merge($quota, [
|
||||
'quota_cible' => $this->formatMinutes((int) $quota['quota_cible_minutes']),
|
||||
'valides' => $this->formatMinutes((int) $quota['minutes_valides']),
|
||||
'brouillons' => $this->formatMinutes((int) $quota['minutes_brouillon']),
|
||||
'affectees' => $this->formatMinutes((int) $quota['minutes_affectees']),
|
||||
'restantes' => $this->formatMinutes((int) $quota['minutes_restantes']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function formatMinutes(int $minutes): array
|
||||
{
|
||||
$sign = $minutes < 0 ? -1 : 1;
|
||||
$absolute = abs($minutes);
|
||||
$hours = intdiv($absolute, 60);
|
||||
$mins = $absolute % 60;
|
||||
|
||||
return [
|
||||
'minutes' => $minutes,
|
||||
'heures_decimales' => round($minutes / 60, 2),
|
||||
'libelle' => ($sign < 0 ? '-' : '') . $hours . ' h ' . str_pad((string) $mins, 2, '0', STR_PAD_LEFT),
|
||||
];
|
||||
}
|
||||
|
||||
private function quotaProfile(int $agentId, int $year): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare(
|
||||
"SELECT annee, quotite_travail, quota_reference_minutes, quota_cible_minutes
|
||||
FROM quota_agent_annuel
|
||||
WHERE id_agent = :agent_id
|
||||
ORDER BY CASE
|
||||
WHEN annee = :year_exact THEN 0
|
||||
WHEN annee < :year_past THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
CASE WHEN annee <= :year_latest THEN annee END DESC,
|
||||
CASE WHEN annee > :year_future THEN annee END ASC
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute([
|
||||
'agent_id' => $agentId,
|
||||
'year_exact' => $year,
|
||||
'year_past' => $year,
|
||||
'year_latest' => $year,
|
||||
'year_future' => $year,
|
||||
]);
|
||||
$quota = $stmt->fetch();
|
||||
|
||||
if (!$quota) {
|
||||
return [
|
||||
'quotite_travail' => 100.00,
|
||||
'quota_reference_minutes' => self::REFERENCE_MINUTES,
|
||||
'quota_cible_minutes' => self::REFERENCE_MINUTES,
|
||||
'quota_source_annee' => $year,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'quotite_travail' => (float) $quota['quotite_travail'],
|
||||
'quota_reference_minutes' => (int) $quota['quota_reference_minutes'],
|
||||
'quota_cible_minutes' => (int) $quota['quota_cible_minutes'],
|
||||
'quota_source_annee' => (int) $quota['annee'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user