<?php
namespace App\Service;
use App\Repository\CurrencyExchangerateRepository;
use Exception;
use Sherlockode\ConfigurationBundle\Manager\ParameterManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\User\UserInterface;
class CurrencyService
{
public function __construct(
private readonly CurrencyExchangerateRepository $currencyExchangerateRepository,
private readonly ParameterManagerInterface $parameterManager,
private readonly ParameterService $parameterService,
private readonly RequestStack $requestStack
){}
public function getCurrencies(): array
{
$currencies = [];
$parameters = $this->parameterManager->getAll();
foreach ($parameters as $key => $value) {
if (str_starts_with($key, 'Currency_')){
$currencies [$value] = $value;
}
}
return $currencies;
}
/**
* Retourne la devise active de la session.
* Si la session ne contient rien, on retombe sur la devise de référence du projet.
*/
public function getSessionCurrency() : string{
$session = $this->requestStack->getSession();
return $session->get('userCurrency') ?? $this->parameterService->getReferenceCurrency();
}
/**
* Retourne la locale active de la session.
* Cette valeur pilote l'affichage RTL/LTR et le formatage des montants.
*/
public function getSessionLocale(): string
{
$session = $this->requestStack->getSession();
return $session->get('_locale') ?? 'fr';
}
/**
* Calcule le libellé affichable d'une devise en fonction de la locale.
* On tente d'utiliser Intl pour obtenir le symbole localisé, puis on revient au code ISO.
*/
public function getCurrencyLabel(?string $currency, ?string $locale = null, ?string $fallbackCurrency = null): string
{
$resolvedCurrency = $currency ?? $fallbackCurrency;
if (empty($resolvedCurrency)) {
return '';
}
$normalizedCurrency = strtoupper((string) $resolvedCurrency);
$normalizedLocale = strtolower(str_replace('_', '-', $locale ?? $this->getSessionLocale()));
try {
$formatter = new \NumberFormatter($normalizedLocale, \NumberFormatter::CURRENCY);
$formatted = $formatter->formatCurrency(1, $normalizedCurrency);
if ($formatted !== false) {
$symbol = preg_replace('/[\p{N}\p{Z}\p{P}]/u', '', $formatted);
if (is_string($symbol) && trim($symbol) !== '') {
return trim($symbol);
}
}
} catch (\Throwable $throwable) {
// Fall back to the ISO code below.
}
return $normalizedCurrency;
}
/**
* Convertit un montant d'une devise source vers une devise cible.
* Le résultat est arrondi au supérieur pour conserver un montant exploitable côté métier.
* @throws Exception
*/
public function convert($amount, $currencyFrom, $currencyTo) : ?float
{
$exchangeRate = $this->calculateExchangeRate($currencyFrom, $currencyTo);
return ceil($amount * $exchangeRate);
}
/**
* Récupère le taux de change entre deux devises.
* Si les devises sont identiques, on renvoie 1 pour éviter une requête inutile.
* @throws Exception
*/
public function calculateExchangeRate($currencyFrom, $currencyTo) : float
{
if($currencyFrom == $currencyTo){
return 1.0;
}else
{
$currencyExchange = $this->currencyExchangerateRepository->findOneBy(
[
'currencyFrom' => $currencyFrom,
'currencyTo' => $currencyTo
]
);
if($currencyExchange!=null){
return $currencyExchange->getExchangeRate();
}else{
throw new Exception("Currency ExchangeRate Exception");
return 1;
}
}
}
/**
* Retourne la liste des devises disponibles dans les taux d'échange configurés.
* Utile pour alimenter les sélecteurs de devise en front et en back.
*/
public function getCurrenciesFromExchange(): array
{
$exchangeCurrencies = $this->currencyExchangerateRepository->findAll();
$currenciesToValues = array_map(function($currency) {
return $currency->getCurrencyTo();
}, $exchangeCurrencies);
$currencyArrayWithValues = [];
foreach ($currenciesToValues as $currency) {
$currencyArrayWithValues[$currency] = $currency;
}
return $currencyArrayWithValues;
}
/**
* Détermine la devise de vente liée au user connecté.
* Si aucun customer n'est associé, on garde la devise de référence du projet.
*/
public function getSaleCurrency(?UserInterface $user) : ?string
{
$customer = ($user?$user->getCustomer() : null);
if(is_null($user) || is_null($customer)){
// return $this->getSessionCurrency();
return $this->parameterService->getReferenceCurrency();
}
return $customer->getCurrency();
}
/**
* Retourne le code ISO4217 numérique de certaines devises utilisées par les passerelles de paiement.
*/
public function getISO4217Code(?string $currency) : ?int // 3 characters
{
return match ($currency) {
'TND' => 788,
'EUR' => 200,
'USD' => 100,
default => null,
};
}
}