diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 54d1038f31..65e4bff36d 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -157,6 +157,10 @@ class DebugController extends Controller return view('debug', compact('table', 'now', 'logContent')); } + public function apiTest() { + return view('test.api-test'); + } + private function generateTable(): string { // system information: diff --git a/app/Support/JsonApi/Enrichments/AccountEnrichment.php b/app/Support/JsonApi/Enrichments/AccountEnrichment.php index 222f9dc9cf..a2850a597b 100644 --- a/app/Support/JsonApi/Enrichments/AccountEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AccountEnrichment.php @@ -51,8 +51,8 @@ use Override; */ class AccountEnrichment implements EnrichmentInterface { - private array $accountIds; - private array $accountTypeIds; + private array $ids; + private array $accountTypeIds; private array $accountTypes; private Collection $collection; private array $currencies; @@ -66,14 +66,15 @@ class AccountEnrichment implements EnrichmentInterface private array $lastActivities; private ?Carbon $date = null; private bool $convertToPrimary = false; + private array $balances = []; /** * TODO The account enricher must do conversion from and to the primary currency. */ public function __construct() { - $this->accountIds = []; - $this->openingBalances = []; + $this->ids = []; + $this->openingBalances = []; $this->currencies = []; $this->accountTypeIds = []; $this->accountTypes = []; @@ -105,7 +106,7 @@ class AccountEnrichment implements EnrichmentInterface // prep local fields $this->collection = $collection; - $this->collectAccountIds(); + $this->collectIds(); $this->getAccountTypes(); $this->collectMetaData(); $this->collectNotes(); @@ -118,14 +119,14 @@ class AccountEnrichment implements EnrichmentInterface return $this->collection; } - private function collectAccountIds(): void + private function collectIds(): void { /** @var Account $account */ foreach ($this->collection as $account) { - $this->accountIds[] = (int) $account->id; + $this->ids[] = (int) $account->id; $this->accountTypeIds[] = (int) $account->account_type_id; } - $this->accountIds = array_unique($this->accountIds); + $this->ids = array_unique($this->ids); $this->accountTypeIds = array_unique($this->accountTypeIds); } @@ -142,7 +143,7 @@ class AccountEnrichment implements EnrichmentInterface private function collectMetaData(): void { $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) - ->whereIn('account_id', $this->accountIds) + ->whereIn('account_id', $this->ids) ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray() ; @@ -167,7 +168,7 @@ class AccountEnrichment implements EnrichmentInterface private function collectNotes(): void { - $notes = Note::query()->whereIn('noteable_id', $this->accountIds) + $notes = Note::query()->whereIn('noteable_id', $this->ids) ->whereNotNull('notes.text') ->where('notes.text', '!=', '') ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray() @@ -180,7 +181,7 @@ class AccountEnrichment implements EnrichmentInterface private function collectLocations(): void { - $locations = Location::query()->whereIn('locatable_id', $this->accountIds) + $locations = Location::query()->whereIn('locatable_id', $this->ids) ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray() ; foreach ($locations as $location) { @@ -234,13 +235,10 @@ class AccountEnrichment implements EnrichmentInterface private function appendCollectedData(): void { - $notes = $this->notes; - $openingBalances = $this->openingBalances; - $locations = $this->locations; - $lastActivities = $this->lastActivities; - $this->collection = $this->collection->map(function (Account $item) use ($notes, $openingBalances, $locations, $lastActivities) { + $this->collection = $this->collection->map(function (Account $item) { + $id = (int) $item->id; $item->full_account_type = $this->accountTypes[(int) $item->account_type_id] ?? null; - $accountMeta = [ + $meta = [ 'currency' => null, 'location' => [ 'latitude' => null, @@ -248,45 +246,50 @@ class AccountEnrichment implements EnrichmentInterface 'zoom_level' => null, ], 'opening_balance_date' => null, + 'opening_balance_amount' => null, + 'account_number'=> null, + 'notes' => $notes[$id] ?? null, + 'last_activity' => $this->lastActivities[$id] ?? null, ]; - if (array_key_exists((int) $item->id, $this->meta)) { - foreach ($this->meta[(int) $item->id] as $name => $value) { - $accountMeta[$name] = $value; + + // if location, add location: + if (array_key_exists($id, $this->locations)) { + $meta['location'] = $this->locations[$id]; + } + if (array_key_exists($id, $this->meta)) { + foreach ($this->meta[$id] as $name => $value) { + $meta[$name] = $value; } } // also add currency, if present. - if (array_key_exists('currency_id', $accountMeta)) { - $currencyId = (int) $accountMeta['currency_id']; - $accountMeta['currency'] = $this->currencies[$currencyId]; + if (array_key_exists('currency_id', $meta)) { + $currencyId = (int) $meta['currency_id']; + $meta['currency'] = $this->currencies[$currencyId]; } - // if notes, add notes. - if (array_key_exists($item->id, $notes)) { - $accountMeta['notes'] = $notes[$item->id]; - } - // if opening balance, add opening balance - if (array_key_exists($item->id, $openingBalances)) { - $accountMeta['opening_balance_date'] = $openingBalances[$item->id]['date']; - $accountMeta['opening_balance_amount'] = $openingBalances[$item->id]['amount']; + if (array_key_exists($id, $this->openingBalances)) { + $meta['opening_balance_date'] = $this->openingBalances[$id]['date']; + $meta['opening_balance_amount'] = $this->openingBalances[$id]['amount']; } // add balances // get currencies: $currency = $this->primaryCurrency; // assume primary currency - if (null !== $accountMeta['currency']) { - $currency = $accountMeta['currency']; + if (null !== $meta['currency']) { + $currency = $meta['currency']; } // get the current balance: $date = $this->getDate(); - $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); + //$finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); + $finalBalance = $this->balances[$id]; Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance); // collect current balances: $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); - $openingBalance = Steam::bcround($accountMeta['opening_balance_amount'] ?? '0', $currency->decimal_places); + $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); $virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places); - $debtAmount = $accountMeta['current_debt'] ?? null; + $debtAmount = $meta['current_debt'] ?? null; // set some pc_ default values to NULL: $pcCurrentBalance = null; @@ -311,11 +314,11 @@ class AccountEnrichment implements EnrichmentInterface } // set opening balance(s) to NULL if the date is null - if (null === $accountMeta['opening_balance_date']) { + if (null === $meta['opening_balance_date']) { $openingBalance = null; $pcOpeningBalance = null; } - $accountMeta['balances'] = [ + $meta['balances'] = [ 'current_balance' => $currentBalance, 'pc_current_balance' => $pcCurrentBalance, 'opening_balance' => $openingBalance, @@ -326,16 +329,7 @@ class AccountEnrichment implements EnrichmentInterface 'pc_debt_amount' => $pcDebtAmount, ]; // end add balances - - - // if location, add location: - if (array_key_exists($item->id, $locations)) { - $accountMeta['location'] = $locations[$item->id]; - } - if (array_key_exists($item->id, $lastActivities)) { - $accountMeta['last_activity'] = $lastActivities[$item->id]; - } - $item->meta = $accountMeta; + $item->meta = $meta; return $item; }); @@ -343,10 +337,12 @@ class AccountEnrichment implements EnrichmentInterface private function collectLastActivities(): void { - $this->lastActivities = Steam::getLastActivities($this->accountIds); + $this->lastActivities = Steam::getLastActivities($this->ids); } - private function collectBalances(): void {} + private function collectBalances(): void { + $this->balances = Steam::finalAccountsBalanceOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary); + } public function setDate(?Carbon $date): void { diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 7ef6df2963..8cd41670a8 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -24,8 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Support; use Carbon\Carbon; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; +use FireflyIII\Models\AccountMeta; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; @@ -34,11 +36,9 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; -use Exception; use ValueError; - -use function Safe\preg_replace; use function Safe\parse_url; +use function Safe\preg_replace; /** * Class Steam. @@ -64,10 +64,10 @@ class Steam // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); if (str_contains($number, '.')) { if ('-' !== $number[0]) { - return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); + return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); } - return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); + return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); } return $number; @@ -203,7 +203,7 @@ class Steam Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); // set up cache - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty('final-balance-in-range'); $cache->addProperty($start); @@ -213,21 +213,21 @@ class Steam return $cache->get(); } - $balances = []; - $formatted = $start->format('Y-m-d'); + $balances = []; + $formatted = $start->format('Y-m-d'); /* * To make sure the start balance is correct, we need to get the balance at the exact end of the previous day. * Since we just did "startOfDay" we can do subDay()->endOfDay() to get the correct moment. * THAT will be the start balance. */ - $request = clone $start; + $request = clone $start; $request->subDay()->endOfDay(); Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String())); - $startBalance = $this->finalAccountBalance($account, $request); - $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); - $accountCurrency = $this->getAccountCurrency($account); - $hasCurrency = $accountCurrency instanceof TransactionCurrency; - $currency = $accountCurrency ?? $primaryCurrency; + $startBalance = $this->finalAccountBalance($account, $request); + $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); + $accountCurrency = $this->getAccountCurrency($account); + $hasCurrency = $accountCurrency instanceof TransactionCurrency; + $currency = $accountCurrency ?? $primaryCurrency; Log::debug(sprintf('Currency is %s', $currency->code)); @@ -240,7 +240,7 @@ class Steam Log::debug(sprintf('Also set start balance in %s', $primaryCurrency->code)); $startBalance[$primaryCurrency->code] ??= '0'; } - $currencies = [ + $currencies = [ $currency->id => $currency, $primaryCurrency->id => $primaryCurrency, ]; @@ -250,66 +250,65 @@ class Steam // sums up the balance changes per day. Log::debug(sprintf('Date >= %s and <= %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); - $set = $account->transactions() - ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) - ->groupBy('transaction_journals.date') - ->groupBy('transactions.transaction_currency_id') - ->orderBy('transaction_journals.date', 'ASC') - ->whereNull('transaction_journals.deleted_at') - ->get( - [ // @phpstan-ignore-line - 'transaction_journals.date', - 'transactions.transaction_currency_id', - DB::raw('SUM(transactions.amount) AS sum_of_day'), - ] - ) - ; + $set = $account->transactions() + ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) + ->groupBy('transaction_journals.date') + ->groupBy('transactions.transaction_currency_id') + ->orderBy('transaction_journals.date', 'ASC') + ->whereNull('transaction_journals.deleted_at') + ->get( + [ // @phpstan-ignore-line + 'transaction_journals.date', + 'transactions.transaction_currency_id', + DB::raw('SUM(transactions.amount) AS sum_of_day'), + ] + ); - $currentBalance = $startBalance; - $converter = new ExchangeRateConverter(); + $currentBalance = $startBalance; + $converter = new ExchangeRateConverter(); /** @var Transaction $entry */ foreach ($set as $entry) { // get date object - $carbon = new Carbon($entry->date, $entry->date_tz); - $carbonKey = $carbon->format('Y-m-d'); + $carbon = new Carbon($entry->date, $entry->date_tz); + $carbonKey = $carbon->format('Y-m-d'); // make sure sum is a string: - $sumOfDay = (string) ($entry->sum_of_day ?? '0'); + $sumOfDay = (string)($entry->sum_of_day ?? '0'); // #10426 make sure sum is not in scientific notation. - $sumOfDay = $this->floatalize($sumOfDay); + $sumOfDay = $this->floatalize($sumOfDay); // find currency of this entry, does not have to exist. $currencies[$entry->transaction_currency_id] ??= TransactionCurrency::find($entry->transaction_currency_id); // make sure this $entry has its own $entryCurrency /** @var TransactionCurrency $entryCurrency */ - $entryCurrency = $currencies[$entry->transaction_currency_id]; + $entryCurrency = $currencies[$entry->transaction_currency_id]; Log::debug(sprintf('Processing transaction(s) on moment %s', $carbon->format('Y-m-d H:i:s'))); // add amount to current balance in currency code. - $currentBalance[$entryCurrency->code] ??= '0'; - $currentBalance[$entryCurrency->code] = bcadd($sumOfDay, (string) $currentBalance[$entryCurrency->code]); + $currentBalance[$entryCurrency->code] ??= '0'; + $currentBalance[$entryCurrency->code] = bcadd($sumOfDay, (string)$currentBalance[$entryCurrency->code]); // if not requested to convert to primary currency, add the amount to "balance", do nothing else. if (!$convertToPrimary) { - $currentBalance['balance'] = bcadd((string) $currentBalance['balance'], $sumOfDay); + $currentBalance['balance'] = bcadd((string)$currentBalance['balance'], $sumOfDay); } // if convert to primary currency add the converted amount to "pc_balance". // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. if ($convertToPrimary) { $pcSumOfDay = $converter->convert($entryCurrency, $primaryCurrency, $carbon, $sumOfDay); - $currentBalance['pc_balance'] = bcadd((string) ($currentBalance['pc_balance'] ?? '0'), $pcSumOfDay); + $currentBalance['pc_balance'] = bcadd((string)($currentBalance['pc_balance'] ?? '0'), $pcSumOfDay); // if it's the same currency as the entry, also add to balance (see other code). if ($currency->id === $entryCurrency->id) { - $currentBalance['balance'] = bcadd((string) $currentBalance['balance'], $sumOfDay); + $currentBalance['balance'] = bcadd((string)$currentBalance['balance'], $sumOfDay); } } // add to final array. - $balances[$carbonKey] = $currentBalance; + $balances[$carbonKey] = $currentBalance; Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance); } $cache->store($balances); @@ -318,6 +317,70 @@ class Steam return $balances; } + public function finalAccountsBalanceOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array + { + $result = []; + $convertToPrimary = $convertToPrimary ?? Amount::convertToPrimary(); + $primary = $primary ?? Amount::getPrimaryCurrency(); + $currencies = $this->getCurrencies($accounts); + + // balance(s) in all currencies for ALL accounts. + $array = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) + ->get(['transactions.account_id', 'transaction_currencies.code', 'transactions.amount'])->toArray(); + + /** @var Account $account */ + foreach ($accounts as $account) { + // filter array back to this account: + $filtered = array_filter($array, function ($item) use ($account) { + return (int)$item['account_id'] === $account->id; + }); + $currency = $currencies[$account->id]; + // this array is PER account, so we wait a bit before we change code here. + $return = [ + 'pc_balance' => '0', + 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. + ]; + + // balance(s) in all currencies. + $others = $this->groupAndSumTransactions($filtered, 'code', 'amount'); + // Log::debug('All balances are (joined)', $others); + // if there is no request to convert, take this as "balance" and "pc_balance". + $return['balance'] = $others[$currency->code] ?? '0'; + if (!$convertToPrimary) { + unset($return['pc_balance']); + // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); + } + // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. + if ($convertToPrimary) { + $return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert. + // Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance'])); + } + + // either way, the balance is always combined with the virtual balance: + $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); + + if ($convertToPrimary) { + // the primary currency balance is combined with a converted virtual_balance: + $converter = new ExchangeRateConverter(); + $pcVirtualBalance = $converter->convert($currency, $primary, $date, $virtualBalance); + $return['pc_balance'] = bcadd($pcVirtualBalance, $return['pc_balance']); + // Log::debug(sprintf('Primary virtual balance makes the primary total %s', $return['pc_balance'])); + } + if (!$convertToPrimary) { + // if not, also increase the balance + primary balance for consistency. + $return['balance'] = bcadd($return['balance'], $virtualBalance); + // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); + } + $final = array_merge($return, $others); + $result[$account->id] = $final; + // Log::debug('Final balance is', $final); + } + return $result; + } + /** * Returns smaller than or equal to, so be careful with END OF DAY. * @@ -334,13 +397,13 @@ class Steam public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array { - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty($date); if ($cache->has()) { Log::debug(sprintf('CACHED finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); - return $cache->get(); + //return $cache->get(); } // Log::debug(sprintf('finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); if (null === $convertToPrimary) { @@ -350,7 +413,7 @@ class Steam $primary = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); } // account balance thing. - $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; + $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; if ($currencyPresent) { $accountCurrency = $account->meta['currency']; } @@ -358,20 +421,19 @@ class Steam $accountCurrency = $this->getAccountCurrency($account); } - $hasCurrency = null !== $accountCurrency; - $currency = $hasCurrency ? $accountCurrency : $primary; - $return = [ - 'pc_balance' => '0', - 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. + $hasCurrency = null !== $accountCurrency; + $currency = $hasCurrency ? $accountCurrency : $primary; + $return = [ + 'pc_balance' => '0', + 'balance' => '0', // this key is overwritten right away, but I must remember it is always created. ]; // balance(s) in all currencies. - $array = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') - ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) - ->get(['transaction_currencies.code', 'transactions.amount'])->toArray() - ; - $others = $this->groupAndSumTransactions($array, 'code', 'amount'); + $array = $account->transactions() + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') + ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) + ->get(['transaction_currencies.code', 'transactions.amount'])->toArray(); + $others = $this->groupAndSumTransactions($array, 'code', 'amount'); // Log::debug('All balances are (joined)', $others); // if there is no request to convert, take this as "balance" and "pc_balance". $return['balance'] = $others[$currency->code] ?? '0'; @@ -386,7 +448,7 @@ class Steam } // either way, the balance is always combined with the virtual balance: - $virtualBalance = (string) ('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance); + $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); if ($convertToPrimary) { // the primary currency balance is combined with a converted virtual_balance: @@ -400,7 +462,7 @@ class Steam $return['balance'] = bcadd($return['balance'], $virtualBalance); // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); } - $final = array_merge($return, $others); + $final = array_merge($return, $others); // Log::debug('Final balance is', $final); $cache->store($final); @@ -409,8 +471,8 @@ class Steam public function getAccountCurrency(Account $account): ?TransactionCurrency { - $type = $account->accountType->type; - $list = config('firefly.valid_currency_account_types'); + $type = $account->accountType->type; + $list = config('firefly.valid_currency_account_types'); // return null if not in this list. if (!in_array($type, $list, true)) { @@ -421,7 +483,7 @@ class Steam return null; } - return TransactionCurrency::find((int) $result->data); + return TransactionCurrency::find((int)$result->data); } private function groupAndSumTransactions(array $array, string $group, string $field): array @@ -430,7 +492,7 @@ class Steam foreach ($array as $item) { $groupKey = $item[$group] ?? 'unknown'; - $return[$groupKey] = bcadd($return[$groupKey] ?? '0', (string) $item[$field]); + $return[$groupKey] = bcadd($return[$groupKey] ?? '0', (string)$item[$field]); } return $return; @@ -445,9 +507,9 @@ class Steam if (null === $currency) { continue; } - $current = $converter->convert($currency, $primary, $date, $amount); + $current = $converter->convert($currency, $primary, $date, $amount); Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current)); - $total = bcadd($current, $total); + $total = bcadd($current, $total); } return $total; @@ -478,28 +540,28 @@ class Steam $hostName = $ipAddress; } - if ('' !== (string) $hostName && $hostName !== $ipAddress) { + if ('' !== (string)$hostName && $hostName !== $ipAddress) { $host = $hostName; } - return (string) $host; + return (string)$host; } public function getLastActivities(array $accounts): array { $list = []; - $set = auth()->user()->transactions() - ->whereIn('transactions.account_id', $accounts) - ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) - ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line + $set = auth()->user()->transactions() + ->whereIn('transactions.account_id', $accounts) + ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) + ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line ; /** @var Transaction $entry */ foreach ($set as $entry) { - $date = new Carbon($entry->max_date, config('app.timezone')); + $date = new Carbon($entry->max_date, config('app.timezone')); $date->setTimezone(config('app.timezone')); - $list[(int) $entry->account_id] = $date; + $list[(int)$entry->account_id] = $date; } return $list; @@ -517,7 +579,7 @@ class Steam if ('equal' === $locale) { $locale = $this->getLanguage(); } - $locale = (string) $locale; + $locale = (string)$locale; // Check for Windows to replace the locale correctly. if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) { @@ -572,9 +634,9 @@ class Steam public function getSafeUrl(string $unknownUrl, string $safeUrl): string { // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); - $returnUrl = $safeUrl; - $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); - $safeHost = parse_url($safeUrl, PHP_URL_HOST); + $returnUrl = $safeUrl; + $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); + $safeHost = parse_url($safeUrl, PHP_URL_HOST); if (null !== $unknownHost && $unknownHost === $safeHost) { $returnUrl = $unknownUrl; @@ -611,26 +673,26 @@ class Steam */ public function floatalize(string $value): string { - $value = strtoupper($value); + $value = strtoupper($value); if (!str_contains($value, 'E')) { return $value; } Log::debug(sprintf('Floatalizing %s', $value)); - $number = substr($value, 0, (int) strpos($value, 'E')); + $number = substr($value, 0, (int)strpos($value, 'E')); if (str_contains($number, '.')) { - $post = strlen(substr($number, (int) strpos($number, '.') + 1)); - $mantis = substr($value, (int) strpos($value, 'E') + 1); + $post = strlen(substr($number, (int)strpos($number, '.') + 1)); + $mantis = substr($value, (int)strpos($value, 'E') + 1); if ($mantis < 0) { - $post += abs((int) $mantis); + $post += abs((int)$mantis); } // TODO careless float could break financial math. - return number_format((float) $value, $post, '.', ''); + return number_format((float)$value, $post, '.', ''); } // TODO careless float could break financial math. - return number_format((float) $value, 0, '.', ''); + return number_format((float)$value, 0, '.', ''); } public function opposite(?string $amount = null): ?string @@ -650,24 +712,24 @@ class Steam // has a K in it, remove the K and multiply by 1024. $bytes = bcmul(rtrim($string, 'k'), '1024'); - return (int) $bytes; + return (int)$bytes; } if (false !== stripos($string, 'm')) { // has a M in it, remove the M and multiply by 1048576. $bytes = bcmul(rtrim($string, 'm'), '1048576'); - return (int) $bytes; + return (int)$bytes; } if (false !== stripos($string, 'g')) { // has a G in it, remove the G and multiply by (1024)^3. $bytes = bcmul(rtrim($string, 'g'), '1073741824'); - return (int) $bytes; + return (int)$bytes; } - return (int) $string; + return (int)$string; } public function positive(string $amount): string @@ -689,4 +751,42 @@ class Steam return $amount; } + + private function getCurrencies(Collection $accounts): array + { + $currencies = []; + $accountCurrencies = []; + $accountPreferences = []; + $primary = Amount::getPrimaryCurrency(); + + $ids = $accounts->pluck('id')->toArray(); + $result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get(); + /** @var AccountMeta $item */ + foreach ($result as $item) { + $accountPreferences[(int)$item->account_id] = (int)$item->data; + } + // collect those currencies. + $set = TransactionCurrency::whereIn('id', $accountPreferences)->get(); + foreach ($set as $item) { + $currencies[$item->id] = $item; + } + + /** @var Account $account */ + foreach ($accounts as $account) { + $accountId = $account->id; + $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; + if ($currencyPresent) { + $currencyId = $account->meta['currency']->id; + $currencies[$currencyId] ??= $account->meta['currency']; + $accountCurrencies[$accountId] = $account->meta['currency']; + } + if (!$currencyPresent && !array_key_exists($account->id, $accountPreferences)) { + $accountCurrencies[$accountId] = $primary; + } + if (!$currencyPresent && array_key_exists($account->id, $accountPreferences)) { + $accountCurrencies[$account->id] = $currencies[$accountPreferences[$account->id]]; + } + } + return $accountCurrencies; + } } diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index c02879e4c9..6b4b64d309 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -143,7 +143,7 @@ class AccountTransformer extends AbstractTransformer 'notes' => $account->meta['notes'] ?? null, 'monthly_payment_date' => $monthlyPaymentDate, 'credit_card_type' => $creditCardType, - 'account_number' => $account->meta['account_number'] ?? null, + 'account_number' => $account->meta['account_number'], 'iban' => '' === $account->iban ? null : $account->iban, 'bic' => $account->meta['BIC'] ?? null, 'opening_balance_date' => $openingBalanceDate, @@ -155,7 +155,7 @@ class AccountTransformer extends AbstractTransformer 'longitude' => $longitude, 'latitude' => $latitude, 'zoom_level' => $zoomLevel, - 'last_activity' => array_key_exists('last_activity', $account->meta) ? $account->meta['last_activity']->toAtomString() : null, + 'last_activity' => $account->meta['last_activity']?->toAtomString(), 'links' => [ [ 'rel' => 'self', diff --git a/resources/views/test/api-test.twig b/resources/views/test/api-test.twig new file mode 100644 index 0000000000..e8b74587b3 --- /dev/null +++ b/resources/views/test/api-test.twig @@ -0,0 +1,33 @@ + + + Firefly III API test + + + + + + +

+ Hi there, +

+

+ This page is created to do some basic API testing. It's not very exciting, is it? +

+ + + + diff --git a/resources/views/test/test.twig b/resources/views/test/test.twig deleted file mode 100644 index 6befc1f428..0000000000 --- a/resources/views/test/test.twig +++ /dev/null @@ -1,2 +0,0 @@ -list-length: {{ listLength }} -user-email: {{ Auth.user.email }} diff --git a/routes/web.php b/routes/web.php index 4a7b4362fa..cc59c95ca8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -120,6 +120,7 @@ Route::group( Route::get('flush', ['uses' => 'DebugController@flush', 'as' => 'flush']); Route::get('routes', ['uses' => 'DebugController@routes', 'as' => 'routes']); Route::get('debug', 'DebugController@index')->name('debug'); + Route::get('debug/api-test', 'DebugController@apiTest')->name('api-test'); } );