src/Entity/LignePlanAchat.php line 18

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Repository\LignePlanAchatRepository;
  4. use Doctrine\DBAL\Types\Types;
  5. use Doctrine\ORM\Mapping as ORM;
  6. use DateTimeInterface;
  7. use Doctrine\Common\Collections\ArrayCollection;
  8. use Doctrine\Common\Collections\Collection;
  9. /**
  10. * Ligne du plan d'achats.
  11. * La procédure applicable est détectée automatiquement depuis la matrice des seuils.
  12. */
  13. #[ORM\Entity(repositoryClass: LignePlanAchatRepository::class)]
  14. #[ORM\Table(name: 'ligne_plan_achat')]
  15. #[ORM\HasLifecycleCallbacks]
  16. class LignePlanAchat
  17. {
  18. const STATUT_PLANIFIE = 'planifie';
  19. const STATUT_EN_COURS = 'en_cours';
  20. const STATUT_DA_CREEE = 'da_creee';
  21. const STATUT_REALISE = 'realise';
  22. const STATUT_ANNULE = 'annule';
  23. const STATUT_REPORTE = 'reporte';
  24. const TRIMESTRE_T1 = 'T1';
  25. const TRIMESTRE_T2 = 'T2';
  26. const TRIMESTRE_T3 = 'T3';
  27. const TRIMESTRE_T4 = 'T4';
  28. public static function getStatuts(): array
  29. {
  30. return [
  31. self::STATUT_PLANIFIE => 'Planifié',
  32. self::STATUT_EN_COURS => 'En cours',
  33. self::STATUT_DA_CREEE => 'DA créée',
  34. self::STATUT_REALISE => 'Réalisé',
  35. self::STATUT_ANNULE => 'Annulé',
  36. self::STATUT_REPORTE => 'Reporté',
  37. ];
  38. }
  39. public static function getTrimestres(): array
  40. {
  41. return [
  42. self::TRIMESTRE_T1 => '1er trimestre (Jan-Mar)',
  43. self::TRIMESTRE_T2 => '2ème trimestre (Avr-Jun)',
  44. self::TRIMESTRE_T3 => '3ème trimestre (Jul-Sep)',
  45. self::TRIMESTRE_T4 => '4ème trimestre (Oct-Déc)',
  46. ];
  47. }
  48. #[ORM\Id]
  49. #[ORM\GeneratedValue]
  50. #[ORM\Column]
  51. private ?int $id = null;
  52. #[ORM\ManyToOne(targetEntity: PlanAchatAnnuel::class, inversedBy: 'lignes')]
  53. #[ORM\JoinColumn(name: 'plan_achat_id', nullable: false, onDelete: 'CASCADE')]
  54. private ?PlanAchatAnnuel $planAchat = null;
  55. #[ORM\Column(name: 'montant_reserve', type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
  56. private ?string $montantReserve = '0';
  57. // Numéro de ligne dans le plan
  58. #[ORM\Column(name: 'num_ligne', nullable: true)]
  59. private ?int $numLigne = null;
  60. // Désignation de l'article/service à acheter
  61. #[ORM\Column(name: 'designation', length: 255)]
  62. private ?string $designation = null;
  63. #[ORM\Column(name: 'description', type: Types::TEXT, nullable: true)]
  64. private ?string $description = null;
  65. // Quantité prévue
  66. #[ORM\Column(name: 'quantite', type: Types::DECIMAL, precision: 10, scale: 2, nullable: true)]
  67. private ?string $quantite = null;
  68. #[ORM\Column(name: 'unite', length: 50, nullable: true)]
  69. private ?string $unite = null;
  70. // Budget estimé — CLEF pour la détection automatique du seuil
  71. #[ORM\Column(name: 'budget_estime', type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
  72. private ?string $budgetEstime = null;
  73. // Montant réalisé (après exécution)
  74. #[ORM\Column(name: 'montant_realise', type: Types::DECIMAL, precision: 15, scale: 2, nullable: true)]
  75. private ?string $montantRealise = null;
  76. // Catégorie de marché (lien vers CategorieMarche)
  77. #[ORM\ManyToOne(targetEntity: CategorieMarche::class)]
  78. #[ORM\JoinColumn(name: 'categorie_marche_id', nullable: true)]
  79. private ?CategorieMarche $categorieMarche = null;
  80. // Seuil détecté automatiquement
  81. #[ORM\ManyToOne(targetEntity: SeuilPassation::class)]
  82. #[ORM\JoinColumn(name: 'seuil_passation_id', nullable: true)]
  83. private ?SeuilPassation $seuilPassation = null;
  84. // Trimestre prévu
  85. #[ORM\Column(name: 'trimestre_prevu', length: 5, nullable: true)]
  86. private ?string $trimestrePrevu = null;
  87. // Mois prévu (1-12)
  88. #[ORM\Column(name: 'mois_prevu', nullable: true)]
  89. private ?int $moisPrevu = null;
  90. #[ORM\Column(name: 'statut', length: 20)]
  91. private string $statut = self::STATUT_PLANIFIE;
  92. // Observation / justification
  93. #[ORM\Column(name: 'observation', type: Types::TEXT, nullable: true)]
  94. private ?string $observation = null;
  95. // Lien vers la DA créée depuis cette ligne
  96. #[ORM\OneToMany(mappedBy: 'lignePlanAchat', targetEntity: demAchat::class)]
  97. private Collection $demandesAchat;
  98. // Priorité (1 = haute, 2 = moyenne, 3 = faible)
  99. #[ORM\Column(name: 'priorite', nullable: true)]
  100. private ?int $priorite = null;
  101. #[ORM\Column(name: 'created_at', type: Types::DATETIME_MUTABLE)]
  102. private ?\DateTimeInterface $createdAt = null;
  103. #[ORM\Column(name: 'updated_at', type: Types::DATETIME_MUTABLE, nullable: true)]
  104. private ?\DateTimeInterface $updatedAt = null;
  105. public function __construct()
  106. {
  107. $this->createdAt = new \DateTime();
  108. $this->statut = self::STATUT_PLANIFIE;
  109. $this->demandesAchat = new ArrayCollection(); // Indispensable
  110. }
  111. #[ORM\PreUpdate]
  112. public function onPreUpdate(): void
  113. {
  114. $this->updatedAt = new \DateTime();
  115. }
  116. // ---------------------------------------------------------------
  117. // Méthodes métier
  118. // ---------------------------------------------------------------
  119. /**
  120. * Retourne l'écart entre budget estimé et montant réalisé.
  121. */
  122. public function getEcartBudget(): ?float
  123. {
  124. if ($this->budgetEstime === null || $this->montantRealise === null) return null;
  125. return floatval($this->montantRealise) - floatval($this->budgetEstime);
  126. }
  127. /**
  128. * Libellé de priorité.
  129. */
  130. public function getPrioriteLabel(): string
  131. {
  132. return match($this->priorite) {
  133. 1 => 'Haute',
  134. 2 => 'Moyenne',
  135. 3 => 'Faible',
  136. default => '—',
  137. };
  138. }
  139. // ---------------------------------------------------------------
  140. // Getters / Setters
  141. // ---------------------------------------------------------------
  142. public function getId(): ?int { return $this->id; }
  143. public function getPlanAchat(): ?PlanAchatAnnuel { return $this->planAchat; }
  144. public function setPlanAchat(?PlanAchatAnnuel $p): static { $this->planAchat = $p; return $this; }
  145. public function getNumLigne(): ?int { return $this->numLigne; }
  146. public function setNumLigne(?int $n): static { $this->numLigne = $n; return $this; }
  147. public function getDesignation(): ?string { return $this->designation; }
  148. public function setDesignation(string $d): static { $this->designation = $d; return $this; }
  149. public function getDescription(): ?string { return $this->description; }
  150. public function setDescription(?string $d): static { $this->description = $d; return $this; }
  151. public function getQuantite(): ?string { return $this->quantite; }
  152. public function setQuantite(?string $q): static { $this->quantite = $q; return $this; }
  153. public function getUnite(): ?string { return $this->unite; }
  154. public function setUnite(?string $u): static { $this->unite = $u; return $this; }
  155. public function getBudgetEstime(): ?string { return $this->budgetEstime; }
  156. public function setBudgetEstime(?string $b): static { $this->budgetEstime = $b; return $this; }
  157. public function getMontantRealise(): ?string { return $this->montantRealise; }
  158. public function setMontantRealise(?string $m): static { $this->montantRealise = $m; return $this; }
  159. public function getCategorieMarche(): ?CategorieMarche { return $this->categorieMarche; }
  160. public function setCategorieMarche(?CategorieMarche $c): static { $this->categorieMarche = $c; return $this; }
  161. public function getSeuilPassation(): ?SeuilPassation { return $this->seuilPassation; }
  162. public function setSeuilPassation(?SeuilPassation $s): static { $this->seuilPassation = $s; return $this; }
  163. public function getTrimestrePrevu(): ?string { return $this->trimestrePrevu; }
  164. public function setTrimestrePrevu(?string $t): static { $this->trimestrePrevu = $t; return $this; }
  165. public function getMoisPrevu(): ?int { return $this->moisPrevu; }
  166. public function setMoisPrevu(?int $m): static { $this->moisPrevu = $m; return $this; }
  167. public function getStatut(): string { return $this->statut; }
  168. public function setStatut(string $s): static { $this->statut = $s; return $this; }
  169. public function getObservation(): ?string { return $this->observation; }
  170. public function setObservation(?string $o): static { $this->observation = $o; return $this; }
  171. public function getPriorite(): ?int { return $this->priorite; }
  172. public function setPriorite(?int $p): static { $this->priorite = $p; return $this; }
  173. public function getCreatedAt(): ?\DateTimeInterface { return $this->createdAt; }
  174. public function getUpdatedAt(): ?\DateTimeInterface { return $this->updatedAt; }
  175. public function getMontantConsomme(): float
  176. {
  177. $total = 0;
  178. foreach ($this->demandesAchat as $da) {
  179. // On suppose que votre entité demAchat a une méthode getMontant()
  180. $total += (float) $da->getMontant();
  181. }
  182. return $total;
  183. }
  184. /**
  185. * @return Collection<int, demAchat>
  186. */
  187. public function getDemandesAchat(): Collection
  188. {
  189. return $this->demandesAchat;
  190. }
  191. public function getMontantReserve(): ?string { return $this->montantReserve; }
  192. public function setMontantReserve(?string $m): static {
  193. $this->montantReserve = $m;
  194. return $this;
  195. }
  196. /**
  197. * Budget réellement disponible =
  198. * budgetEstime - montantRealise - montantReserve
  199. */
  200. public function getResteDisponible(): float
  201. {
  202. return (float)$this->getBudgetEstime()
  203. - (float)($this->getMontantRealise() ?? 0)
  204. - (float)($this->getMontantReserve() ?? 0);
  205. }
  206. /**
  207. * Récapitulatif complet du budget
  208. */
  209. public function getBudgetRecap(): array
  210. {
  211. $estime = (float)$this->getBudgetEstime();
  212. $realise = (float)($this->getMontantRealise() ?? 0);
  213. $reserve = (float)($this->getMontantReserve() ?? 0);
  214. $disponible = $estime - $realise - $reserve;
  215. return [
  216. 'estime' => $estime,
  217. 'realise' => $realise,
  218. 'reserve' => $reserve,
  219. 'disponible' => $disponible,
  220. 'taux' => $estime > 0
  221. ? round((($realise + $reserve) / $estime) * 100, 1)
  222. : 0,
  223. ];
  224. }
  225. }