A lot less queries for the account transformer.

This commit is contained in:
James Cole
2025-02-15 16:51:13 +01:00
parent f755dd2d48
commit 90fc4b44f2
11 changed files with 321 additions and 154 deletions

View File

@@ -29,7 +29,9 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
@@ -88,9 +90,17 @@ class ShowController extends Controller
$count = $collection->count(); $count = $collection->count();
// continue sort: // continue sort:
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$accounts = $enrichment->enrich($accounts);
// make paginator: // make paginator:
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.accounts.index').$this->buildParams()); $paginator->setPath(route('api.v1.accounts.index').$this->buildParams());
@@ -118,6 +128,16 @@ class ShowController extends Controller
$account->refresh(); $account->refresh();
$manager = $this->getManager(); $manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$account = $enrichment->enrichSingle($account);
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -27,7 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Models\Account;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Account\StoreRequest; use FireflyIII\Api\V1\Requests\Models\Account\StoreRequest;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
@@ -69,6 +71,15 @@ class StoreController extends Controller
$account = $this->repository->store($data); $account = $this->repository->store($data);
$manager = $this->getManager(); $manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$account = $enrichment->enrichSingle($account);
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Account\UpdateRequest; use FireflyIII\Api\V1\Requests\Models\Account\UpdateRequest;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
@@ -73,6 +75,15 @@ class UpdateController extends Controller
$account->refresh(); $account->refresh();
app('preferences')->mark(); app('preferences')->mark();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$account = $enrichment->enrichSingle($account);
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -28,9 +28,11 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\PiggyBankEventTransformer; use FireflyIII\Transformers\PiggyBankEventTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
@@ -75,17 +77,26 @@ class ListController extends Controller
$collection = $piggyBank->accounts; $collection = $piggyBank->accounts;
$count = $collection->count(); $count = $collection->count();
$events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$accounts = $enrichment->enrich($accounts);
// make paginator: // make paginator:
$paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.piggy-banks.accounts', [$piggyBank->id]).$this->buildParams()); $paginator->setPath(route('api.v1.piggy-banks.accounts', [$piggyBank->id]).$this->buildParams());
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($events, $transformer, 'accounts'); $resource = new FractalCollection($accounts, $transformer, 'accounts');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);

View File

@@ -42,6 +42,7 @@ use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\AvailableBudgetTransformer; use FireflyIII\Transformers\AvailableBudgetTransformer;
@@ -101,6 +102,15 @@ class ListController extends Controller
$count = $collection->count(); $count = $collection->count();
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$accounts = $enrichment->enrich($accounts);
// make paginator: // make paginator:
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]).$this->buildParams()); $paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]).$this->buildParams());

View File

@@ -26,8 +26,10 @@ namespace FireflyIII\Api\V1\Controllers\Search;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\Search\AccountSearch; use FireflyIII\Support\Search\AccountSearch;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@@ -81,6 +83,15 @@ class AccountController extends Controller
$accounts = $search->search(); $accounts = $search->search();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToNative($this->convertToNative);
$enrichment->setNative($this->nativeCurrency);
$accounts = $enrichment->enrich($accounts);
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -25,14 +25,20 @@ declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments; namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Location;
use FireflyIII\Models\Note;
use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Balance; use FireflyIII\Support\Facades\Balance;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -67,15 +73,21 @@ class AccountEnrichment implements EnrichmentInterface
private array $accountTypes; private array $accountTypes;
private array $currencies; private array $currencies;
private array $meta; private array $meta;
private array $openingBalances;
private array $notes;
private array $locations;
public function __construct() public function __construct()
{ {
$this->convertToNative = false; $this->convertToNative = false;
$this->accountIds = []; $this->accountIds = [];
$this->openingBalances = [];
$this->currencies = []; $this->currencies = [];
$this->accountTypeIds = []; $this->accountTypeIds = [];
$this->accountTypes = []; $this->accountTypes = [];
$this->meta = []; $this->meta = [];
$this->notes = [];
$this->locations = [];
// $this->repository = app(AccountRepositoryInterface::class); // $this->repository = app(AccountRepositoryInterface::class);
// $this->currencyRepository = app(CurrencyRepositoryInterface::class); // $this->currencyRepository = app(CurrencyRepositoryInterface::class);
// $this->start = null; // $this->start = null;
@@ -105,6 +117,8 @@ class AccountEnrichment implements EnrichmentInterface
$this->collectAccountIds(); $this->collectAccountIds();
$this->getAccountTypes(); $this->getAccountTypes();
$this->collectMetaData(); $this->collectMetaData();
$this->collectLocations();
$this->collectOpeningBalances();
// $this->default = app('amount')->getNativeCurrency(); // $this->default = app('amount')->getNativeCurrency();
// $this->currencies = []; // $this->currencies = [];
// $this->balances = []; // $this->balances = [];
@@ -156,23 +170,90 @@ class AccountEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$accountTypes = $this->accountTypes; $accountTypes = $this->accountTypes;
$meta = $this->meta; $meta = $this->meta;
$this->collection = $this->collection->map(function (Account $item) use ($accountTypes, $meta) { $currencies = $this->currencies;
$notes = $this->notes;
$openingBalances = $this->openingBalances;
$locations = $this->locations;
$this->collection = $this->collection->map(function (Account $item) use ($accountTypes, $meta, $currencies, $notes, $openingBalances, $locations) {
$item->full_account_type = $accountTypes[(int) $item->account_type_id] ?? null; $item->full_account_type = $accountTypes[(int) $item->account_type_id] ?? null;
$meta = []; $accountMeta = [
'currency' => null,
'location' => [
'latitude' => null,
'longitude' => null,
'zoom_level' => null,
],
];
if (array_key_exists((int) $item->id, $meta)) { if (array_key_exists((int) $item->id, $meta)) {
foreach ($meta[(int) $item->id] as $name => $value) { foreach ($meta[(int) $item->id] as $name => $value) {
$meta[$name] = $value; $accountMeta[$name] = $value;
} }
} }
$item->meta = $meta; // also add currency, if present.
if (array_key_exists('currency_id', $accountMeta)) {
$currencyId = (int) $accountMeta['currency_id'];
$accountMeta['currency'] = $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 location, add location:
if (array_key_exists($item->id, $locations)) {
$accountMeta['location'] = $locations[$item->id];
}
$item->meta = $accountMeta;
return $item; return $item;
}); });
} }
private function collectOpeningBalances(): void
{
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setAccounts($this->collection)
->withAccountInformation()
->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]);
$journals = $collector->getExtractedJournals();
foreach ($journals as $journal) {
$this->openingBalances[(int) $journal['source_account_id']]
= [
'amount' => Steam::negative($journal['amount']),
'date' => $journal['date'],
];
$this->openingBalances[(int) $journal['destination_account_id']]
= [
'amount' => Steam::positive($journal['amount']),
'date' => $journal['date'],
];
}
}
private function collectLocations(): void {
$locations = Location::query()->whereIn('locatable_id', $this->accountIds)
->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray();
foreach ($locations as $location) {
$this->locations[(int) $location['locatable_id']]
= [
'latitude' => (float) $location['latitude'],
'longitude' => (float) $location['longitude'],
'zoom_level' => (int) $location['zoom_level'],
];
}
Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations)));
}
/** /**
* TODO this method refers to a single-use method inside Steam that could be moved here. * TODO this method refers to a single-use method inside Steam that could be moved here.
*/ */
@@ -386,5 +467,17 @@ class AccountEnrichment implements EnrichmentInterface
$this->native = $native; $this->native = $native;
} }
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->accountIds)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray();
foreach ($notes as $note) {
$this->notes[(int) $note['noteable_id']] = (string) $note['text'];
}
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
}
} }

View File

@@ -24,6 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments; namespace FireflyIII\Support\JsonApi\Enrichments;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -32,4 +34,11 @@ interface EnrichmentInterface
public function enrich(Collection $collection): Collection; public function enrich(Collection $collection): Collection;
public function enrichSingle(Model|array $model): Model|array; public function enrichSingle(Model|array $model): Model|array;
public function setUserGroup(UserGroup $userGroup): void;
public function setUser(User $user): void;
} }

View File

@@ -29,6 +29,7 @@ use FireflyIII\Models\Attachment;
use FireflyIII\Models\Location; use FireflyIII\Models\Location;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
@@ -80,7 +81,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
#[\Override] public function enrichSingle(Model|array $model): Model|array #[\Override] public function enrichSingle(Model|array $model): TransactionGroup|array
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
if(is_array($model)) { if(is_array($model)) {

View File

@@ -48,7 +48,7 @@ class Steam
if (!in_array($type, $list, true)) { if (!in_array($type, $list, true)) {
return null; return null;
} }
$result = $account->accountMeta->where('name', 'currency_id')->first(); $result = $account->accountMeta()->where('name', 'currency_id')->first();
if (null === $result) { if (null === $result) {
return null; return null;
} }
@@ -125,8 +125,7 @@ class Steam
'transactions.transaction_currency_id', 'transactions.transaction_currency_id',
DB::raw('SUM(transactions.amount) AS sum_of_day'), DB::raw('SUM(transactions.amount) AS sum_of_day'),
] ]
) );
;
$currentBalance = $startBalance; $currentBalance = $startBalance;
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
@@ -207,10 +206,10 @@ class Steam
// Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
if (str_contains($number, '.')) { if (str_contains($number, '.')) {
if ('-' !== $number[0]) { 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; return $number;
@@ -288,21 +287,32 @@ class Steam
* --> "native_balance": balance in the user's native balance, with all amounts converted to native. * --> "native_balance": balance in the user's native balance, with all amounts converted to native.
* "EUR": balance in EUR (or whatever currencies the account has balance in) * "EUR": balance in EUR (or whatever currencies the account has balance in)
*/ */
public function finalAccountBalance(Account $account, Carbon $date): array public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $native = null, ?bool $convertToNative = null): array
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($date); $cache->addProperty($date);
if ($cache->has()) { if ($cache->has()) {
// Log::debug(sprintf('CACHED finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s'))); 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'))); Log::debug(sprintf('finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s')));
if (null === $convertToNative) {
$native = Amount::getNativeCurrencyByUserGroup($account->user->userGroup);
$convertToNative = Amount::convertToNative($account->user); $convertToNative = Amount::convertToNative($account->user);
}
if (null === $native) {
$native = Amount::getNativeCurrencyByUserGroup($account->user->userGroup);
}
// account balance thing.
$currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && $account->meta['currency'] !== null;
if($currencyPresent) {
$accountCurrency = $account->meta['currency'];
}
if(!$currencyPresent) {
$accountCurrency = $this->getAccountCurrency($account); $accountCurrency = $this->getAccountCurrency($account);
}
$hasCurrency = null !== $accountCurrency; $hasCurrency = null !== $accountCurrency;
$currency = $hasCurrency ? $accountCurrency : $native; $currency = $hasCurrency ? $accountCurrency : $native;
$return = [ $return = [
@@ -314,8 +324,7 @@ class Steam
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transaction_currencies.code', 'transactions.amount'])->toArray() ->get(['transaction_currencies.code', 'transactions.amount'])->toArray();
;
$others = $this->groupAndSumTransactions($array, 'code', 'amount'); $others = $this->groupAndSumTransactions($array, 'code', 'amount');
// Log::debug('All balances are (joined)', $others); // Log::debug('All balances are (joined)', $others);
// if there is no request to convert, take this as "balance" and "native_balance". // if there is no request to convert, take this as "balance" and "native_balance".

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\ParameterBag;
@@ -61,26 +62,21 @@ class AccountTransformer extends AbstractTransformer
*/ */
public function transform(Account $account): array public function transform(Account $account): array
{ {
$this->repository->setUser($account->user);
// get account type: // get account type:
$accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type));
$fullType = $account->accountType->type; $liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type));
$accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $fullType));
$liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $fullType));
$liabilityType = '' === $liabilityType ? null : strtolower($liabilityType); $liabilityType = '' === $liabilityType ? null : strtolower($liabilityType);
$liabilityDirection = $this->repository->getMetaValue($account, 'liability_direction');
$convertToNative = Amount::convertToNative();
$liabilityDirection = $account->meta['liability_direction'] ?? null;
// get account role (will only work if the type is asset). // get account role (will only work if the type is asset).
$native = Amount::getNativeCurrency();
$accountRole = $this->getAccountRole($account, $accountType); $accountRole = $this->getAccountRole($account, $accountType);
// date (for balance etc.)
$date = $this->getDate(); $date = $this->getDate();
$date->endOfDay(); $date->endOfDay();
[$currencyId, $currencyCode, $currencySymbol, $decimalPlaces] = $this->getCurrency($account);
[$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType); [$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType);
[$openingBalance, $nativeOpeningBalance, $openingBalanceDate] = $this->getOpeningBalance($account, $accountType, $convertToNative); [$openingBalance, $nativeOpeningBalance, $openingBalanceDate] = $this->getOpeningBalance($account, $accountType);
[$interest, $interestPeriod] = $this->getInterest($account, $accountType); [$interest, $interestPeriod] = $this->getInterest($account, $accountType);
$native = $this->native; $native = $this->native;
@@ -88,18 +84,14 @@ class AccountTransformer extends AbstractTransformer
// reset native currency to NULL, not interesting. // reset native currency to NULL, not interesting.
$native = null; $native = null;
} }
//
$openingBalance = app('steam')->bcround($openingBalance, $decimalPlaces); $decimalPlaces = (int) $account->meta['currency']?->decimal_places;
$includeNetWorth = '0' !== $this->repository->getMetaValue($account, 'include_net_worth'); $decimalPlaces = 0 === $decimalPlaces ? 2 : $decimalPlaces;
$longitude = null; $openingBalance = Steam::bcround($openingBalance, $decimalPlaces);
$latitude = null; $includeNetWorth = '0' !== ($account->meta['include_net_worth'] ?? null);
$zoomLevel = null; $longitude = $account->meta['location']['longitude'] ?? null;
$location = $this->repository->getLocation($account); $latitude = $account->meta['location']['latitude'] ?? null;
if (null !== $location) { $zoomLevel = $account->meta['location']['zoom_level'] ?? null;
$longitude = $location->longitude;
$latitude = $location->latitude;
$zoomLevel = (int) $location->zoom_level;
}
// no order for some accounts: // no order for some accounts:
$order = $account->order; $order = $account->order;
@@ -108,13 +100,13 @@ class AccountTransformer extends AbstractTransformer
} }
// balance, native balance, virtual balance, native virtual balance? // balance, native balance, virtual balance, native virtual balance?
Log::debug(sprintf('transform: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); Log::debug(sprintf('transform: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
$finalBalance = Steam::finalAccountBalance($account, $date); $finalBalance = Steam::finalAccountBalance($account, $date, $this->native, $this->convertToNative);
if ($convertToNative) { if ($this->convertToNative) {
$finalBalance['balance'] = $finalBalance[$currencyCode] ?? '0'; $finalBalance['balance'] = $finalBalance[$account->meta['currency']?->code] ?? '0';
} }
$currentBalance = app('steam')->bcround($finalBalance['balance'] ?? '0', $decimalPlaces); $currentBalance = Steam::bcround($finalBalance['balance'] ?? '0', $decimalPlaces);
$nativeCurrentBalance = $convertToNative ? app('steam')->bcround($finalBalance['native_balance'] ?? '0', $native->decimal_places) : null; $nativeCurrentBalance = $this->convertToNative ? Steam::bcround($finalBalance['native_balance'] ?? '0', $native->decimal_places) : null;
return [ return [
'id' => (string) $account->id, 'id' => (string) $account->id,
@@ -125,10 +117,10 @@ class AccountTransformer extends AbstractTransformer
'name' => $account->name, 'name' => $account->name,
'type' => strtolower($accountType), 'type' => strtolower($accountType),
'account_role' => $accountRole, 'account_role' => $accountRole,
'currency_id' => $currencyId, 'currency_id' => $account->meta['currency_id'] ?? null,
'currency_code' => $currencyCode, 'currency_code' => $account->meta['currency']?->code,
'currency_symbol' => $currencySymbol, 'currency_symbol' => $account->meta['currency']?->symbol,
'currency_decimal_places' => $decimalPlaces, 'currency_decimal_places' => $account->meta['currency']?->decimal_places,
'native_currency_id' => null === $native ? null : (string) $native->id, 'native_currency_id' => null === $native ? null : (string) $native->id,
'native_currency_code' => $native?->code, 'native_currency_code' => $native?->code,
'native_currency_symbol' => $native?->symbol, 'native_currency_symbol' => $native?->symbol,
@@ -136,14 +128,14 @@ class AccountTransformer extends AbstractTransformer
'current_balance' => $currentBalance, 'current_balance' => $currentBalance,
'native_current_balance' => $nativeCurrentBalance, 'native_current_balance' => $nativeCurrentBalance,
'current_balance_date' => $date->toAtomString(), 'current_balance_date' => $date->toAtomString(),
'notes' => $this->repository->getNoteText($account), 'notes' => $account->meta['notes'] ?? null,
'monthly_payment_date' => $monthlyPaymentDate, 'monthly_payment_date' => $monthlyPaymentDate,
'credit_card_type' => $creditCardType, 'credit_card_type' => $creditCardType,
'account_number' => $this->repository->getMetaValue($account, 'account_number'), 'account_number' => $account->meta['account_number'] ?? null,
'iban' => '' === $account->iban ? null : $account->iban, 'iban' => '' === $account->iban ? null : $account->iban,
'bic' => $this->repository->getMetaValue($account, 'BIC'), 'bic' => $account->meta['BIC'] ?? null,
'virtual_balance' => app('steam')->bcround($account->virtual_balance, $decimalPlaces), 'virtual_balance' => Steam::bcround($account->virtual_balance, $decimalPlaces),
'native_virtual_balance' => $this->convertToNative ? app('steam')->bcround($account->native_virtual_balance, $native->decimal_places) : null, 'native_virtual_balance' => $this->convertToNative ? Steam::bcround($account->native_virtual_balance, $native->decimal_places) : null,
'opening_balance' => $openingBalance, 'opening_balance' => $openingBalance,
'native_opening_balance' => $nativeOpeningBalance, 'native_opening_balance' => $nativeOpeningBalance,
'opening_balance_date' => $openingBalanceDate, 'opening_balance_date' => $openingBalanceDate,
@@ -151,7 +143,7 @@ class AccountTransformer extends AbstractTransformer
'liability_direction' => $liabilityDirection, 'liability_direction' => $liabilityDirection,
'interest' => $interest, 'interest' => $interest,
'interest_period' => $interestPeriod, 'interest_period' => $interestPeriod,
'current_debt' => $this->repository->getMetaValue($account, 'current_debt'), 'current_debt' => $account->meta['current_debt'] ?? null,
'include_net_worth' => $includeNetWorth, 'include_net_worth' => $includeNetWorth,
'longitude' => $longitude, 'longitude' => $longitude,
'latitude' => $latitude, 'latitude' => $latitude,
@@ -159,7 +151,7 @@ class AccountTransformer extends AbstractTransformer
'links' => [ 'links' => [
[ [
'rel' => 'self', 'rel' => 'self',
'uri' => '/accounts/'.$account->id, 'uri' => sprintf('/accounts/%d', $account->id),
], ],
], ],
]; ];
@@ -167,7 +159,7 @@ class AccountTransformer extends AbstractTransformer
private function getAccountRole(Account $account, string $accountType): ?string private function getAccountRole(Account $account, string $accountType): ?string
{ {
$accountRole = $this->repository->getMetaValue($account, 'account_role'); $accountRole = $account->meta['account_role'] ?? null;
if ('asset' !== $accountType || '' === (string) $accountRole) { if ('asset' !== $accountType || '' === (string) $accountRole) {
$accountRole = null; $accountRole = null;
} }
@@ -188,29 +180,13 @@ class AccountTransformer extends AbstractTransformer
return $date; return $date;
} }
private function getCurrency(Account $account): array
{
$currency = $this->repository->getAccountCurrency($account);
// only grab native when result is null:
if (null === $currency) {
$currency = $this->native;
}
$currencyId = (string) $currency->id;
$currencyCode = $currency->code;
$decimalPlaces = $currency->decimal_places;
$currencySymbol = $currency->symbol;
return [$currencyId, $currencyCode, $currencySymbol, $decimalPlaces];
}
private function getCCInfo(Account $account, ?string $accountRole, string $accountType): array private function getCCInfo(Account $account, ?string $accountRole, string $accountType): array
{ {
$monthlyPaymentDate = null; $monthlyPaymentDate = null;
$creditCardType = null; $creditCardType = null;
if ('ccAsset' === $accountRole && 'asset' === $accountType) { if ('ccAsset' === $accountRole && 'asset' === $accountType) {
$creditCardType = $this->repository->getMetaValue($account, 'cc_type'); $creditCardType = $account->meta['cc_type'] ?? null;
$monthlyPaymentDate = $this->repository->getMetaValue($account, 'cc_monthly_payment_date'); $monthlyPaymentDate = $account->meta['cc_monthly_payment_date'] ?? null;
} }
if (null !== $monthlyPaymentDate) { if (null !== $monthlyPaymentDate) {
// try classic date: // try classic date:
@@ -229,18 +205,16 @@ class AccountTransformer extends AbstractTransformer
return [$creditCardType, $monthlyPaymentDate]; return [$creditCardType, $monthlyPaymentDate];
} }
/** private function getOpeningBalance(Account $account, string $accountType): array
* TODO refactor call to get~OpeningBalanceAmount / Date because it is a lot of queries
*/
private function getOpeningBalance(Account $account, string $accountType, bool $convertToNative): array
{ {
$openingBalance = null; $openingBalance = null;
$openingBalanceDate = null; $openingBalanceDate = null;
$nativeOpeningBalance = null; $nativeOpeningBalance = null;
if (in_array($accountType, ['asset', 'liabilities'], true)) { if (in_array($accountType, ['asset', 'liabilities'], true)) {
$openingBalance = $this->repository->getOpeningBalanceAmount($account, false); // grab from meta.
$nativeOpeningBalance = $this->repository->getOpeningBalanceAmount($account, true); $openingBalance = $account->meta['opening_balance_amount'] ?? null;
$openingBalanceDate = $this->repository->getOpeningBalanceDate($account); $nativeOpeningBalance = null;
$openingBalanceDate = $account->meta['opening_balance_date'] ?? null;
} }
if (null !== $openingBalanceDate) { if (null !== $openingBalanceDate) {
$object = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone')); $object = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone'));
@@ -248,6 +222,13 @@ class AccountTransformer extends AbstractTransformer
$object = today(config('app.timezone')); $object = today(config('app.timezone'));
} }
$openingBalanceDate = $object->toAtomString(); $openingBalanceDate = $object->toAtomString();
// NOW do conversion.
if ($this->convertToNative && null !== $account->meta['currency']) {
$converter = new ExchangeRateConverter();
$nativeOpeningBalance = $converter->convert($account->meta['currency'], $this->native, $object, $openingBalance);
}
} }
return [$openingBalance, $nativeOpeningBalance, $openingBalanceDate]; return [$openingBalance, $nativeOpeningBalance, $openingBalanceDate];
@@ -258,8 +239,8 @@ class AccountTransformer extends AbstractTransformer
$interest = null; $interest = null;
$interestPeriod = null; $interestPeriod = null;
if ('liabilities' === $accountType) { if ('liabilities' === $accountType) {
$interest = $this->repository->getMetaValue($account, 'interest'); $interest = $account->meta['interest'] ?? null;
$interestPeriod = $this->repository->getMetaValue($account, 'interest_period'); $interestPeriod = $account->meta['interest_period'] ?? null;
} }
return [$interest, $interestPeriod]; return [$interest, $interestPeriod];