currency no-join-mode

This commit is contained in:
Yuri Kuznetsov
2022-12-19 10:58:05 +02:00
parent cf7f382784
commit 085e5ef4bf
3 changed files with 263 additions and 99 deletions

View File

@@ -29,17 +29,17 @@
namespace Espo\Core\Utils\Database\Orm\Fields;
use Espo\ORM\Query\Part\Expression as Expr;
class Currency extends Base
{
/**
* @param string $fieldName
* @param string $entityType
* @return array<string,mixed>
* @return array<string, mixed>
*/
protected function load($fieldName, $entityType)
{
$alias = $fieldName . 'CurrencyRate';
$defs = [
$entityType => [
'fields' => [
@@ -50,109 +50,19 @@ class Currency extends Base
],
];
$leftJoins = [
[
'Currency',
$alias,
[$alias . '.id:' => $fieldName . 'Currency'],
]
];
$foreignCurrencyAlias = "{$alias}{$entityType}{alias}Foreign";
$mulExpression = "MUL:({$fieldName}, {$alias}.rate)";
$params = $this->getFieldParams($fieldName);
if (!empty($params['notStorable'])) {
$defs[$entityType]['fields'][$fieldName]['notStorable'] = true;
}
else {
$defs[$entityType]['fields'][$fieldName . 'Converted'] = [
'type' => 'float',
'select' => [
'select' => $mulExpression,
'leftJoins' => $leftJoins,
],
'selectForeign' => [
'select' => "MUL:({alias}.{$fieldName}, {$foreignCurrencyAlias}.rate)",
'leftJoins' => [
[
'Currency',
$foreignCurrencyAlias,
[
$foreignCurrencyAlias . '.id:' => "{alias}.{$fieldName}Currency",
]
]
],
],
'where' => [
"=" => [
'whereClause' => [
$mulExpression . '=' => '{value}',
],
'leftJoins' => $leftJoins,
],
">" => [
'whereClause' => [
$mulExpression . '>' => '{value}',
],
'leftJoins' => $leftJoins,
],
"<" => [
'whereClause' => [
$mulExpression . '<' => '{value}',
],
'leftJoins' => $leftJoins,
],
">=" => [
'whereClause' => [
$mulExpression . '>=' => '{value}',
],
'leftJoins' => $leftJoins,
],
"<=" => [
'whereClause' => [
$mulExpression . '<=' => '{value}',
],
'leftJoins' => $leftJoins,
],
"<>" => [
'whereClause' => [
$mulExpression . '!=' => '{value}',
],
'leftJoins' => $leftJoins,
],
"IS NULL" => [
'whereClause' => [
$fieldName . '=' => null,
],
],
"IS NOT NULL" => [
'whereClause' => [
$fieldName . '!=' => null,
],
],
],
'notStorable' => true,
'order' => [
'order' => [
[$mulExpression, '{direction}'],
],
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
],
'attributeRole' => 'valueConverted',
'fieldType' => 'currency',
];
if ($this->config->get('currencyNoJoinMode')) {
$this->applyNoJoinMode($fieldName, $entityType, $defs);
$defs[$entityType]['fields'][$fieldName]['order'] = [
"order" => [
[$mulExpression, '{direction}'],
],
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
];
}
else {
$this->applyJoinMode($fieldName, $entityType, $defs);
}
}
$defs[$entityType]['fields'][$fieldName]['attributeRole'] = 'value';
@@ -163,4 +73,256 @@ class Currency extends Base
return $defs;
}
/**
* @param string $fieldName
* @param string $entityType
* @param array<string, mixed> $defs
*/
private function applyJoinMode(
string $fieldName,
string $entityType,
array &$defs
): void {
$alias = $fieldName . 'CurrencyRate';
$leftJoins = [
[
'Currency',
$alias,
[$alias . '.id:' => $fieldName . 'Currency'],
]
];
$foreignCurrencyAlias = "{$alias}{$entityType}{alias}Foreign";
$mulExpression = "MUL:({$fieldName}, {$alias}.rate)";
$defs[$entityType]['fields'][$fieldName . 'Converted'] = [
'type' => 'float',
'select' => [
'select' => $mulExpression,
'leftJoins' => $leftJoins,
],
'selectForeign' => [
'select' => "MUL:({alias}.{$fieldName}, {$foreignCurrencyAlias}.rate)",
'leftJoins' => [
[
'Currency',
$foreignCurrencyAlias,
[
$foreignCurrencyAlias . '.id:' => "{alias}.{$fieldName}Currency",
]
]
],
],
'where' => [
"=" => [
'whereClause' => [
$mulExpression . '=' => '{value}',
],
'leftJoins' => $leftJoins,
],
">" => [
'whereClause' => [
$mulExpression . '>' => '{value}',
],
'leftJoins' => $leftJoins,
],
"<" => [
'whereClause' => [
$mulExpression . '<' => '{value}',
],
'leftJoins' => $leftJoins,
],
">=" => [
'whereClause' => [
$mulExpression . '>=' => '{value}',
],
'leftJoins' => $leftJoins,
],
"<=" => [
'whereClause' => [
$mulExpression . '<=' => '{value}',
],
'leftJoins' => $leftJoins,
],
"<>" => [
'whereClause' => [
$mulExpression . '!=' => '{value}',
],
'leftJoins' => $leftJoins,
],
"IS NULL" => [
'whereClause' => [
$fieldName . '=' => null,
],
],
"IS NOT NULL" => [
'whereClause' => [
$fieldName . '!=' => null,
],
],
],
'notStorable' => true,
'order' => [
'order' => [
[$mulExpression, '{direction}'],
],
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
],
'attributeRole' => 'valueConverted',
'fieldType' => 'currency',
];
$defs[$entityType]['fields'][$fieldName]['order'] = [
"order" => [
[$mulExpression, '{direction}'],
],
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
];
}
/**
* @param string $fieldName
* @param string $entityType
* @param array<string, mixed> $defs
*/
private function applyNoJoinMode(
string $fieldName,
string $entityType,
array &$defs
): void {
$currencyAttribute = $fieldName . 'Currency';
$defaultCurrency = $this->config->get('defaultCurrency');
$baseCurrency = $this->config->get('baseCurrency');
$rates = $this->config->get('currencyRates');
if ($defaultCurrency !== $baseCurrency) {
$rates = $this->exchangeRates($baseCurrency, $defaultCurrency, $rates);
}
$expr = Expr::multiply(
Expr::column($fieldName),
Expr::if(
Expr::equal(Expr::column($currencyAttribute), $defaultCurrency),
1.0,
$this->buildExpression($currencyAttribute, $rates)
)
)->getValue();
$exprForeign = Expr::multiply(
Expr::column("ALIAS.{$fieldName}"),
Expr::if(
Expr::equal(Expr::column("ALIAS.{$fieldName}Currency"), $defaultCurrency),
1.0,
$this->buildExpression("ALIAS.{$fieldName}Currency", $rates)
)
)->getValue();
$exprForeign = str_replace('ALIAS', '{alias}', $exprForeign);
$defs[$entityType]['fields'][$fieldName . 'Converted'] = [
'type' => 'float',
'select' => [
'select' => $expr,
],
'selectForeign' => [
'select' => $exprForeign,
],
'where' => [
"=" => [
'whereClause' => [
$expr . '=' => '{value}',
],
],
">" => [
'whereClause' => [
$expr . '>' => '{value}',
],
],
"<" => [
'whereClause' => [
$expr . '<' => '{value}',
],
],
">=" => [
'whereClause' => [
$expr . '>=' => '{value}',
],
],
"<=" => [
'whereClause' => [
$expr . '<=' => '{value}',
],
],
"<>" => [
'whereClause' => [
$expr . '!=' => '{value}',
],
],
"IS NULL" => [
'whereClause' => [
$expr . '=' => null,
],
],
"IS NOT NULL" => [
'whereClause' => [
$expr . '!=' => null,
],
],
],
'notStorable' => true,
'order' => [
'order' => [
[$expr, '{direction}'],
],
],
'attributeRole' => 'valueConverted',
'fieldType' => 'currency',
];
}
/**
* @param array<string, float> $currencyRates
* @return array<string, float>
*/
private function exchangeRates(string $baseCurrency, string $defaultCurrency, array $currencyRates): array
{
$precision = 5;
$defaultCurrencyRate = round(1 / $currencyRates[$defaultCurrency], $precision);
$exchangedRates = [];
$exchangedRates[$baseCurrency] = $defaultCurrencyRate;
unset($currencyRates[$baseCurrency], $currencyRates[$defaultCurrency]);
foreach ($currencyRates as $currencyName => $rate) {
$exchangedRates[$currencyName] = round($rate * $defaultCurrencyRate, $precision);
}
return $exchangedRates;
}
/**
* @param array<string, float> $rates
*/
private function buildExpression(string $currencyAttribute, array $rates): Expr|float
{
if ($rates === []) {
return 0.0;
}
$currency = array_key_first($rates);
$value = $rates[$currency];
unset($rates[$currency]);
return Expr::if(
Expr::equal(Expr::column($currencyAttribute), $currency),
$value,
$this->buildExpression($currencyAttribute, $rates)
);
}
}

View File

@@ -65,6 +65,7 @@ return [
'defaultCurrency' => 'USD',
'baseCurrency' => 'USD',
'currencyRates' => [],
'currencyNoJoinMode' => false,
'outboundEmailIsShared' => true,
'outboundEmailFromName' => 'EspoCRM',
'outboundEmailFromAddress' => '',

View File

@@ -191,6 +191,7 @@ return [
'thumbImageCacheDisabled',
'emailReminderPortionSize',
'outboundSmsFromNumber',
'currencyNoJoinMode',
'latestVersion',
],
'superAdminItems' => [