diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 89e94cad78..d546ec315a 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -39,6 +39,8 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Http\Api\ExchangeRateConverter; +use FireflyIII\Support\Report\Summarizer\TransactionSummarizer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; @@ -93,10 +95,10 @@ class BasicController extends Controller public function basic(DateRequest $request): JsonResponse { // parameters for boxes: - $dates = $request->getAll(); - $start = $dates['start']; - $end = $dates['end']; - $code = $request->get('currency_code'); + $dates = $request->getAll(); + $start = $dates['start']; + $end = $dates['end']; + $code = $request->get('currency_code'); // balance information: $balanceData = $this->getBalanceInformation($start, $end); @@ -107,10 +109,10 @@ class BasicController extends Controller // $billData = []; // $spentData = []; // $netWorthData = []; - $total = array_merge($balanceData, $billData, $spentData, $netWorthData); + $total = array_merge($balanceData, $billData, $spentData, $netWorthData); // give new keys - $return = []; + $return = []; foreach ($total as $entry) { if (null === $code || ($code === $entry['currency_code'])) { $return[$entry['key']] = $entry; @@ -122,55 +124,119 @@ class BasicController extends Controller private function getBalanceInformation(Carbon $start, Carbon $end): array { + Log::debug('getBalanceInformation'); // some config settings $convertToNative = Amount::convertToNative(); $default = Amount::getNativeCurrency(); // prep some arrays: - $incomes = []; - $expenses = []; - $sums = []; - $return = []; + $incomes = []; + $expenses = []; + $sums = []; + $return = []; + $currencies = [ + $default->id => $default, + ]; // collect income of user using the new group collector. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::DEPOSIT->value]); + $collector = app(GroupCollectorInterface::class); + $summarizer = new TransactionSummarizer(); + $set = $collector->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value])->getExtractedJournals(); + $incomes = $summarizer->groupByCurrencyId($set, 'positive', false); - $set = $collector->getExtractedJournals(); - /** @var array $journal */ - foreach ($set as $journal) { - $currencyId = $convertToNative ? $default->id : (int) $journal['currency_id']; - $amount = Amount::getAmountFromJournal($journal); - $incomes[$currencyId] ??= '0'; - $incomes[$currencyId] = bcadd( - $incomes[$currencyId], - bcmul($amount, '-1') - ); - $sums[$currencyId] ??= '0'; - $sums[$currencyId] = bcadd($sums[$currencyId], bcmul($amount, '-1')); - } - - // collect expenses of user using the new group collector. + // collect expenses of user. +// collect expenses of user using the new group collector. /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - $set = $collector->getExtractedJournals(); + $collector = app(GroupCollectorInterface::class); + $set = $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->getExtractedJournals(); + $expenses = $summarizer->groupByCurrencyId($set, 'negative', false); - /** @var array $journal */ - foreach ($set as $journal) { - $currencyId = $convertToNative ? $default->id : (int) $journal['currency_id']; - $amount = Amount::getAmountFromJournal($journal); - $expenses[$currencyId] ??= '0'; - $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); - $sums[$currencyId] ??= '0'; - $sums[$currencyId] = bcadd($sums[$currencyId], $amount); + // if convert to native, do so right now. + if ($convertToNative) { + $newExpenses = [ + $default->id => [ + 'currency_id' => $default->id, + 'currency_code' => $default->code, + 'currency_symbol' => $default->symbol, + 'currency_decimal_places' => $default->decimal_places, + 'sum' => '0', + ], + ]; + $newIncomes = [ + $default->id => [ + 'currency_id' => $default->id, + 'currency_code' => $default->code, + 'currency_symbol' => $default->symbol, + 'currency_decimal_places' => $default->decimal_places, + 'sum' => '0', + ], + ]; + $sums = [ + $default->id => [ + 'currency_id' => $default->id, + 'currency_code' => $default->code, + 'currency_symbol' => $default->symbol, + 'currency_decimal_places' => $default->decimal_places, + 'sum' => '0', + ], + ]; + + $converter = new ExchangeRateConverter(); + // loop over income and expenses + foreach([$expenses, $incomes] as $index => $array) { + + // loop over either one. + foreach ($array as $entry) { + + // if it is the native currency already. + if ($entry['currency_id'] === $default->id) { + $sums[$default->id]['sum'] = bcadd($entry['sum'], $sums[$default->id]['sum']); + + // don't forget to add it to newExpenses and newIncome + if(0 === $index) { + $newExpenses[$default->id]['sum'] = bcadd($newExpenses[$default->id]['sum'], $entry['sum']); + } + if(1 === $index) { + $newIncomes[$default->id]['sum'] = bcadd($newIncomes[$default->id]['sum'], $entry['sum']); + } + + continue; + } + + $currencies[$entry['currency_id']] ??= $this->currencyRepos->find($entry['currency_id']); + $convertedSum = $converter->convert($currencies[$entry['currency_id']], $default, $start, $entry['sum']); + $sums[$default->id]['sum'] = bcadd($sums[$default->id]['sum'], $convertedSum); + if(0 === $index) { + $newExpenses[$default->id]['sum'] = bcadd($newExpenses[$default->id]['sum'], $convertedSum); + } + if(1 === $index) { + $newIncomes[$default->id]['sum'] = bcadd($newIncomes[$default->id]['sum'], $convertedSum); + } + } + } + $incomes = $newIncomes; + $expenses = $newExpenses; + } + if(!$convertToNative) { + foreach([$expenses, $incomes] as $array) { + foreach ($array as $entry) { + $currencyId = $entry['currency_id']; + $sums[$currencyId] ??= [ + 'currency_id' => $entry['currency_id'], + 'currency_code' => $entry['currency_code'], + 'currency_symbol' => $entry['currency_symbol'], + 'currency_decimal_places' => $entry['currency_decimal_places'], + 'sum' => '0', + ]; + $sums[$currencyId]['sum'] = bcadd($sums[$currencyId]['sum'], $entry['sum']); + } + } } - // format amounts: - $keys = array_keys($sums); + $keys = array_keys($sums); foreach ($keys as $currencyId) { - $currency = $this->currencyRepos->find($currencyId); + $currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId); if (null === $currency) { continue; } @@ -178,37 +244,37 @@ class BasicController extends Controller $return[] = [ 'key' => sprintf('balance-in-%s', $currency->code), 'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]), - 'monetary_value' => $sums[$currencyId] ?? '0', + 'monetary_value' => $sums[$currencyId]['sum'] ?? '0', 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, - 'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false), + 'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId]['sum'] ?? '0', false), 'local_icon' => 'balance-scale', - 'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) - .' + '.app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), + 'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false) + . ' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false), ]; $return[] = [ 'key' => sprintf('spent-in-%s', $currency->code), 'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]), - 'monetary_value' => $expenses[$currencyId] ?? '0', + 'monetary_value' => $expenses[$currencyId]['sum'] ?? '0', 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, - 'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false), + 'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false), 'local_icon' => 'balance-scale', 'sub_title' => '', ]; $return[] = [ 'key' => sprintf('earned-in-%s', $currency->code), 'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]), - 'monetary_value' => $incomes[$currencyId] ?? '0', + 'monetary_value' => $incomes[$currencyId]['sum'] ?? '0', 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, - 'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), + 'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false), 'local_icon' => 'balance-scale', 'sub_title' => '', ]; @@ -227,7 +293,7 @@ class BasicController extends Controller 'value_parsed' => app('amount')->formatAnything($currency, '0', false), 'local_icon' => 'balance-scale', 'sub_title' => app('amount')->formatAnything($currency, '0', false) - .' + '.app('amount')->formatAnything($currency, '0', false), + . ' + ' . app('amount')->formatAnything($currency, '0', false), ]; $return[] = [ 'key' => sprintf('spent-in-%s', $currency->code), @@ -268,7 +334,7 @@ class BasicController extends Controller $paidAmount = $this->billRepository->sumPaidInRange($start, $end); $unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end); - $return = []; + $return = []; /** * @var array $info @@ -369,7 +435,7 @@ class BasicController extends Controller Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum'])); - $return[] = [ + $return[] = [ 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']), 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]), 'monetary_value' => $leftToSpend, @@ -416,16 +482,16 @@ class BasicController extends Controller $end->endOfDay(); /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); Log::debug(sprintf('getNetWorthInfo up until "%s".', $end->format('Y-m-d H:i:s'))); /** @var NetWorthInterface $netWorthHelper */ $netWorthHelper = app(NetWorthInterface::class); $netWorthHelper->setUser($user); - $allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]); + $allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]); // filter list on preference of being included. - $filtered = $allAccounts->filter( + $filtered = $allAccounts->filter( function (Account $account) { $includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth'); @@ -433,13 +499,13 @@ class BasicController extends Controller } ); - $netWorthSet = $netWorthHelper->byAccounts($filtered, $end); - $return = []; + $netWorthSet = $netWorthHelper->byAccounts($filtered, $end); + $return = []; foreach ($netWorthSet as $key => $data) { if ('native' === $key) { continue; } - $amount = $data['balance']; + $amount = $data['balance']; if (0 === bccomp($amount, '0')) { continue; } diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index 6c3a22fd6a..27f5bc1cda 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -49,7 +49,7 @@ class TransactionSummarizer $this->convertToNative = Amount::convertToNative($user); } - public function groupByCurrencyId(array $journals, string $method = 'negative'): array + public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array { Log::debug(sprintf('Now in groupByCurrencyId(array, "%s")', $method)); $array = []; @@ -94,10 +94,10 @@ class TransactionSummarizer } } if (!$this->convertToNative) { - Log::debug(sprintf('Journal #%d also includes foreign amount (foreign is %s)', $journal['transaction_journal_id'], $journal['foreign_currency_code'])); // use foreign amount? $foreignCurrencyId = (int) $journal['foreign_currency_id']; if (0 !== $foreignCurrencyId) { + Log::debug(sprintf('Journal #%d also includes foreign amount (foreign is "%s")', $journal['transaction_journal_id'], $journal['foreign_currency_code'])); $foreignCurrencyName = $journal['foreign_currency_name']; $foreignCurrencySymbol = $journal['foreign_currency_symbol']; $foreignCurrencyCode = $journal['foreign_currency_code']; @@ -124,7 +124,7 @@ class TransactionSummarizer } // then process foreign amount, if it exists. - if (0 !== $foreignCurrencyId) { + if (0 !== $foreignCurrencyId && true === $includeForeign) { $amount = (string) ($journal['foreign_amount'] ?? '0'); $array[$foreignCurrencyId] ??= [ 'sum' => '0', diff --git a/config/firefly.php b/config/firefly.php index 95bfbe5881..da3a7baf7c 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -187,6 +187,7 @@ return [ 'currencyPreference' => 'EUR', 'language' => 'en_US', 'locale' => 'equal', + 'convertToNative' => false, ], 'default_currency' => 'EUR', 'default_language' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'),