<?php namespace App\Entity; use App\Enum\MoyenEnum; use App\Flux\FluxInterface; use Doctrine\Common\Persistence\Event\LifecycleEventArgs; use Doctrine\ORM\Mapping as ORM; use Gedmo\Timestampable\Traits\TimestampableEntity; use Ramsey\Uuid\Doctrine\UuidGenerator; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; /** * FLUX = TRANSFERT ou TRANSACTION ou COTISATION ou RECONVERSION ou RETRAIT ou VENTE ou ACHAT MONNAIE. * * @ORM\Entity(repositoryClass="App\Repository\FluxRepository") * @ORM\HasLifecycleCallbacks() * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="discr", type="string") * @ORM\DiscriminatorMap({ * "achat_monnaie" = "AchatMonnaie", * "achat_monnaie_adherent" = "AchatMonnaieAdherent", * "achat_monnaie_prestataire" = "AchatMonnaiePrestataire", * "change" = "Change", * "chg_pre_cpt" = "ChangePrestataireComptoir", * "chg_adh_cpt" = "ChangeAdherentComptoir", * "cotisation" = "Cotisation", * "cotisation_adherent" = "CotisationAdherent", * "cotisation_prestataire" = "CotisationPrestataire", * "don" = "Don", * "don_adherent" = "DonAdherent", * "don_prestataire" = "DonPrestataire", * "retrait" = "Retrait", * "ret_cpt_adh" = "RetraitComptoirAdherent", * "ret_cpt_pre" = "RetraitComptoirPrestataire", * "transaction" = "Transaction", * "tro_adh_pre" = "TransactionAdherentPrestataire", * "tro_adh_adh" = "TransactionAdherentAdherent", * "tro_pre_adh" = "TransactionPrestataireAdherent", * "tro_pre_pre" = "TransactionPrestatairePrestataire", * "transfert" = "Transfert", * "tre_cpt_grp" = "TransfertComptoirGroupe", * "tre_grp_cpt" = "TransfertGroupeComptoir", * "tre_pre_sie" = "Reconversion", * "tre_sie_grp" = "TransfertSiegeGroupe", * "tre_grp_sie" = "TransfertGroupeSiege", * "vente" = "Vente", * "vte_cpt_pre" = "VenteComptoirPrestataire", * "vte_cpt_adh" = "VenteComptoirAdherent", * "venteemlc" = "VenteEmlc", * "vte_emlc_cpt_adh" = "VenteEmlcComptoirAdherent", * "vte_emlc_cpt_pre" = "VenteEmlcComptoirPrestataire", * "ticket_fix" = "TicketFix", * "ticket_fix_print" = "TicketFixPrint", * "ticket_fix_destroy" = "TicketFixDestroy", * "application_cotisation_tav" = "CotisationTavApplication", * "reversement_cotisation_adherent" = "CotisationTavReversement", * "prelevement_cotisation_adherent" = "CotisationTavPrelevement", * "prelevement_cotisation_adherent_depassement_plafond" = "CotisationTavPrelevementDepassementPlafond", * }) */ abstract class Flux implements FluxInterface { use TimestampableEntity; const TYPE_ACHAT = 'achat'; const TYPE_CHANGE = 'change'; const TYPE_COTISATION = 'cotisation'; const TYPE_DON = 'don'; const TYPE_RECONVERSION = 'reconversion'; const TYPE_RETRAIT = 'retrait'; const TYPE_TRANSACTION = 'transaction'; const TYPE_TRANSFERT = 'transfert'; const TYPE_VENTE = 'vente'; const TYPE_VENTE_EMLC = 'vente_emlc'; const TYPE_TICKET_FIX = 'ticket_fix'; const TYPE_APPLICATION_COTISATION_TAV = 'application_cotisation_tav'; /** * @var \Ramsey\Uuid\UuidInterface * * @ORM\Id * @ORM\Column(type="uuid", unique=true) * @ORM\GeneratedValue(strategy="CUSTOM") * @ORM\CustomIdGenerator(class=UuidGenerator::class) * @Groups({"read"}) */ protected $id; /** * @ORM\ManyToOne(targetEntity="User", inversedBy="flux") * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true) * @Groups({"read", "write"}) */ protected $operateur; /** * Role en cours de l'utilisateur. * * @var string * * @ORM\Column(name="role", type="string", length=200, nullable=true) * @Groups({"read", "write"}) */ protected $role; /** * Type de transfert / transaction : exemple : Prestataire à Adhérent. * * @var string * * @ORM\Column(name="type", type="string", length=200) * @Assert\NotBlank * @Groups({"read", "write"}) */ protected $type; /** * Type de flux : transfert / transaction. * * @var string * * @ORM\Column(name="parenttype", type="string", length=30) * @Assert\NotBlank * @Groups({"read", "write"}) */ protected $parenttype; /** * @var float * * @ORM\Column(name="montant", type="decimal", scale=2) * @Assert\NotBlank * @Assert\Type("numeric") * @Assert\GreaterThanOrEqual( * value = 0 * ) * @Groups({"read", "write"}) */ protected $montant; /** * @var string * * @ORM\Column(name="moyen", type="string", length=100) * @Assert\NotBlank * @Groups({"read", "write"}) */ private $moyen; /** * @var string|null * * @ORM\Column(name="reference", type="string", length=255, nullable=true) * @Assert\NotBlank * @Groups({"read", "write"}) */ protected $reference; /** * Hash => permet de vérifier l'intégrité des données. * * @var text * * @ORM\Column(name="hash", type="text", options={"default" : "tmp"}) * @Groups({"read"}) */ protected $hash = 'tmp'; /** * @var float * * @ORM\Column(name="tauxreconversion", type="decimal", precision=7, scale=2, nullable=true) * @Groups({"read", "write"}) */ protected $tauxreconversion; /** * @var float * * @ORM\Column(name="data", type="array", nullable=true) * @Groups({"read", "write"}) */ protected $data; /** * @Assert\NotBlank * @Groups({"read", "write"}) */ protected $expediteur = null; /** * @Assert\NotBlank * @Groups({"read", "write"}) */ protected $destinataire = null; /** * @Groups({"read", "write"}) */ protected $cotisationInfos = null; /** * @var ArrayCollection|OperationAdherent[] * @ORM\OneToMany(targetEntity=OperationAdherent::class, mappedBy="flux") */ protected $operationsAdherent; /** * @var ArrayCollection|OperationPrestataire[] * @ORM\OneToMany(targetEntity=OperationPrestataire::class, mappedBy="flux") */ protected $operationsPrestataire; /** * @var ArrayCollection|OperationComptoir[] * @ORM\OneToMany(targetEntity=OperationComptoir::class, mappedBy="flux") */ protected $operationsComptoir; /** * @var ArrayCollection|OperationGroupe[] * @ORM\OneToMany(targetEntity=OperationGroupe::class, mappedBy="flux") */ protected $operationsGroupe; /** * @var ArrayCollection|OperationSiege[] * @ORM\OneToMany(targetEntity=OperationSiege::class, mappedBy="flux") */ protected $operationsSiege; /** * @var string|null * * @Assert\Type("bool") * @ORM\Column(name="historical", type="boolean", nullable=false, options={"default" : false}) */ protected $historical; abstract public function getParenttype(): string; abstract public function getType(): string; /** * Retourne la liste des opérations à effectuer. * * @return array Tableau d'operations */ abstract public function getAllOperations($em); /** * @deprecated since v2.1.4 */ abstract public function operate($em); /** * Obtenir la liste des utilisateurs à notifier. * * @return array Tableau d'utilisateurs */ abstract public function getUsersToNotify(); public function __construct() { $this->parenttype = $this->getParenttype(); $this->type = $this->getType(); $this->historical = false; } public function getId() { return $this->id; } /** * @param $parenttype * * @return $this */ public function setParenttype($parenttype) { $this->parenttype = $parenttype; return $this; } /** * @param User $destinataire * * @return $this */ public function setOperateur(?User $operateur) { $this->operateur = $operateur; return $this; } /** * @return User operateur */ public function getOperateur(): ?User { return $this->operateur; } /** * @param $destinataire * * @return $this */ public function setDestinataire($destinataire) { $this->destinataire = $destinataire; return $this; } /** * @return destinataire */ public function getDestinataire() { return $this->destinataire; } /** * @param $expediteur * * @return $this */ public function setExpediteur($expediteur) { $this->expediteur = $expediteur; return $this; } /** * @return expediteur */ public function getExpediteur() { return $this->expediteur; } /** * @param string $type * * @return Transaction */ public function setType(string $type) { $this->type = $type; return $this; } /** * @return string */ public function getRole(): ?string { return $this->role; } /** * @param string $role * * @return Transaction */ public function setRole(?string $role) { $this->role = $role; return $this; } /** * @return float */ public function getMontant(): ?float { return number_format($this->montant, 2, '.', ''); } /** * @param float $montant * * @return Transaction */ public function setMontant(float $montant) { $this->montant = $montant; return $this; } /** * @return string */ public function getReference(): ?string { return $this->reference; } /** * @param string $reference * * @return Transaction */ public function setReference(string $reference) { $this->reference = $reference; return $this; } /** * Get hash. * * @return text */ public function getHash() { return $this->hash; } /** * Set hash. * * @return $this */ public function setHash($hash) { $this->hash = $hash; return $this; } /** * @return string */ public function getMoyen(): ?string { return $this->moyen; } public function setMoyen($moyen) { if (!in_array($moyen, MoyenEnum::getAvailableTypes())) { throw new \InvalidArgumentException('Moyen de paiement invalide !'); } $this->moyen = $moyen; return $this; } /** * Get tauxreconversion. * * @return */ public function getTauxreconversion(): ?float { return $this->tauxreconversion; } public function getTauxreconversionpercent(): ?float { return $this->tauxreconversion / 100; } /** * Set tauxreconversion. * * @return $this */ public function setTauxreconversion(?float $tauxreconversion): self { $this->tauxreconversion = $tauxreconversion; return $this; } /** * Get data. * * @return */ public function getData(): ?array { return $this->data; } /** * Set data. * * @return $this */ public function setData(?array $data) { $this->data = $data; return $this; } public function isValidationAchat() { if (null !== $this->getData()) { if (isset($this->data['validationAchat']) && true == $this->data['validationAchat']) { return true; } } return false; } public function getMontantareconvertir() { return round($this->getMontant() - ($this->getMontant() * ($this->getTauxreconversion() / 100)), 2); } public function getMontantcommission() { return round(($this->getMontant() - $this->getMontantareconvertir()), 2); } public function isVente() { return false; } /** * Is historical ? * * @return bool */ public function isHistorical(): bool { return $this->historical; } /** * Get historical. * * @return */ public function getHistorical(): bool { return $this->historical; } /** * Set historical. * * @return $this */ public function setHistorical($historical): self { $this->historical = $historical; return $this; } public function getVerify() { if (null == $this->getHash()) { return 'Vide'; } return (true === password_verify($this->getAllInfosUncrypted(), $this->getHash())) ? 'Oui' : 'Non'; } /** * @ORM\PrePersist * * @param LifecycleEventArgs $event */ public function prePersist(LifecycleEventArgs $event) { $flux = $event->getEntity(); if (empty($flux->getExpediteur())) { throw new \Exception("Opération impossible ! Pas d'expéditeur !"); } if ($flux->getMontant() <= 0 && !(self::TYPE_COTISATION == $flux->getParenttype() || self::TYPE_DON == $flux->getParenttype())) { throw new \Exception('Opération impossible ! Montant inférieur ou égal à zéro !'); } if ($flux->getExpediteur() == $flux->getDestinataire()) { throw new \Exception("Opération impossible ! L'expéditeur et le destinataire ne peuvent pas être similaire !"); } // $flux->setHash('tmp'); } /** * @ORM\PostPersist * * @param LifecycleEventArgs $event */ public function postPersist(LifecycleEventArgs $event): void { $this->updateHash($event); } private function updateHash(LifecycleEventArgs $event): void { $flux = $event->getEntity(); // if (empty($flux->getExpediteur())) { // throw new \Exception("[FLUX] Opération impossible ! Pas d'expéditeur !"); // } // if ($flux->getMontant() <= 0 && self::TYPE_COTISATION !== $flux->getParenttype()) { // throw new \Exception('[FLUX] Opération impossible ! Montant inférieur ou égal à zéro !'); // } // if ($flux->getExpediteur() == $flux->getDestinataire()) { // throw new \Exception('[FLUX] Opération impossible ! Expéditeur et Destinataire ne peuvent pas être les mêmes !'); // } // @TODO : generation du hash du flux gourmand en ressource => voir pour optimiser ou le faire en async ! $hash = password_hash($this->getAllInfosUncrypted(), PASSWORD_BCRYPT, ['cost' => 5]); $flux->setHash($hash); $event->getEntityManager()->persist($flux); $event->getEntityManager()->flush(); } public function getAllInfosUncrypted(): string { return $_ENV['APP_SECRET'] . $this->getId() . ($this->getOperateur() ? $this->getOperateur()->getId() : '') . $this->getRole() . $this->getType() . (0 == $this->getMontant() ? 0 : $this->getMontant()) . $this->getMoyen() . $this->getReference() . ($this->getDestinataire() ? $this->getDestinataire()->getId() : '') . ($this->getExpediteur() ? $this->getExpediteur()->getId() : ''); } public function getOperateurAndRole(): string { return $this->getOperateur() ? ($this->getOperateur()->__toString() . ' (' . $this->getRole() . ')') : $this->getRole(); } public function __toString(): string { if (empty($this->getDestinataire()) || empty($this->getExpediteur()) || null === $this->getMontant()) { return '[FLUX] Visualisation impossible ! Destinataire / Expéditeur et/ou montant manquant(s) !'; } return ($this->getCreatedAt() ? $this->getCreatedAt()->format('d/m/Y H:i') . ' | ' : '') . ucwords($this->getParenttype()) . ' : ' . $this->getExpediteur() . ' => ' . $this->getDestinataire() . ' : ' . $this->getMontant() . '€'; } public function ssaFriendlyTypeName() { $friendlyTypeNames = Flux::ssaUsefulFriendlyTypeNames(); return array_key_exists($this->type,$friendlyTypeNames) ? $friendlyTypeNames[$this->type] : $this->type; } public static function ssaUsefulFriendlyTypeNames($uniqueValues = false) { return [ VenteEmlc::TYPE_VENTE_EMLC_ADHERENT => "Achat de MonA via cotisation" . ($uniqueValues ? " (au comptoir)" : ""), AchatMonnaie::TYPE_ACHAT_ADHERENT => "Achat de MonA via cotisation" . ($uniqueValues ? " (via Payzen)" : ""), CotisationTavApplication::TYPE_REVERSEMENT_COTISATION_ADHERENT => "Allocation complémentaire de la caisse", CotisationTavApplication::TYPE_PRELEVEMENT_COTISATION_ADHERENT => "Réduction de l'allocation", CotisationTavApplication::TYPE_PRELEVEMENT_COTISATION_ADHERENT_DEPASSEMENT_PLAFOND => "Réduction de l'allocation" . ($uniqueValues ? " (dépassement plafond)" : ""), Don::TYPE_DON_ADHERENT => Don::TYPE_DON_ADHERENT, Don::TYPE_DON_PRESTATAIRE => Don::TYPE_DON_PRESTATAIRE, Reconversion::TYPE_RECONVERSION_PRESTATAIRE => Reconversion::TYPE_RECONVERSION_PRESTATAIRE, Transaction::TYPE_TRANSACTION_PRESTATAIRE_ADHERENT => Transaction::TYPE_TRANSACTION_PRESTATAIRE_ADHERENT, Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE => Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE ]; } }