src/Service/CurrencyService.php line 43

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Repository\CurrencyExchangerateRepository;
  4. use Exception;
  5. use Sherlockode\ConfigurationBundle\Manager\ParameterManagerInterface;
  6. use Symfony\Component\HttpFoundation\RequestStack;
  7. use Symfony\Component\Security\Core\User\UserInterface;
  8. class CurrencyService
  9. {
  10.     public function __construct(
  11.         private readonly CurrencyExchangerateRepository $currencyExchangerateRepository,
  12.         private readonly ParameterManagerInterface      $parameterManager,
  13.         private readonly ParameterService               $parameterService,
  14.         private readonly RequestStack                   $requestStack
  15.     ){}
  16.     public function getCurrencies(): array
  17.     {
  18.         $currencies = [];
  19.         $parameters = $this->parameterManager->getAll();
  20.         foreach ($parameters as $key => $value) {
  21.             if (str_starts_with($key, 'Currency_')){
  22.                 $currencies [$value] = $value;
  23.             }
  24.         }
  25.         return $currencies;
  26.     }
  27.     /**
  28.      * Retourne la devise active de la session.
  29.      * Si la session ne contient rien, on retombe sur la devise de référence du projet.
  30.      */
  31.     public function getSessionCurrency() : string{
  32.         $session = $this->requestStack->getSession();
  33.         return $session->get('userCurrency') ?? $this->parameterService->getReferenceCurrency();
  34.     }
  35.     /**
  36.      * Retourne la locale active de la session.
  37.      * Cette valeur pilote l'affichage RTL/LTR et le formatage des montants.
  38.      */
  39.     public function getSessionLocale(): string
  40.     {
  41.         $session = $this->requestStack->getSession();
  42.         return $session->get('_locale') ?? 'fr';
  43.     }
  44.     /**
  45.      * Calcule le libellé affichable d'une devise en fonction de la locale.
  46.      * On tente d'utiliser Intl pour obtenir le symbole localisé, puis on revient au code ISO.
  47.      */
  48.     public function getCurrencyLabel(?string $currency, ?string $locale = null, ?string $fallbackCurrency = null): string
  49.     {
  50.         $resolvedCurrency = $currency ?? $fallbackCurrency;
  51.         if (empty($resolvedCurrency)) {
  52.             return '';
  53.         }
  54.         $normalizedCurrency = strtoupper((string) $resolvedCurrency);
  55.         $normalizedLocale = strtolower(str_replace('_', '-', $locale ?? $this->getSessionLocale()));
  56.         try {
  57.             $formatter = new \NumberFormatter($normalizedLocale, \NumberFormatter::CURRENCY);
  58.             $formatted = $formatter->formatCurrency(1, $normalizedCurrency);
  59.             if ($formatted !== false) {
  60.                 $symbol = preg_replace('/[\p{N}\p{Z}\p{P}]/u', '', $formatted);
  61.                 if (is_string($symbol) && trim($symbol) !== '') {
  62.                     return trim($symbol);
  63.                 }
  64.             }
  65.         } catch (\Throwable $throwable) {
  66.             // Fall back to the ISO code below.
  67.         }
  68.         return $normalizedCurrency;
  69.     }
  70.     /**
  71.      * Convertit un montant d'une devise source vers une devise cible.
  72.      * Le résultat est arrondi au supérieur pour conserver un montant exploitable côté métier.
  73.      * @throws Exception
  74.      */
  75.     public function convert($amount, $currencyFrom, $currencyTo) : ?float
  76.     {
  77.         $exchangeRate = $this->calculateExchangeRate($currencyFrom, $currencyTo);
  78.         return ceil($amount * $exchangeRate);
  79.     }
  80.     /**
  81.      * Récupère le taux de change entre deux devises.
  82.      * Si les devises sont identiques, on renvoie 1 pour éviter une requête inutile.
  83.      * @throws Exception
  84.      */
  85.     public function calculateExchangeRate($currencyFrom, $currencyTo) : float
  86.     {
  87.         if($currencyFrom == $currencyTo){
  88.             return 1.0;
  89.         }else
  90.         {
  91.             $currencyExchange = $this->currencyExchangerateRepository->findOneBy(
  92.                 [
  93.                     'currencyFrom' => $currencyFrom,
  94.                     'currencyTo' => $currencyTo
  95.                 ]
  96.             );
  97.             if($currencyExchange!=null){
  98.                 return $currencyExchange->getExchangeRate();
  99.             }else{
  100.                 throw new Exception("Currency ExchangeRate Exception");
  101.                 return 1;
  102.             }
  103.         }
  104.     }
  105.     /**
  106.      * Retourne la liste des devises disponibles dans les taux d'échange configurés.
  107.      * Utile pour alimenter les sélecteurs de devise en front et en back.
  108.      */
  109.     public function  getCurrenciesFromExchange(): array
  110.     {
  111.         $exchangeCurrencies = $this->currencyExchangerateRepository->findAll();
  112.         $currenciesToValues = array_map(function($currency) {
  113.             return $currency->getCurrencyTo();
  114.         }, $exchangeCurrencies);
  115.         $currencyArrayWithValues = [];
  116.         foreach ($currenciesToValues as $currency) {
  117.             $currencyArrayWithValues[$currency] = $currency;
  118.         }
  119.         return $currencyArrayWithValues;
  120.     }
  121.     /**
  122.      * Détermine la devise de vente liée au user connecté.
  123.      * Si aucun customer n'est associé, on garde la devise de référence du projet.
  124.      */
  125.     public function getSaleCurrency(?UserInterface $user) : ?string
  126.     {
  127.         $customer = ($user?$user->getCustomer() : null);
  128.         if(is_null($user) || is_null($customer)){
  129. //            return $this->getSessionCurrency();
  130.             return $this->parameterService->getReferenceCurrency();
  131.         }
  132.         return $customer->getCurrency();
  133.     }
  134.     /**
  135.      * Retourne le code ISO4217 numérique de certaines devises utilisées par les passerelles de paiement.
  136.      */
  137.     public function getISO4217Code(?string $currency) : ?int // 3 characters
  138.     {
  139.         return match ($currency) {
  140.             'TND' => 788,
  141.             'EUR' => 200,
  142.             'USD' => 100,
  143.             default => null,
  144.         };
  145.     }
  146. }