Skip to content

Commit

Permalink
currency no-join-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
yurikuzn committed Dec 19, 2022
1 parent cf7f382 commit 085e5ef
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 90 deletions.
342 changes: 252 additions & 90 deletions application/Espo/Core/Utils/Database/Orm/Fields/Currency.php
Expand Up @@ -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' => [
Expand All @@ -50,117 +50,279 @@ protected function load($fieldName, $entityType)
],
];

$params = $this->getFieldParams($fieldName);

if (!empty($params['notStorable'])) {
$defs[$entityType]['fields'][$fieldName]['notStorable'] = true;
}
else {
if ($this->config->get('currencyNoJoinMode')) {
$this->applyNoJoinMode($fieldName, $entityType, $defs);

}
else {
$this->applyJoinMode($fieldName, $entityType, $defs);
}
}

$defs[$entityType]['fields'][$fieldName]['attributeRole'] = 'value';
$defs[$entityType]['fields'][$fieldName]['fieldType'] = 'currency';

$defs[$entityType]['fields'][$fieldName . 'Currency']['attributeRole'] = 'currency';
$defs[$entityType]['fields'][$fieldName . 'Currency']['fieldType'] = 'currency';

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)";

$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' => [
$defs[$entityType]['fields'][$fieldName . 'Converted'] = [
'type' => 'float',
'select' => [
'select' => $mulExpression,
'leftJoins' => $leftJoins,
],
'selectForeign' => [
'select' => "MUL:({alias}.{$fieldName}, {$foreignCurrencyAlias}.rate)",
'leftJoins' => [
[
'Currency',
$foreignCurrencyAlias,
[
'Currency',
$foreignCurrencyAlias,
[
$foreignCurrencyAlias . '.id:' => "{alias}.{$fieldName}Currency",
]
$foreignCurrencyAlias . '.id:' => "{alias}.{$fieldName}Currency",
]
]
],
],
'where' => [
"=" => [
'whereClause' => [
$mulExpression . '=' => '{value}',
],
'leftJoins' => $leftJoins,
],
'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}'],
">" => [
'whereClause' => [
$mulExpression . '>' => '{value}',
],
'leftJoins' => $leftJoins,
'additionalSelect' => ["{$alias}.rate"],
],
'attributeRole' => 'valueConverted',
'fieldType' => 'currency',
];

$defs[$entityType]['fields'][$fieldName]['order'] = [
"order" => [
"<" => [
'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);
}

$defs[$entityType]['fields'][$fieldName]['attributeRole'] = 'value';
$defs[$entityType]['fields'][$fieldName]['fieldType'] = 'currency';
$expr = Expr::multiply(
Expr::column($fieldName),
Expr::if(
Expr::equal(Expr::column($currencyAttribute), $defaultCurrency),
1.0,
$this->buildExpression($currencyAttribute, $rates)
)
)->getValue();

$defs[$entityType]['fields'][$fieldName . 'Currency']['attributeRole'] = 'currency';
$defs[$entityType]['fields'][$fieldName . 'Currency']['fieldType'] = 'currency';
$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();

return $defs;
$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)
);
}
}
1 change: 1 addition & 0 deletions application/Espo/Resources/defaults/config.php
Expand Up @@ -65,6 +65,7 @@
'defaultCurrency' => 'USD',
'baseCurrency' => 'USD',
'currencyRates' => [],
'currencyNoJoinMode' => false,
'outboundEmailIsShared' => true,
'outboundEmailFromName' => 'EspoCRM',
'outboundEmailFromAddress' => '',
Expand Down

0 comments on commit 085e5ef

Please sign in to comment.