diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 74a71fd4c2..89217caf55 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Summary; use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\DateRequest; +use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Report\NetWorthInterface; @@ -38,6 +39,7 @@ use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; @@ -102,10 +104,10 @@ class BasicController extends Controller $billData = $this->getBillInformation($start, $end); $spentData = $this->getLeftToSpendInfo($start, $end); $netWorthData = $this->getNetWorthInfo($start, $end); - $balanceData = []; - $billData = []; -// $spentData = []; - $netWorthData = []; +// $balanceData = []; +// $billData = []; +// $spentData = []; +// $netWorthData = []; $total = array_merge($balanceData, $billData, $spentData, $netWorthData); // give new keys @@ -121,6 +123,9 @@ class BasicController extends Controller private function getBalanceInformation(Carbon $start, Carbon $end): array { + // some config settings + $convertToNative = app('preferences')->get('convert_to_native', false)->data; + $default = app('amount')->getDefaultCurrency(); // prep some arrays: $incomes = []; $expenses = []; @@ -134,16 +139,17 @@ class BasicController extends Controller $set = $collector->getExtractedJournals(); - /** @var array $transactionJournal */ - foreach ($set as $transactionJournal) { - $currencyId = (int) $transactionJournal['currency_id']; + /** @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($transactionJournal['amount'], '-1') + bcmul($amount, '-1') ); $sums[$currencyId] ??= '0'; - $sums[$currencyId] = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1')); + $sums[$currencyId] = bcadd($sums[$currencyId], bcmul($amount, '-1')); } // collect expenses of user using the new group collector. @@ -152,13 +158,14 @@ class BasicController extends Controller $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $set = $collector->getExtractedJournals(); - /** @var array $transactionJournal */ - foreach ($set as $transactionJournal) { - $currencyId = (int) $transactionJournal['currency_id']; + /** @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], $transactionJournal['amount']); + $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); $sums[$currencyId] ??= '0'; - $sums[$currencyId] = bcadd($sums[$currencyId], $transactionJournal['amount']); + $sums[$currencyId] = bcadd($sums[$currencyId], $amount); } // format amounts: @@ -317,7 +324,7 @@ class BasicController extends Controller { /** @var User $user */ $user = auth()->user(); - $date = today(config('app.timezone'))->startOfDay(); + $date = now(config('app.timezone')); // start and end in the future? use $end if ($this->notInDateRange($date, $start, $end)) { /** @var Carbon $date */ @@ -327,9 +334,7 @@ class BasicController extends Controller /** @var NetWorthInterface $netWorthHelper */ $netWorthHelper = app(NetWorthInterface::class); $netWorthHelper->setUser($user); - $allAccounts = $this->accountRepository->getActiveAccountsByType( - [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT] - ); + $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( diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index dbffefd0e3..1a8a9d5a9f 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -33,7 +33,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; -use FireflyIII\Support\Http\Api\ExchangeRateConverter; +use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; @@ -65,87 +66,58 @@ class NetWorth implements NetWorthInterface public function byAccounts(Collection $accounts, Carbon $date): array { // start in the past, end in the future? use $date - $ids = implode(',', $accounts->pluck('id')->toArray()); - $cache = new CacheProperties(); + $convertToNative = app('preferences')->get('convert_to_native', false)->data; + $ids = implode(',', $accounts->pluck('id')->toArray()); + $cache = new CacheProperties(); $cache->addProperty($date); + $cache->addProperty($convertToNative); $cache->addProperty('net-worth-by-accounts'); $cache->addProperty($ids); if ($cache->has()) { - return $cache->get(); + // return $cache->get(); } - app('log')->debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d'))); - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $default = app('amount')->getDefaultCurrency(); - $converter = new ExchangeRateConverter(); - - // default "native" currency has everything twice, for consistency. - $netWorth = [ - 'native' => [ - 'balance' => '0', - 'native_balance' => '0', - 'currency_id' => $default->id, - 'currency_code' => $default->code, - 'currency_name' => $default->name, - 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => $default->decimal_places, - 'native_currency_id' => $default->id, - 'native_currency_code' => $default->code, - 'native_currency_name' => $default->name, - 'native_currency_symbol' => $default->symbol, - 'native_currency_decimal_places' => $default->decimal_places, - ], - ]; - $balances = app('steam')->finalAccountsBalance($accounts, $date); + Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s'))); + $default = Amount::getDefaultCurrency(); + $netWorth = []; + $balances = Steam::finalAccountsBalance($accounts, $date); /** @var Account $account */ foreach ($accounts as $account) { - app('log')->debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name)); - $currency = $this->getRepository()->getAccountCurrency($account); - if (null === $currency) { - $currency = app('amount')->getDefaultCurrency(); - } - $currencyCode = $currency->code; - $balance = '0'; - $nativeBalance = '0'; + Log::debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name)); + $currency = $this->getRepository()->getAccountCurrency($account) ?? $default; + $useNative = $convertToNative && $default->id !== $currency->id; + $currency = $useNative ? $default : $currency; + $currencyCode = $currency->code; + $balance = '0'; + $nativeBalance = '0'; if (array_key_exists($account->id, $balances)) { $balance = $balances[$account->id]['balance'] ?? '0'; $nativeBalance = $balances[$account->id]['native_balance'] ?? '0'; } - app('log')->debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance)); - // always subtract virtual balance - $virtualBalance = $account->virtual_balance; - if ('' !== $virtualBalance) { - $balance = bcsub($balance, $virtualBalance); - $nativeVirtualBalance = $converter->convert($default, $currency, $account->created_at, $virtualBalance); - $nativeBalance = bcsub($nativeBalance, $nativeVirtualBalance); - } + Log::debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance)); + // always subtract virtual balance again. + $balance = '' !== (string) $account->virtual_balance ? bcsub($balance, $account->virtual_balance) : $balance; + $nativeBalance = '' !== (string) $account->native_virtual_balance ? bcsub($nativeBalance, $account->native_virtual_balance) : $nativeBalance; + $amountToUse = $useNative ? $nativeBalance : $balance; + Log::debug(sprintf('Will use %s %s', $currencyCode, $amountToUse)); + $netWorth[$currencyCode] ??= [ - 'balance' => '0', - 'native_balance' => '0', - 'currency_id' => (string) $currency->id, - 'currency_code' => $currency->code, - 'currency_name' => $currency->name, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'native_currency_id' => (string) $default->id, - 'native_currency_code' => $default->code, - 'native_currency_name' => $default->name, - 'native_currency_symbol' => $default->symbol, - 'native_currency_decimal_places' => $default->decimal_places, + 'balance' => '0', + 'currency_id' => (string) $currency->id, + 'currency_code' => $currency->code, + 'currency_name' => $currency->name, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, ]; - $netWorth[$currencyCode]['balance'] = bcadd($balance, $netWorth[$currencyCode]['balance']); - $netWorth[$currencyCode]['native_balance'] = bcadd($nativeBalance, $netWorth[$currencyCode]['native_balance']); - $netWorth['native']['balance'] = bcadd($nativeBalance, $netWorth['native']['balance']); - $netWorth['native']['native_balance'] = bcadd($nativeBalance, $netWorth['native']['native_balance']); + $netWorth[$currencyCode]['balance'] = bcadd($amountToUse, $netWorth[$currencyCode]['balance']); } $cache->store($netWorth); - $converter->summarize(); return $netWorth; } - private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface + private function getRepository(): AccountRepositoryInterface | AdminAccountRepositoryInterface { if (null === $this->userGroup) { return $this->accountRepository; @@ -154,19 +126,19 @@ class NetWorth implements NetWorthInterface return $this->adminAccountRepository; } - public function setUser(null|Authenticatable|User $user): void + public function setUser(null | Authenticatable | User $user): void { if (!$user instanceof User) { return; } - $this->user = $user; - $this->userGroup = null; + $this->user = $user; + $this->userGroup = null; // make repository: $this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository->setUser($this->user); - $this->currencyRepos = app(CurrencyRepositoryInterface::class); + $this->currencyRepos = app(CurrencyRepositoryInterface::class); $this->currencyRepos->setUser($this->user); } @@ -187,18 +159,18 @@ class NetWorth implements NetWorthInterface */ $accounts = $this->getAccounts(); $return = []; - $balances = app('steam')->finalAccountsBalance($accounts, $date); + $balances = Steam::finalAccountsBalance($accounts, $date); foreach ($accounts as $account) { - $currency = $this->getRepository()->getAccountCurrency($account); - $balance = $balances[$account->id]['balance'] ?? '0'; + $currency = $this->getRepository()->getAccountCurrency($account); + $balance = $balances[$account->id]['balance'] ?? '0'; // always subtract virtual balance. - $virtualBalance = $account->virtual_balance; + $virtualBalance = $account->virtual_balance; if ('' !== $virtualBalance) { $balance = bcsub($balance, $virtualBalance); } - $return[$currency->id] ??= [ + $return[$currency->id] ??= [ 'id' => (string) $currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index 154160a8bc..58f91c3060 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -29,13 +29,11 @@ use FireflyIII\Helpers\Report\NetWorthInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; -use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; @@ -48,6 +46,7 @@ class BoxController extends Controller /** * Deprecated method, no longer in use. + * * @deprecated */ public function available(): JsonResponse @@ -73,7 +72,7 @@ class BoxController extends Controller $cache->addProperty($convertToNative); $cache->addProperty('box-balance'); if ($cache->has()) { - return response()->json($cache->get()); + // return response()->json($cache->get()); } // prep some arrays: $incomes = []; @@ -91,9 +90,8 @@ class BoxController extends Controller /** @var array $journal */ foreach ($set as $journal) { - $field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount'; $currencyId = $convertToNative ? $currency->id : (int) $journal['currency_id']; - $amount = $journal[$field] ?? '0'; + $amount = Amount::getAmountFromJournal($journal); $incomes[$currencyId] ??= '0'; $incomes[$currencyId] = bcadd($incomes[$currencyId], app('steam')->positive($amount)); $sums[$currencyId] ??= '0'; @@ -109,9 +107,8 @@ class BoxController extends Controller /** @var array $journal */ foreach ($set as $journal) { - $field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount'; $currencyId = $convertToNative ? $currency->id : (int) $journal['currency_id']; - $amount = $journal[$field] ?? '0'; + $amount = Amount::getAmountFromJournal($journal); $expenses[$currencyId] ??= '0'; $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); $sums[$currencyId] ??= '0'; diff --git a/app/Models/Account.php b/app/Models/Account.php index fcb43a5878..c56940e2ee 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -61,7 +61,7 @@ class Account extends Model 'virtual_balance' => 'string', ]; - protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban']; + protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban','native_virtual_balance']; protected $hidden = ['encrypted']; private bool $joinedAccountTypes = false; diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 0edeb437cf..fb8c0a087e 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -37,6 +37,7 @@ use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Destroy\BillDestroyService; use FireflyIII\Services\Internal\Update\BillUpdateService; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Query\JoinClause; @@ -501,6 +502,7 @@ class BillRepository implements BillRepositoryInterface public function sumPaidInRange(Carbon $start, Carbon $end): array { + Log::debug(sprintf('sumPaidInRange from %s to %s', $start->toW3cString(), $end->toW3cString())); $bills = $this->getActiveBills(); $return = []; $convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data; @@ -508,12 +510,11 @@ class BillRepository implements BillRepositoryInterface /** @var Bill $bill */ foreach ($bills as $bill) { + /** @var Collection $set */ - $set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); - $currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; - $field = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount' : 'amount'; - $foreignField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_foreign_amount' : 'foreign_amount'; - $return[$currency->id] ??= [ + $set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); + $currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; + $return[(int) $currency->id] ??= [ 'id' => (string) $currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, @@ -521,20 +522,14 @@ class BillRepository implements BillRepositoryInterface 'decimal_places' => $currency->decimal_places, 'sum' => '0', ]; - + $setAmount = '0'; /** @var TransactionJournal $transactionJournal */ foreach ($set as $transactionJournal) { - /** @var null|Transaction $sourceTransaction */ - $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); - if (null !== $sourceTransaction) { - $amount = $sourceTransaction->$field; - if ((int) $sourceTransaction->foreign_currency_id === $currency->id) { - // use foreign amount instead! - $amount = (string) $sourceTransaction->$foreignField; - } - $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); - } + $setAmount = bcadd($setAmount, Amount::getAmountFromJournalObject($transactionJournal)); } + Log::debug(sprintf('Bill #%d ("%s") with %d transaction(s) and sum %s %s', $bill->id, $bill->name, $set->count(), $currency->code, $setAmount)); + $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $setAmount); + Log::debug(sprintf('Total sum is now %s', $return[$currency->id]['sum'])); } return $return; @@ -568,6 +563,7 @@ class BillRepository implements BillRepositoryInterface $minField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_min' : 'amount_min'; $maxField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_max' : 'amount_max'; + Log::debug(sprintf('min field is %s, max field is %s', $minField, $maxField)); if ($total > 0) { $currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 43f5a12d76..e3c49d4b03 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -31,9 +31,11 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; /** * Class OperationsRepository @@ -209,11 +211,12 @@ class OperationsRepository implements OperationsRepositoryInterface ?TransactionCurrency $currency = null ): array { - // this collector excludes all transfers TO - // liabilities (which are also withdrawals) - // because those expenses only become expenses - // once they move from the liability to the friend. + // this collector excludes all transfers TO liabilities (which are also withdrawals) + // because those expenses only become expenses once they move from the liability to the friend. // TODO this filter must be somewhere in AccountRepositoryInterface because I suspect its needed more often (A113) + + // 2024-12-24 disable the exclusion for now. + $repository = app(AccountRepositoryInterface::class); $repository->setUser($this->user); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); @@ -234,7 +237,7 @@ class OperationsRepository implements OperationsRepositoryInterface $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user) ->setRange($start, $end) - ->excludeDestinationAccounts($selection) + // ->excludeDestinationAccounts($selection) ->setTypes([TransactionType::WITHDRAWAL]); if (null !== $accounts) { @@ -249,13 +252,12 @@ class OperationsRepository implements OperationsRepositoryInterface $collector->setBudgets($budgets); $journals = $collector->getExtractedJournals(); - // same but for foreign currencies: + // same but for transactions in the foreign currency: if (null !== $currency) { // app('log')->debug(sprintf('Currency is "%s".', $currency->name)); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) - ->setForeignCurrency($currency)->setBudgets($budgets); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setForeignCurrency($currency)->setBudgets($budgets); if (null !== $accounts) { $collector->setAccounts($accounts); @@ -273,9 +275,9 @@ class OperationsRepository implements OperationsRepositoryInterface $currencySymbol = $journal['currency_symbol']; $currencyCode = $journal['currency_code']; $currencyDecimalPlaces = $journal['currency_decimal_places']; - $field = 'amount'; - $foreignField = 'foreign_amount'; + // if the user wants everything in native currency, use it. + // if the foreign amount is in this currency it's OK because Amount::getAmountFromJournal catches that. if ($convertToNative && $default->id !== (int) $journal['currency_id']) { // use default currency info $currencyId = $default->id; @@ -283,8 +285,6 @@ class OperationsRepository implements OperationsRepositoryInterface $currencySymbol = $default->symbol; $currencyCode = $default->code; $currencyDecimalPlaces = $default->decimal_places; - $field = 'native_amount'; - $foreignField = 'native_foreign_amount'; } $array[$currencyId] ??= [ 'sum' => '0', @@ -294,21 +294,9 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $currencyCode, 'currency_decimal_places' => $currencyDecimalPlaces, ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal[$field])); - - // also do foreign amount: - $foreignId = (int) $journal['foreign_currency_id']; - if (0 !== $foreignId && $foreignId !== $currencyId) { - $array[$foreignId] ??= [ - 'sum' => '0', - 'currency_id' => $foreignId, - 'currency_name' => $journal['foreign_currency_name'], - 'currency_symbol' => $journal['foreign_currency_symbol'], - 'currency_code' => $journal['foreign_currency_code'], - 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], - ]; - $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->negative($journal['foreign_amount'])); - } + $amount = Amount::getAmountFromJournal($journal); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($amount)); + Log::debug(sprintf('Journal #%d adds amount %s %s', $journal['transaction_journal_id'], $currencyCode, $amount)); } return $array; diff --git a/app/Support/Amount.php b/app/Support/Amount.php index cff9e3989d..a185a6c891 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -24,7 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Support; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use FireflyIII\User; use Illuminate\Support\Collection; @@ -45,6 +47,46 @@ class Amount return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); } + /** + * Experimental function to see if we can quickly and quietly get the amount from a journal. + * This depends on the user's default currency and the wish to have it converted. + */ + public function getAmountFromJournal(array $journal): string + { + $convertToNative = app('preferences')->get('convert_to_native', false)->data; + $currency = app('amount')->getDefaultCurrency(); + $field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount'; + $amount = $journal[$field] ?? '0'; + // fallback, the transaction has a foreign amount in $currency. + if ($convertToNative && null !== $journal['foreign_amount'] && $currency->id === $journal['foreign_currency_id']) { + $amount = $journal['foreign_amount']; + } + return $amount; + } + + /** + * Experimental function to see if we can quickly and quietly get the amount from a journal. + * This depends on the user's default currency and the wish to have it converted. + */ + public function getAmountFromJournalObject(TransactionJournal $journal): string + { + $convertToNative = app('preferences')->get('convert_to_native', false)->data; + $currency = app('amount')->getDefaultCurrency(); + $field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_amount' : 'amount'; + /** @var null|Transaction $sourceTransaction */ + $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); + if (null === $sourceTransaction) { + return '0'; + } + $amount = $sourceTransaction->$field; + if ((int) $sourceTransaction->foreign_currency_id === $currency->id) { + // use foreign amount instead! + $amount = (string) $sourceTransaction->foreign_amount; // hard coded to be foreign amount. + } + return $amount; + } + + /** * This method will properly format the given number, in color or "black and white", * as a currency, given two things: the currency required and the current locale. @@ -55,15 +97,15 @@ class Amount */ public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string { - $locale = app('steam')->getLocale(); - $rounded = app('steam')->bcround($amount, $decimalPlaces); + $locale = app('steam')->getLocale(); + $rounded = app('steam')->bcround($amount, $decimalPlaces); $coloured ??= true; - $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); $fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = (string) $fmt->format((float) $rounded); // intentional float + $result = (string) $fmt->format((float) $rounded); // intentional float if (true === $coloured) { if (1 === bccomp($rounded, '0')) { @@ -109,7 +151,7 @@ class Amount public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency { - $cache = new CacheProperties(); + $cache = new CacheProperties(); $cache->addProperty('getDefaultCurrencyByGroup'); $cache->addProperty($userGroup->id); if ($cache->has()) { @@ -172,20 +214,20 @@ class Amount private function getLocaleInfo(): array { // get config from preference, not from translation: - $locale = app('steam')->getLocale(); - $array = app('steam')->getLocaleArray($locale); + $locale = app('steam')->getLocale(); + $array = app('steam')->getLocaleArray($locale); setlocale(LC_MONETARY, $array); - $info = localeconv(); + $info = localeconv(); // correct variables - $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); - $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); + $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); + $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); - $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); - $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); + $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); + $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); - $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); + $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); $info['mon_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); @@ -208,7 +250,7 @@ class Amount public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string { // negative first: - $space = ' '; + $space = ' '; // require space between symbol and amount? if (false === $sepBySpace) { @@ -217,11 +259,11 @@ class Amount // there are five possible positions for the "+" or "-" sign (if it is even used) // pos_a and pos_e could be the ( and ) symbol. - $posA = ''; // before everything - $posB = ''; // before currency symbol - $posC = ''; // after currency symbol - $posD = ''; // before amount - $posE = ''; // after everything + $posA = ''; // before everything + $posB = ''; // before currency symbol + $posC = ''; // after currency symbol + $posD = ''; // before amount + $posE = ''; // after everything // format would be (currency before amount) // AB%sC_D%vE @@ -263,11 +305,11 @@ class Amount } // default is amount before currency - $format = $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; + $format = $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; if ($csPrecedes) { // alternative is currency before amount - $format = $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; + $format = $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; } return $format; diff --git a/app/Support/Steam.php b/app/Support/Steam.php index ca846170c1..70518f43af 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -289,7 +289,7 @@ class Steam ; $return['native_balance'] = $this->sumTransactions($array, 'native_amount'); // Log::debug(sprintf('native_balance is %s', $return['native_balance'])); - $return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['balance']); + $return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']); // Log::debug(sprintf('native_balance is %s (with virtual balance)', $return['native_balance'])); } diff --git a/public/v1/js/ff/index.js b/public/v1/js/ff/index.js index 5f963c126a..f053545079 100644 --- a/public/v1/js/ff/index.js +++ b/public/v1/js/ff/index.js @@ -26,6 +26,7 @@ $(function () { }); + function drawChart() { "use strict"; lineChart(accountFrontpageUrl, 'accounts-chart'); @@ -37,13 +38,77 @@ function drawChart() { columnChart('chart/category/frontpage', 'categories-chart'); columnChart(accountExpenseUrl, 'expense-accounts-chart'); columnChart(accountRevenueUrl, 'revenue-accounts-chart'); - - // get balance box: - getBalanceBox(); - getBillsBox(); - getAvailableBox(); - getNetWorthBox(); getPiggyBanks(); + getAllBoxes(); + + function getAllBoxes() { + // get summary. + $.getJSON('api/v1/summary/basic?start=' + sessionStart + '&end=' + sessionEnd).done(function (data) { + var key; + + // balance. + var balance_top = []; + var balance_bottom = []; + + // bills + var unpaid = []; + var paid = []; + + // left to spend. + var left_to_spend_top = []; + var left_to_spend_bottom = []; + + // net worth + var net_worth = []; + + + for (key in data) { + // balance + if (key.substring(0, 11) === 'balance-in-') { + balance_top.push(data[key].value_parsed); + balance_bottom.push(data[key].sub_title); + } + + // bills + if (key.substring(0, 16) === 'bills-unpaid-in-') { + unpaid.push(data[key].value_parsed); + } + if (key.substring(0, 14) === 'bills-paid-in-') { + paid.push(data[key].value_parsed); + } + + // left to spend + if (key.substring(0, 17) === 'left-to-spend-in-') { + left_to_spend_top.push(data[key].value_parsed); + left_to_spend_bottom.push(data[key].sub_title); + if(parseFloat(data[key].monetary_value) < 0) { + $('#box-left-to-spend-box').removeClass('bg-green-gradient').addClass('bg-red-gradient'); + } + } + + // net worth + if (key.substring(0, 13) === 'net-worth-in-') { + net_worth.push(data[key].value_parsed); + } + } + + // balance + $('#box-balance-sums').html(balance_top.join(', ')); + $('#box-balance-list').html(balance_bottom.join(', ')); + + // bills + $('#box-bills-unpaid').html(unpaid.join(', ')); + $('#box-bills-paid').html(paid.join(', ')); + + // left to spend + $('#box-left-to-spend').html(left_to_spend_top.join(', ')); + $('#box-left-per-day').html(left_to_spend_bottom.join(', ')); + + // net worth + $('#box-net-worth').html(net_worth.join(', ')); + + }); + } //getBoxAmounts(); } @@ -58,121 +123,3 @@ function getPiggyBanks() { } }); } - -function getNetWorthBox() { - // box-net-worth - $.getJSON('json/box/net-worth').done(function (data) { - $('#box-net-worth').html(data.net_worths.join(', ')); - }); -} - -/** - * - */ -function getAvailableBox() { - // box-left-to-spend - // box-left-per-day - // * 0) If the user has available amount this period and has overspent: overspent box. - // * 1) If the user has available amount this period and has NOT overspent: left to spend box. - // * 2) if the user has no available amount set this period: spent per day - $.getJSON('json/box/available').done(function (data) { - $('#box-left-to-spend-text').text(data.title); - if (0 === data.display) { - $('#box-left-to-spend-box').removeClass('bg-green-gradient').addClass('bg-red-gradient'); - $('#box-left-to-spend').html(data.left_to_spend); - $('#box-left-per-day').html(data.left_per_day); - } - if (1 === data.display) { - $('#box-left-to-spend').html(data.left_to_spend); - $('#box-left-per-day').html(data.left_per_day); - } - if (2 === data.display) { - $('#box-left-to-spend').html(data.spent_total); - $('#box-left-per-day').html(data.spent_per_day); - } - }); -} - -/** - * - */ -function getBillsBox() { - // box-bills-unpaid - // box-bills-paid - - // get summary. - - $.getJSON('api/v1/summary/basic?start=' + sessionStart + '&end=' + sessionEnd).done(function (data) { - var key; - var unpaid = []; - var paid = []; - for (key in data) { - //console.log(key); - if (key.substr(0, 16) === 'bills-unpaid-in-') { - // only when less than 3. - if (unpaid.length < 3) { - unpaid.push(data[key].value_parsed); - } - } - if (key.substr(0, 14) === 'bills-paid-in-') { - // only when less than 5. - if (paid.length < 3) { - paid.push(data[key].value_parsed); - } - } - } - $('#box-bills-unpaid').html(unpaid.join(', ')); - $('#box-bills-paid').html(paid.join(', ')); - }); -} - -/** - * - */ -function getBalanceBox() { - // box-balance-sums - // box-balance-list - $.getJSON('json/box/balance').done(function (data) { - if (data.size === 1) { - // show balance in "sums", show single entry in list. - for (var x in data.sums) { - $('#box-balance-sums').html(data.sums[x]); - $('#box-balance-list').html(data.incomes[x] + ' + ' + data.expenses[x]); - } - return; - } - // do not use "sums", only use list. - $('#box-balance-progress').remove(); - var expense, string, sum, income, current; - - // first loop, echo only "preferred". - for (x in data.sums) { - current = $('#box-balance-list').html(); - sum = data.sums[x]; - expense = data.expenses[x]; - income = data.incomes[x]; - string = income + ' + ' + expense + ': ' + sum; - if (data.preferred == x) { - $('#box-balance-list').html(current + '' + string + '' + '
'); - } - } - // then list the others (only 1 space) - - var count = 0; - for (x in data.sums) { - if (count > 2) { - return; - } - current = $('#box-balance-list').html(); - sum = data.sums[x]; - expense = data.expenses[x]; - income = data.incomes[x]; - string = income + ' + ' + expense + ': ' + sum; - if (data.preferred != x) { - $('#box-balance-list').html(current + '' + string + '' + '
'); - } - count++; - - } - }); -} \ No newline at end of file