Auto commit for release 'branch-v6.2' on 2024-12-25

This commit is contained in:
github-actions
2024-12-25 07:13:41 +01:00
parent e73fe06f7e
commit e8cc321898
28 changed files with 713 additions and 626 deletions

View File

@@ -32,7 +32,6 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface; use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
@@ -94,24 +93,24 @@ class BasicController extends Controller
public function basic(DateRequest $request): JsonResponse public function basic(DateRequest $request): JsonResponse
{ {
// parameters for boxes: // parameters for boxes:
$dates = $request->getAll(); $dates = $request->getAll();
$start = $dates['start']; $start = $dates['start'];
$end = $dates['end']; $end = $dates['end'];
$code = $request->get('currency_code'); $code = $request->get('currency_code');
// balance information: // balance information:
$balanceData = $this->getBalanceInformation($start, $end); $balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end); $billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end); $spentData = $this->getLeftToSpendInfo($start, $end);
$netWorthData = $this->getNetWorthInfo($start, $end); $netWorthData = $this->getNetWorthInfo($start, $end);
// $balanceData = []; // $balanceData = [];
// $billData = []; // $billData = [];
// $spentData = []; // $spentData = [];
// $netWorthData = []; // $netWorthData = [];
$total = array_merge($balanceData, $billData, $spentData, $netWorthData); $total = array_merge($balanceData, $billData, $spentData, $netWorthData);
// give new keys // give new keys
$return = []; $return = [];
foreach ($total as $entry) { foreach ($total as $entry) {
if (null === $code || ($code === $entry['currency_code'])) { if (null === $code || ($code === $entry['currency_code'])) {
$return[$entry['key']] = $entry; $return[$entry['key']] = $entry;
@@ -125,19 +124,19 @@ class BasicController extends Controller
{ {
// some config settings // some config settings
$convertToNative = app('preferences')->get('convert_to_native', false)->data; $convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getDefaultCurrency();
// prep some arrays: // prep some arrays:
$incomes = []; $incomes = [];
$expenses = []; $expenses = [];
$sums = []; $sums = [];
$return = []; $return = [];
// collect income of user using the new group collector. // collect income of user using the new group collector.
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$set = $collector->getExtractedJournals(); $set = $collector->getExtractedJournals();
/** @var array $journal */ /** @var array $journal */
foreach ($set as $journal) { foreach ($set as $journal) {
@@ -154,14 +153,14 @@ class BasicController extends Controller
// collect expenses of user using the new group collector. // collect expenses of user using the new group collector.
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$set = $collector->getExtractedJournals(); $set = $collector->getExtractedJournals();
/** @var array $journal */ /** @var array $journal */
foreach ($set as $journal) { foreach ($set as $journal) {
$currencyId = $convertToNative ? $default->id : (int) $journal['currency_id']; $currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal); $amount = Amount::getAmountFromJournal($journal);
$expenses[$currencyId] ??= '0'; $expenses[$currencyId] ??= '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount);
$sums[$currencyId] ??= '0'; $sums[$currencyId] ??= '0';
@@ -169,7 +168,7 @@ class BasicController extends Controller
} }
// format amounts: // format amounts:
$keys = array_keys($sums); $keys = array_keys($sums);
foreach ($keys as $currencyId) { foreach ($keys as $currencyId) {
$currency = $this->currencyRepos->find($currencyId); $currency = $this->currencyRepos->find($currencyId);
if (null === $currency) { if (null === $currency) {
@@ -186,8 +185,8 @@ class BasicController extends Controller
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false), 'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale', 'local_icon' => 'balance-scale',
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) . 'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false).
' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), ' + '.app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
]; ];
$return[] = [ $return[] = [
'key' => sprintf('spent-in-%s', $currency->code), 'key' => sprintf('spent-in-%s', $currency->code),
@@ -228,7 +227,7 @@ class BasicController extends Controller
$paidAmount = $this->billRepository->sumPaidInRange($start, $end); $paidAmount = $this->billRepository->sumPaidInRange($start, $end);
$unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end); $unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end);
$return = []; $return = [];
/** /**
* @var array $info * @var array $info
@@ -298,7 +297,7 @@ class BasicController extends Controller
Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum'])); Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum']));
$return[] = [ $return[] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']), 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]), 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
'monetary_value' => $leftToSpend, 'monetary_value' => $leftToSpend,
@@ -323,9 +322,10 @@ class BasicController extends Controller
private function getNetWorthInfo(Carbon $start, Carbon $end): array private function getNetWorthInfo(Carbon $start, Carbon $end): array
{ {
Log::debug('getNetWorthInfo'); Log::debug('getNetWorthInfo');
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$date = now(config('app.timezone')); $date = now(config('app.timezone'));
// start and end in the future? use $end // start and end in the future? use $end
if ($this->notInDateRange($date, $start, $end)) { if ($this->notInDateRange($date, $start, $end)) {
/** @var Carbon $date */ /** @var Carbon $date */
@@ -335,10 +335,10 @@ class BasicController extends Controller
/** @var NetWorthInterface $netWorthHelper */ /** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class); $netWorthHelper = app(NetWorthInterface::class);
$netWorthHelper->setUser($user); $netWorthHelper->setUser($user);
$allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]); $allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]);
// filter list on preference of being included. // filter list on preference of being included.
$filtered = $allAccounts->filter( $filtered = $allAccounts->filter(
function (Account $account) { function (Account $account) {
$includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth'); $includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth');
@@ -346,13 +346,13 @@ class BasicController extends Controller
} }
); );
$netWorthSet = $netWorthHelper->byAccounts($filtered, $date); $netWorthSet = $netWorthHelper->byAccounts($filtered, $date);
$return = []; $return = [];
foreach ($netWorthSet as $key => $data) { foreach ($netWorthSet as $key => $data) {
if ('native' === $key) { if ('native' === $key) {
continue; continue;
} }
$amount = $data['balance']; $amount = $data['balance'];
if (0 === bccomp($amount, '0')) { if (0 === bccomp($amount, '0')) {
continue; continue;
} }

View File

@@ -212,10 +212,10 @@ class RecalculateNativeAmounts extends Command
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id) ->where('transaction_journals.user_group_id', $userGroup->id)
->where(function(DatabaseBuilder $q1) use ($currency) { ->where(function (DatabaseBuilder $q1) use ($currency): void {
$q1->where(function(DatabaseBuilder $q2) use ($currency) { $q1->where(function (DatabaseBuilder $q2) use ($currency): void {
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id'); $q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
})->orWhere(function(DatabaseBuilder $q3) use ($currency) { })->orWhere(function (DatabaseBuilder $q3) use ($currency): void {
$q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id); $q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id);
}); });
}) })

View File

@@ -77,28 +77,28 @@ class NetWorth implements NetWorthInterface
// return $cache->get(); // return $cache->get();
} }
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s'))); Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
$default = Amount::getDefaultCurrency(); $default = Amount::getDefaultCurrency();
$netWorth = []; $netWorth = [];
$balances = Steam::finalAccountsBalance($accounts, $date); $balances = Steam::finalAccountsBalance($accounts, $date);
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
Log::debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name)); Log::debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name));
$currency = $this->getRepository()->getAccountCurrency($account) ?? $default; $currency = $this->getRepository()->getAccountCurrency($account) ?? $default;
$useNative = $convertToNative && $default->id !== $currency->id; $useNative = $convertToNative && $default->id !== $currency->id;
$currency = $useNative ? $default : $currency; $currency = $useNative ? $default : $currency;
$currencyCode = $currency->code; $currencyCode = $currency->code;
$balance = '0'; $balance = '0';
$nativeBalance = '0'; $nativeBalance = '0';
if (array_key_exists($account->id, $balances)) { if (array_key_exists($account->id, $balances)) {
$balance = $balances[$account->id]['balance'] ?? '0'; $balance = $balances[$account->id]['balance'] ?? '0';
$nativeBalance = $balances[$account->id]['native_balance'] ?? '0'; $nativeBalance = $balances[$account->id]['native_balance'] ?? '0';
} }
Log::debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance)); Log::debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance));
// always subtract virtual balance again. // always subtract virtual balance again.
$balance = '' !== (string) $account->virtual_balance ? bcsub($balance, $account->virtual_balance) : $balance; $balance = '' !== (string) $account->virtual_balance ? bcsub($balance, $account->virtual_balance) : $balance;
$nativeBalance = '' !== (string) $account->native_virtual_balance ? bcsub($nativeBalance, $account->native_virtual_balance) : $nativeBalance; $nativeBalance = '' !== (string) $account->native_virtual_balance ? bcsub($nativeBalance, $account->native_virtual_balance) : $nativeBalance;
$amountToUse = $useNative ? $nativeBalance : $balance; $amountToUse = $useNative ? $nativeBalance : $balance;
Log::debug(sprintf('Will use %s %s', $currencyCode, $amountToUse)); Log::debug(sprintf('Will use %s %s', $currencyCode, $amountToUse));
$netWorth[$currencyCode] ??= [ $netWorth[$currencyCode] ??= [
@@ -117,7 +117,7 @@ class NetWorth implements NetWorthInterface
return $netWorth; return $netWorth;
} }
private function getRepository(): AccountRepositoryInterface | AdminAccountRepositoryInterface private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface
{ {
if (null === $this->userGroup) { if (null === $this->userGroup) {
return $this->accountRepository; return $this->accountRepository;
@@ -126,19 +126,19 @@ class NetWorth implements NetWorthInterface
return $this->adminAccountRepository; return $this->adminAccountRepository;
} }
public function setUser(null | Authenticatable | User $user): void public function setUser(null|Authenticatable|User $user): void
{ {
if (!$user instanceof User) { if (!$user instanceof User) {
return; return;
} }
$this->user = $user; $this->user = $user;
$this->userGroup = null; $this->userGroup = null;
// make repository: // make repository:
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$this->accountRepository->setUser($this->user); $this->accountRepository->setUser($this->user);
$this->currencyRepos = app(CurrencyRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->currencyRepos->setUser($this->user); $this->currencyRepos->setUser($this->user);
} }
@@ -161,16 +161,16 @@ class NetWorth implements NetWorthInterface
$return = []; $return = [];
$balances = Steam::finalAccountsBalance($accounts, $date); $balances = Steam::finalAccountsBalance($accounts, $date);
foreach ($accounts as $account) { foreach ($accounts as $account) {
$currency = $this->getRepository()->getAccountCurrency($account); $currency = $this->getRepository()->getAccountCurrency($account);
$balance = $balances[$account->id]['balance'] ?? '0'; $balance = $balances[$account->id]['balance'] ?? '0';
// always subtract virtual balance. // always subtract virtual balance.
$virtualBalance = $account->virtual_balance; $virtualBalance = $account->virtual_balance;
if ('' !== $virtualBalance) { if ('' !== $virtualBalance) {
$balance = bcsub($balance, $virtualBalance); $balance = bcsub($balance, $virtualBalance);
} }
$return[$currency->id] ??= [ $return[$currency->id] ??= [
'id' => (string) $currency->id, 'id' => (string) $currency->id,
'name' => $currency->name, 'name' => $currency->name,
'symbol' => $currency->symbol, 'symbol' => $currency->symbol,

View File

@@ -71,22 +71,22 @@ class IndexController extends Controller
* */ * */
public function inactive(Request $request, string $objectType) public function inactive(Request $request, string $objectType)
{ {
$inactivePage = true; $inactivePage = true;
$subTitle = (string) trans(sprintf('firefly.%s_accounts_inactive', $objectType)); $subTitle = (string) trans(sprintf('firefly.%s_accounts_inactive', $objectType));
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
$types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType)); $types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType));
$collection = $this->repository->getInactiveAccountsByType($types); $collection = $this->repository->getInactiveAccountsByType($types);
$total = $collection->count(); $total = $collection->count();
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page'); $page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data; $pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$accounts = $collection->slice(($page - 1) * $pageSize, $pageSize); $accounts = $collection->slice(($page - 1) * $pageSize, $pageSize);
unset($collection); unset($collection);
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$start->subDay(); $start->subDay();
$ids = $accounts->pluck('id')->toArray(); $ids = $accounts->pluck('id')->toArray();
@@ -111,7 +111,7 @@ class IndexController extends Controller
); );
// make paginator: // make paginator:
$accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page); $accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page);
$accounts->setPath(route('accounts.inactive.index', [$objectType])); $accounts->setPath(route('accounts.inactive.index', [$objectType]));
return view('accounts.index', compact('objectType', 'inactivePage', 'subTitleIcon', 'subTitle', 'page', 'accounts')); return view('accounts.index', compact('objectType', 'inactivePage', 'subTitleIcon', 'subTitle', 'page', 'accounts'));
@@ -127,9 +127,9 @@ class IndexController extends Controller
public function index(Request $request, string $objectType) public function index(Request $request, string $objectType)
{ {
app('log')->debug(sprintf('Now at %s', __METHOD__)); app('log')->debug(sprintf('Now at %s', __METHOD__));
$subTitle = (string) trans(sprintf('firefly.%s_accounts', $objectType)); $subTitle = (string) trans(sprintf('firefly.%s_accounts', $objectType));
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
$types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType)); $types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType));
$this->repository->resetAccountOrder(); $this->repository->resetAccountOrder();
@@ -145,10 +145,10 @@ class IndexController extends Controller
unset($collection); unset($collection);
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$start->subDay(); $start->subDay();
$ids = $accounts->pluck('id')->toArray(); $ids = $accounts->pluck('id')->toArray();
@@ -159,9 +159,9 @@ class IndexController extends Controller
$accounts->each( $accounts->each(
function (Account $account) use ($activities, $startBalances, $endBalances): void { function (Account $account) use ($activities, $startBalances, $endBalances): void {
$interest = (string) $this->repository->getMetaValue($account, 'interest'); $interest = (string) $this->repository->getMetaValue($account, 'interest');
$interest = '' === $interest ? '0' : $interest; $interest = '' === $interest ? '0' : $interest;
$currency = $this->repository->getAccountCurrency($account); $currency = $this->repository->getAccountCurrency($account);
$account->startBalances = $this->getBalance($account, $currency, $startBalances); $account->startBalances = $this->getBalance($account, $currency, $startBalances);
$account->endBalances = $this->getBalance($account, $currency, $endBalances); $account->endBalances = $this->getBalance($account, $currency, $endBalances);
@@ -175,7 +175,7 @@ class IndexController extends Controller
$account->location = $this->repository->getLocation($account); $account->location = $this->repository->getLocation($account);
$account->liability_direction = $this->repository->getMetaValue($account, 'liability_direction'); $account->liability_direction = $this->repository->getMetaValue($account, 'liability_direction');
$account->current_debt = $this->repository->getMetaValue($account, 'current_debt') ?? '-'; $account->current_debt = $this->repository->getMetaValue($account, 'current_debt') ?? '-';
$account->currency = $currency; $account->currency = $currency;
$account->iban = implode(' ', str_split((string) $account->iban, 4)); $account->iban = implode(' ', str_split((string) $account->iban, 4));
@@ -185,7 +185,7 @@ class IndexController extends Controller
app('log')->debug(sprintf('Count of accounts before LAP: %d', $accounts->count())); app('log')->debug(sprintf('Count of accounts before LAP: %d', $accounts->count()));
/** @var LengthAwarePaginator $accounts */ /** @var LengthAwarePaginator $accounts */
$accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page); $accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page);
$accounts->setPath(route('accounts.index', [$objectType])); $accounts->setPath(route('accounts.index', [$objectType]));
app('log')->debug(sprintf('Count of accounts after LAP (1): %d', $accounts->count())); app('log')->debug(sprintf('Count of accounts after LAP (1): %d', $accounts->count()));
@@ -194,7 +194,7 @@ class IndexController extends Controller
return view('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts')); return view('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts'));
} }
private function getBalance(Account $account, ?TransactionCurrency $currency = null, array $balances): array private function getBalance(Account $account, ?TransactionCurrency $currency, array $balances): array
{ {
if (!array_key_exists($account->id, $balances)) { if (!array_key_exists($account->id, $balances)) {
return []; return [];
@@ -217,6 +217,7 @@ class IndexController extends Controller
foreach ($endBalances as $key => $value) { foreach ($endBalances as $key => $value) {
$result[$key] = bcsub($value, $startBalances[$key] ?? '0'); $result[$key] = bcsub($value, $startBalances[$key] ?? '0');
} }
return $result; return $result;
} }
} }

View File

@@ -84,6 +84,7 @@ class AccountController extends Controller
public function expenseAccounts(): JsonResponse public function expenseAccounts(): JsonResponse
{ {
Log::debug('RevenueAccounts'); Log::debug('RevenueAccounts');
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
@@ -95,7 +96,7 @@ class AccountController extends Controller
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
$cache->addProperty('chart.account.expense-accounts'); $cache->addProperty('chart.account.expense-accounts');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
$start->subDay(); $start->subDay();
@@ -114,34 +115,38 @@ class AccountController extends Controller
$endBalances = app('steam')->finalAccountsBalance($accounts, $end); $endBalances = app('steam')->finalAccountsBalance($accounts, $end);
// loop the accounts, then check for balance and currency info. // loop the accounts, then check for balance and currency info.
foreach($accounts as $account) { foreach ($accounts as $account) {
Log::debug(sprintf('Now in account #%d ("%s")', $account->id, $account->name)); Log::debug(sprintf('Now in account #%d ("%s")', $account->id, $account->name));
$expenses = $endBalances[$account->id] ?? false; $expenses = $endBalances[$account->id] ?? false;
if(false === $expenses) { if (false === $expenses) {
Log::error(sprintf('Found no end balance for account #%d',$account->id)); Log::error(sprintf('Found no end balance for account #%d', $account->id));
continue; continue;
} }
/** /**
* @var string $key * @var string $key
* @var string $endBalance * @var string $endBalance
*/ */
foreach ($expenses as $key => $endBalance) { foreach ($expenses as $key => $endBalance) {
if(!$this->convertToNative && 'native_balance' === $key) { if (!$this->convertToNative && 'native_balance' === $key) {
Log::debug(sprintf('[a] Will skip expense array "%s"', $key)); Log::debug(sprintf('[a] Will skip expense array "%s"', $key));
continue; continue;
} }
if($this->convertToNative && 'native_balance' !== $key) { if ($this->convertToNative && 'native_balance' !== $key) {
Log::debug(sprintf('[b] Will skip expense array "%s"', $key)); Log::debug(sprintf('[b] Will skip expense array "%s"', $key));
continue; continue;
} }
Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance));
$searchCode = $this->convertToNative ? $default->code: $key; $searchCode = $this->convertToNative ? $default->code : $key;
Log::debug(sprintf('Search code is %s', $searchCode)); Log::debug(sprintf('Search code is %s', $searchCode));
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startBalance = ($startBalances[$account->id][$key] ?? '0'); $startBalance = ($startBalances[$account->id][$key] ?? '0');
Log::debug(sprintf('Start balance is %s', $startBalance)); Log::debug(sprintf('Start balance is %s', $startBalance));
$diff = bcsub($endBalance, $startBalance); $diff = bcsub($endBalance, $startBalance);
$currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
@@ -522,7 +527,7 @@ class AccountController extends Controller
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
$cache->addProperty('chart.account.revenue-accounts'); $cache->addProperty('chart.account.revenue-accounts');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
$start->subDay(); $start->subDay();
@@ -541,35 +546,39 @@ class AccountController extends Controller
$endBalances = app('steam')->finalAccountsBalance($accounts, $end); $endBalances = app('steam')->finalAccountsBalance($accounts, $end);
// loop the accounts, then check for balance and currency info. // loop the accounts, then check for balance and currency info.
foreach($accounts as $account) { foreach ($accounts as $account) {
Log::debug(sprintf('Now in account #%d ("%s")', $account->id, $account->name)); Log::debug(sprintf('Now in account #%d ("%s")', $account->id, $account->name));
$expenses = $endBalances[$account->id] ?? false; $expenses = $endBalances[$account->id] ?? false;
if(false === $expenses) { if (false === $expenses) {
Log::error(sprintf('Found no end balance for account #%d',$account->id)); Log::error(sprintf('Found no end balance for account #%d', $account->id));
continue; continue;
} }
/** /**
* @var string $key * @var string $key
* @var string $endBalance * @var string $endBalance
*/ */
foreach ($expenses as $key => $endBalance) { foreach ($expenses as $key => $endBalance) {
if(!$this->convertToNative && 'native_balance' === $key) { if (!$this->convertToNative && 'native_balance' === $key) {
Log::debug(sprintf('[a] Will skip expense array "%s"', $key)); Log::debug(sprintf('[a] Will skip expense array "%s"', $key));
continue; continue;
} }
if($this->convertToNative && 'native_balance' !== $key) { if ($this->convertToNative && 'native_balance' !== $key) {
Log::debug(sprintf('[b] Will skip expense array "%s"', $key)); Log::debug(sprintf('[b] Will skip expense array "%s"', $key));
continue; continue;
} }
Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance));
$searchCode = $this->convertToNative ? $default->code: $key; $searchCode = $this->convertToNative ? $default->code : $key;
Log::debug(sprintf('Search code is %s', $searchCode)); Log::debug(sprintf('Search code is %s', $searchCode));
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startBalance = ($startBalances[$account->id][$key] ?? '0'); $startBalance = ($startBalances[$account->id][$key] ?? '0');
Log::debug(sprintf('Start balance is %s', $startBalance)); Log::debug(sprintf('Start balance is %s', $startBalance));
$diff = bcsub($endBalance, $startBalance); $diff = bcsub($endBalance, $startBalance);
$currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.

View File

@@ -85,11 +85,11 @@ class BudgetController extends Controller
public function budget(Budget $budget): JsonResponse public function budget(Budget $budget): JsonResponse
{ {
/** @var Carbon $start */ /** @var Carbon $start */
$start = $this->repository->firstUseDate($budget) ?? session('start', today(config('app.timezone'))); $start = $this->repository->firstUseDate($budget) ?? session('start', today(config('app.timezone')));
/** @var Carbon $end */ /** @var Carbon $end */
$end = session('end', today(config('app.timezone'))); $end = session('end', today(config('app.timezone')));
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('chart.budget.budget'); $cache->addProperty('chart.budget.budget');
@@ -107,19 +107,19 @@ class BudgetController extends Controller
$defaultEntries = []; $defaultEntries = [];
while ($end >= $loopStart) { while ($end >= $loopStart) {
/** @var Carbon $loopEnd */ /** @var Carbon $loopEnd */
$loopEnd = app('navigation')->endOfPeriod($loopStart, $step); $loopEnd = app('navigation')->endOfPeriod($loopStart, $step);
$spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection); $spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection);
$label = trim(app('navigation')->periodShow($loopStart, $step)); $label = trim(app('navigation')->periodShow($loopStart, $step));
foreach ($spent as $row) { foreach ($spent as $row) {
$currencyId = $row['currency_id']; $currencyId = $row['currency_id'];
$currencies[$currencyId] ??= $row; // don't mind the field 'sum' $currencies[$currencyId] ??= $row; // don't mind the field 'sum'
// also store this day's sum: // also store this day's sum:
$currencies[$currencyId]['spent'][$label] = $row['sum']; $currencies[$currencyId]['spent'][$label] = $row['sum'];
} }
$defaultEntries[$label] = 0; $defaultEntries[$label] = 0;
// set loop start to the next period: // set loop start to the next period:
$loopStart = clone $loopEnd; $loopStart = clone $loopEnd;
$loopStart->addSecond(); $loopStart->addSecond();
} }
// loop all currencies: // loop all currencies:
@@ -135,7 +135,7 @@ class BudgetController extends Controller
$chartData[$currencyId]['entries'][$label] = bcmul($spent, '-1'); $chartData[$currencyId]['entries'][$label] = bcmul($spent, '-1');
} }
} }
$data = $this->generator->multiSet(array_values($chartData)); $data = $this->generator->multiSet(array_values($chartData));
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -152,9 +152,9 @@ class BudgetController extends Controller
throw new FireflyException('This budget limit is not part of this budget.'); throw new FireflyException('This budget limit is not part of this budget.');
} }
$start = clone $budgetLimit->start_date; $start = clone $budgetLimit->start_date;
$end = clone $budgetLimit->end_date; $end = clone $budgetLimit->end_date;
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('chart.budget.budget.limit'); $cache->addProperty('chart.budget.budget.limit');
@@ -164,11 +164,11 @@ class BudgetController extends Controller
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$entries = []; $entries = [];
$amount = $budgetLimit->amount; $amount = $budgetLimit->amount;
$budgetCollection = new Collection([$budget]); $budgetCollection = new Collection([$budget]);
$currency = $budgetLimit->transactionCurrency; $currency = $budgetLimit->transactionCurrency;
while ($start <= $end) { while ($start <= $end) {
$current = clone $start; $current = clone $start;
$expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $currency); $expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $currency);
@@ -179,7 +179,7 @@ class BudgetController extends Controller
$start->addDay(); $start->addDay();
} }
$data = $this->generator->singleSet((string) trans('firefly.left'), $entries); $data = $this->generator->singleSet((string) trans('firefly.left'), $entries);
// add currency symbol from budget limit: // add currency symbol from budget limit:
$data['datasets'][0]['currency_symbol'] = $budgetLimit->transactionCurrency->symbol; $data['datasets'][0]['currency_symbol'] = $budgetLimit->transactionCurrency->symbol;
$data['datasets'][0]['currency_code'] = $budgetLimit->transactionCurrency->code; $data['datasets'][0]['currency_code'] = $budgetLimit->transactionCurrency->code;
@@ -200,8 +200,8 @@ class BudgetController extends Controller
$cache->addProperty($budget->id); $cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId); $cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-asset'); $cache->addProperty('chart.budget.expense-asset');
$start = session('first', today(config('app.timezone'))->startOfYear()); $start = session('first', today(config('app.timezone'))->startOfYear());
$end = today(); $end = today();
if (null !== $budgetLimit) { if (null !== $budgetLimit) {
$start = $budgetLimit->start_date; $start = $budgetLimit->start_date;
@@ -216,14 +216,14 @@ class BudgetController extends Controller
} }
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setBudget($budget); $collector->setBudget($budget);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$result = []; $result = [];
$chartData = []; $chartData = [];
// group by asset account ID: // group by asset account ID:
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', (int) $journal['source_account_id'], $journal['currency_id']); $key = sprintf('%d-%d', (int) $journal['source_account_id'], $journal['currency_id']);
$result[$key] ??= [ $result[$key] ??= [
'amount' => '0', 'amount' => '0',
'currency_symbol' => $journal['currency_symbol'], 'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
@@ -232,20 +232,20 @@ class BudgetController extends Controller
$result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']); $result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
} }
$names = $this->getAccountNames(array_keys($result)); $names = $this->getAccountNames(array_keys($result));
foreach ($result as $combinedId => $info) { foreach ($result as $combinedId => $info) {
$parts = explode('-', $combinedId); $parts = explode('-', $combinedId);
$assetId = (int) $parts[0]; $assetId = (int) $parts[0];
$title = sprintf('%s (%s)', $names[$assetId] ?? '(empty)', $info['currency_name']); $title = sprintf('%s (%s)', $names[$assetId] ?? '(empty)', $info['currency_name']);
$chartData[$title] $chartData[$title]
= [ = [
'amount' => $info['amount'], 'amount' => $info['amount'],
'currency_symbol' => $info['currency_symbol'], 'currency_symbol' => $info['currency_symbol'],
'currency_code' => $info['currency_code'], 'currency_code' => $info['currency_code'],
]; ];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -263,8 +263,8 @@ class BudgetController extends Controller
$cache->addProperty($budget->id); $cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId); $cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-category'); $cache->addProperty('chart.budget.expense-category');
$start = session('first', today(config('app.timezone'))->startOfYear()); $start = session('first', today(config('app.timezone'))->startOfYear());
$end = today(); $end = today();
if (null !== $budgetLimit) { if (null !== $budgetLimit) {
$start = $budgetLimit->start_date; $start = $budgetLimit->start_date;
$end = $budgetLimit->end_date; $end = $budgetLimit->end_date;
@@ -278,12 +278,12 @@ class BudgetController extends Controller
} }
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setBudget($budget)->withCategoryInformation(); $collector->setBudget($budget)->withCategoryInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$result = []; $result = [];
$chartData = []; $chartData = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
$result[$key] ??= [ $result[$key] ??= [
'amount' => '0', 'amount' => '0',
'currency_symbol' => $journal['currency_symbol'], 'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
@@ -292,7 +292,7 @@ class BudgetController extends Controller
$result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']); $result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
} }
$names = $this->getCategoryNames(array_keys($result)); $names = $this->getCategoryNames(array_keys($result));
foreach ($result as $combinedId => $info) { foreach ($result as $combinedId => $info) {
$parts = explode('-', $combinedId); $parts = explode('-', $combinedId);
$categoryId = (int) $parts[0]; $categoryId = (int) $parts[0];
@@ -303,7 +303,7 @@ class BudgetController extends Controller
'currency_code' => $info['currency_code'], 'currency_code' => $info['currency_code'],
]; ];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -321,8 +321,8 @@ class BudgetController extends Controller
$cache->addProperty($budget->id); $cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId); $cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-expense'); $cache->addProperty('chart.budget.expense-expense');
$start = session('first', today(config('app.timezone'))->startOfYear()); $start = session('first', today(config('app.timezone'))->startOfYear());
$end = today(); $end = today();
if (null !== $budgetLimit) { if (null !== $budgetLimit) {
$start = $budgetLimit->start_date; $start = $budgetLimit->start_date;
$end = $budgetLimit->end_date; $end = $budgetLimit->end_date;
@@ -336,14 +336,14 @@ class BudgetController extends Controller
} }
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withAccountInformation(); $collector->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withAccountInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$result = []; $result = [];
$chartData = []; $chartData = [];
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['destination_account_id'], $journal['currency_id']); $key = sprintf('%d-%d', $journal['destination_account_id'], $journal['currency_id']);
$result[$key] ??= [ $result[$key] ??= [
'amount' => '0', 'amount' => '0',
'currency_symbol' => $journal['currency_symbol'], 'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
@@ -352,7 +352,7 @@ class BudgetController extends Controller
$result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']); $result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
} }
$names = $this->getAccountNames(array_keys($result)); $names = $this->getAccountNames(array_keys($result));
foreach ($result as $combinedId => $info) { foreach ($result as $combinedId => $info) {
$parts = explode('-', $combinedId); $parts = explode('-', $combinedId);
$opposingId = (int) $parts[0]; $opposingId = (int) $parts[0];
@@ -365,7 +365,7 @@ class BudgetController extends Controller
]; ];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -376,10 +376,10 @@ class BudgetController extends Controller
*/ */
public function frontpage(): JsonResponse public function frontpage(): JsonResponse
{ {
$start = session('start', today(config('app.timezone'))->startOfMonth()); $start = session('start', today(config('app.timezone'))->startOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth()); $end = session('end', today(config('app.timezone'))->endOfMonth());
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
@@ -387,16 +387,16 @@ class BudgetController extends Controller
if ($cache->has()) { if ($cache->has()) {
// return response()->json($cache->get()); // return response()->json($cache->get());
} }
Log::debug(sprintf('Regenerate frontpage chart from scratch.')); Log::debug('Regenerate frontpage chart from scratch.');
$chartGenerator = app(FrontpageChartGenerator::class); $chartGenerator = app(FrontpageChartGenerator::class);
$chartGenerator->setUser(auth()->user()); $chartGenerator->setUser(auth()->user());
$chartGenerator->setStart($start); $chartGenerator->setStart($start);
$chartGenerator->setEnd($end); $chartGenerator->setEnd($end);
$chartGenerator->convertToNative = $this->convertToNative; $chartGenerator->convertToNative = $this->convertToNative;
$chartGenerator->default = Amount::getDefaultCurrency(); $chartGenerator->default = Amount::getDefaultCurrency();
$chartData = $chartGenerator->generate(); $chartData = $chartGenerator->generate();
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -412,7 +412,7 @@ class BudgetController extends Controller
public function period(Budget $budget, TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse public function period(Budget $budget, TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($accounts); $cache->addProperty($accounts);
@@ -441,11 +441,11 @@ class BudgetController extends Controller
], ],
]; ];
$currentStart = clone $start; $currentStart = clone $start;
while ($currentStart <= $end) { while ($currentStart <= $end) {
$currentStart = app('navigation')->startOfPeriod($currentStart, $preferredRange); $currentStart = app('navigation')->startOfPeriod($currentStart, $preferredRange);
$title = $currentStart->isoFormat($titleFormat); $title = $currentStart->isoFormat($titleFormat);
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
// default limit is no limit: // default limit is no limit:
$chartData[0]['entries'][$title] = 0; $chartData[0]['entries'][$title] = 0;
@@ -454,7 +454,7 @@ class BudgetController extends Controller
$chartData[1]['entries'][$title] = 0; $chartData[1]['entries'][$title] = 0;
// get budget limit in this period for this currency. // get budget limit in this period for this currency.
$limit = $this->blRepository->find($budget, $currency, $currentStart, $currentEnd); $limit = $this->blRepository->find($budget, $currency, $currentStart, $currentEnd);
if (null !== $limit) { if (null !== $limit) {
$chartData[1]['entries'][$title] = app('steam')->bcround($limit->amount, $currency->decimal_places); $chartData[1]['entries'][$title] = app('steam')->bcround($limit->amount, $currency->decimal_places);
} }
@@ -464,11 +464,11 @@ class BudgetController extends Controller
$amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0'); $amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0');
$chartData[0]['entries'][$title] = app('steam')->bcround($amount, $currency->decimal_places); $chartData[0]['entries'][$title] = app('steam')->bcround($amount, $currency->decimal_places);
$currentStart = clone $currentEnd; $currentStart = clone $currentEnd;
$currentStart->addDay()->startOfDay(); $currentStart->addDay()->startOfDay();
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -480,7 +480,7 @@ class BudgetController extends Controller
public function periodNoBudget(TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse public function periodNoBudget(TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($accounts); $cache->addProperty($accounts);
@@ -504,7 +504,7 @@ class BudgetController extends Controller
$currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0);
} }
$data = $this->generator->singleSet((string) trans('firefly.spent'), $chartData); $data = $this->generator->singleSet((string) trans('firefly.spent'), $chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);

View File

@@ -70,7 +70,7 @@ class CategoryController extends Controller
public function all(Category $category): JsonResponse public function all(Category $category): JsonResponse
{ {
// cache results: // cache results:
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty('chart.category.all'); $cache->addProperty('chart.category.all');
$cache->addProperty($category->id); $cache->addProperty($category->id);
if ($cache->has()) { if ($cache->has()) {
@@ -78,11 +78,11 @@ class CategoryController extends Controller
} }
/** @var CategoryRepositoryInterface $repository */ /** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class); $repository = app(CategoryRepositoryInterface::class);
$start = $repository->firstUseDate($category) ?? $this->getDate(); $start = $repository->firstUseDate($category) ?? $this->getDate();
$range = app('navigation')->getViewRange(false); $range = app('navigation')->getViewRange(false);
$start = app('navigation')->startOfPeriod($start, $range); $start = app('navigation')->startOfPeriod($start, $range);
$end = $this->getDate(); $end = $this->getDate();
/** @var WholePeriodChartGenerator $chartGenerator */ /** @var WholePeriodChartGenerator $chartGenerator */
$chartGenerator = app(WholePeriodChartGenerator::class); $chartGenerator = app(WholePeriodChartGenerator::class);
@@ -104,10 +104,10 @@ class CategoryController extends Controller
*/ */
public function frontPage(): JsonResponse public function frontPage(): JsonResponse
{ {
$start = session('start', today(config('app.timezone'))->startOfMonth()); $start = session('start', today(config('app.timezone'))->startOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth()); $end = session('end', today(config('app.timezone'))->endOfMonth());
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
@@ -139,7 +139,7 @@ class CategoryController extends Controller
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
$data = $this->reportPeriodChart($accounts, $start, $end, $category); $data = $this->reportPeriodChart($accounts, $start, $end, $category);
$cache->store($data); $cache->store($data);
@@ -160,8 +160,8 @@ class CategoryController extends Controller
$noCatRepository = app(NoCategoryRepositoryInterface::class); $noCatRepository = app(NoCategoryRepositoryInterface::class);
// this gives us all currencies // this gives us all currencies
$expenses = $noCatRepository->listExpenses($start, $end, $accounts); $expenses = $noCatRepository->listExpenses($start, $end, $accounts);
$income = $noCatRepository->listIncome($start, $end, $accounts); $income = $noCatRepository->listIncome($start, $end, $accounts);
} }
if (null !== $category) { if (null !== $category) {
@@ -169,9 +169,9 @@ class CategoryController extends Controller
$opsRepository = app(OperationsRepositoryInterface::class); $opsRepository = app(OperationsRepositoryInterface::class);
$categoryId = $category->id; $categoryId = $category->id;
// this gives us all currencies // this gives us all currencies
$collection = new Collection([$category]); $collection = new Collection([$category]);
$expenses = $opsRepository->listExpenses($start, $end, $accounts, $collection); $expenses = $opsRepository->listExpenses($start, $end, $accounts, $collection);
$income = $opsRepository->listIncome($start, $end, $accounts, $collection); $income = $opsRepository->listIncome($start, $end, $accounts, $collection);
} }
$currencies = array_unique(array_merge(array_keys($income), array_keys($expenses))); $currencies = array_unique(array_merge(array_keys($income), array_keys($expenses)));
$periods = app('navigation')->listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
@@ -185,19 +185,19 @@ class CategoryController extends Controller
$inKey = sprintf('%d-in', $currencyId); $inKey = sprintf('%d-in', $currencyId);
$chartData[$outKey] $chartData[$outKey]
= [ = [
'label' => sprintf('%s (%s)', (string) trans('firefly.spent'), $currencyInfo['currency_name']), 'label' => sprintf('%s (%s)', (string) trans('firefly.spent'), $currencyInfo['currency_name']),
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
]; ];
$chartData[$inKey] $chartData[$inKey]
= [ = [
'label' => sprintf('%s (%s)', (string) trans('firefly.earned'), $currencyInfo['currency_name']), 'label' => sprintf('%s (%s)', (string) trans('firefly.earned'), $currencyInfo['currency_name']),
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
]; ];
// loop empty periods: // loop empty periods:
foreach (array_keys($periods) as $period) { foreach (array_keys($periods) as $period) {
$label = $periods[$period]; $label = $periods[$period];
@@ -205,7 +205,7 @@ class CategoryController extends Controller
$chartData[$inKey]['entries'][$label] = '0'; $chartData[$inKey]['entries'][$label] = '0';
} }
// loop income and expenses for this category.: // loop income and expenses for this category.:
$outSet = $expenses[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []]; $outSet = $expenses[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
foreach ($outSet['transaction_journals'] as $journal) { foreach ($outSet['transaction_journals'] as $journal) {
$amount = app('steam')->positive($journal['amount']); $amount = app('steam')->positive($journal['amount']);
$date = $journal['date']->isoFormat($format); $date = $journal['date']->isoFormat($format);
@@ -214,7 +214,7 @@ class CategoryController extends Controller
$chartData[$outKey]['entries'][$date] = bcadd($amount, $chartData[$outKey]['entries'][$date]); $chartData[$outKey]['entries'][$date] = bcadd($amount, $chartData[$outKey]['entries'][$date]);
} }
$inSet = $income[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []]; $inSet = $income[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []];
foreach ($inSet['transaction_journals'] as $journal) { foreach ($inSet['transaction_journals'] as $journal) {
$amount = app('steam')->positive($journal['amount']); $amount = app('steam')->positive($journal['amount']);
$date = $journal['date']->isoFormat($format); $date = $journal['date']->isoFormat($format);
@@ -240,7 +240,7 @@ class CategoryController extends Controller
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
$data = $this->reportPeriodChart($accounts, $start, $end, null); $data = $this->reportPeriodChart($accounts, $start, $end, null);
$cache->store($data); $cache->store($data);
@@ -255,14 +255,14 @@ class CategoryController extends Controller
*/ */
public function specificPeriod(Category $category, Carbon $date): JsonResponse public function specificPeriod(Category $category, Carbon $date): JsonResponse
{ {
$range = app('navigation')->getViewRange(false); $range = app('navigation')->getViewRange(false);
$start = app('navigation')->startOfPeriod($date, $range); $start = app('navigation')->startOfPeriod($date, $range);
$end = session()->get('end'); $end = session()->get('end');
if ($end < $start) { if ($end < $start) {
[$end, $start] = [$start, $end]; [$end, $start] = [$start, $end];
} }
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($category->id); $cache->addProperty($category->id);

View File

@@ -33,7 +33,6 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\Http\Controllers\GetConfigurationData; use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use FireflyIII\Support\Models\AccountBalanceCalculator; use FireflyIII\Support\Models\AccountBalanceCalculator;
use FireflyIII\User; use FireflyIII\User;
use Http\Discovery\Exception\NotFoundException;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -63,55 +62,59 @@ class DebugController extends Controller
public function routes(): never public function routes(): never
{ {
if(!auth()->user()->hasRole('owner')) { if (!auth()->user()->hasRole('owner')) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
$routes = Route::getRoutes(); $routes = Route::getRoutes();
$return = []; $return = [];
/** @var \Illuminate\Routing\Route $route */ /** @var \Illuminate\Routing\Route $route */
foreach ($routes as $route) { foreach ($routes as $route) {
// skip API and other routes. // skip API and other routes.
if ( if (
str_starts_with($route->uri(), 'api') || str_starts_with($route->uri(), 'api')
str_starts_with($route->uri(), '_debugbar') || || str_starts_with($route->uri(), '_debugbar')
str_starts_with($route->uri(), '_ignition') || || str_starts_with($route->uri(), '_ignition')
str_starts_with($route->uri(), 'oauth') || || str_starts_with($route->uri(), 'oauth')
str_starts_with($route->uri(), 'sanctum') || str_starts_with($route->uri(), 'sanctum')
) { ) {
continue; continue;
} }
// skip non GET routes // skip non GET routes
if (!in_array('GET', $route->methods())) { if (!in_array('GET', $route->methods(), true)) {
continue; continue;
} }
// no name route: // no name route:
if (null === $route->getName()) { if (null === $route->getName()) {
var_dump($route); var_dump($route);
exit; exit;
} }
if (!str_contains($route->uri(), '{')) { if (!str_contains($route->uri(), '{')) {
$return[$route->getName()] = route($route->getName()); $return[$route->getName()] = route($route->getName());
continue; continue;
} }
$params = []; $params = [];
foreach ($route->parameterNames() as $name) { foreach ($route->parameterNames() as $name) {
$params[] = $this->getParameter($name); $params[] = $this->getParameter($name);
} }
$return[$route->getName()] = route($route->getName(), $params); $return[$route->getName()] = route($route->getName(), $params);
} }
$count = 0; $count = 0;
echo '<hr>'; echo '<hr>';
echo '<h1>Routes</h1>'; echo '<h1>Routes</h1>';
echo sprintf('<h2>%s</h2>', $count); echo sprintf('<h2>%s</h2>', $count);
foreach($return as $name => $path) { foreach ($return as $name => $path) {
echo sprintf('<a href="%1$s">%2$s</a><br>', $path, $name) . PHP_EOL; echo sprintf('<a href="%1$s">%2$s</a><br>', $path, $name).PHP_EOL;
$count++; ++$count;
if(0 === $count % 10) { if (0 === $count % 10) {
echo '<hr>'; echo '<hr>';
echo sprintf('<h2>%s</h2>', $count); echo sprintf('<h2>%s</h2>', $count);
} }
} }
exit; exit;
var_dump($return); var_dump($return);
} }
@@ -176,12 +179,12 @@ class DebugController extends Controller
*/ */
public function index() public function index()
{ {
$table = $this->generateTable(); $table = $this->generateTable();
$table = str_replace(["\n", "\t", ' '], '', $table); $table = str_replace(["\n", "\t", ' '], '', $table);
$now = now(config('app.timezone'))->format('Y-m-d H:i:s'); $now = now(config('app.timezone'))->format('Y-m-d H:i:s');
// get latest log file: // get latest log file:
$logger = Log::driver(); $logger = Log::driver();
// PHPstan doesn't recognize the method because of its polymorphic nature. // PHPstan doesn't recognize the method because of its polymorphic nature.
$handlers = $logger->getHandlers(); // @phpstan-ignore-line $handlers = $logger->getHandlers(); // @phpstan-ignore-line
$logContent = ''; $logContent = '';
@@ -195,7 +198,7 @@ class DebugController extends Controller
} }
if ('' !== $logContent) { if ('' !== $logContent) {
// last few lines // last few lines
$logContent = 'Truncated from this point <----|' . substr((string) $logContent, -16384); $logContent = 'Truncated from this point <----|'.substr((string) $logContent, -16384);
} }
return view('debug', compact('table', 'now', 'logContent')); return view('debug', compact('table', 'now', 'logContent'));
@@ -275,7 +278,7 @@ class DebugController extends Controller
private function getAppInfo(): array private function getAppInfo(): array
{ {
$userGuard = config('auth.defaults.guard'); $userGuard = config('auth.defaults.guard');
$config = app('fireflyconfig')->get('last_rt_job', 0); $config = app('fireflyconfig')->get('last_rt_job', 0);
$lastTime = (int) $config->data; $lastTime = (int) $config->data;
@@ -300,24 +303,24 @@ class DebugController extends Controller
// any of the cron jobs will do, they always run at the same time. // any of the cron jobs will do, they always run at the same time.
// but this job is the oldest, so the biggest chance it ran once // but this job is the oldest, so the biggest chance it ran once
'last_cronjob' => $lastCronjob, 'last_cronjob' => $lastCronjob,
'last_cronjob_ago' => $lastCronjobAgo, 'last_cronjob_ago' => $lastCronjobAgo,
]; ];
} }
private function getuserInfo(): array private function getuserInfo(): array
{ {
$userFlags = $this->getUserFlags(); $userFlags = $this->getUserFlags();
// user info // user info
$userAgent = request()->header('user-agent'); $userAgent = request()->header('user-agent');
// set languages, see what happens: // set languages, see what happens:
$original = setlocale(LC_ALL, '0'); $original = setlocale(LC_ALL, '0');
$localeAttempts = []; $localeAttempts = [];
$parts = app('steam')->getLocaleArray(app('steam')->getLocale()); $parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) { foreach ($parts as $code) {
$code = trim($code); $code = trim($code);
app('log')->debug(sprintf('Trying to set %s', $code)); app('log')->debug(sprintf('Trying to set %s', $code));
$result = setlocale(LC_ALL, $code); $result = setlocale(LC_ALL, $code);
$localeAttempts[$code] = $result === $code; $localeAttempts[$code] = $result === $code;
@@ -338,10 +341,10 @@ class DebugController extends Controller
private function getUserFlags(): string private function getUserFlags(): string
{ {
$flags = []; $flags = [];
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
// has liabilities // has liabilities
if ($user->accounts()->accountTypeIn([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->count() > 0) { if ($user->accounts()->accountTypeIn([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->count() > 0) {
@@ -357,7 +360,7 @@ class DebugController extends Controller
} }
// has stored reconciliations // has stored reconciliations
$type = TransactionType::whereType(TransactionType::RECONCILIATION)->first(); $type = TransactionType::whereType(TransactionType::RECONCILIATION)->first();
if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) { if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) {
$flags[] = '<span title="Has reconciled">:ledger:</span>'; $flags[] = '<span title="Has reconciled">:ledger:</span>';
} }
@@ -407,81 +410,118 @@ class DebugController extends Controller
switch ($name) { switch ($name) {
default: default:
throw new FireflyException(sprintf('Unknown parameter "%s"', $name)); throw new FireflyException(sprintf('Unknown parameter "%s"', $name));
case 'cliToken': case 'cliToken':
case 'token': case 'token':
case 'code': case 'code':
case 'oldAddressHash': case 'oldAddressHash':
return 'fake-token'; return 'fake-token';
case 'objectType': case 'objectType':
return 'asset'; return 'asset';
case 'account': case 'account':
return '1'; return '1';
case 'start_date': case 'start_date':
return '20241201'; return '20241201';
case 'end_date': case 'end_date':
return '20241231'; return '20241231';
case 'attachment': case 'attachment':
return '1'; return '1';
case 'bill': case 'bill':
return '1'; return '1';
case 'budget': case 'budget':
return '1'; return '1';
case 'budgetLimit': case 'budgetLimit':
return '1'; return '1';
case 'category': case 'category':
return '1'; return '1';
case 'currency': case 'currency':
return '1'; return '1';
case 'fromCurrencyCode': case 'fromCurrencyCode':
return 'EUR'; return 'EUR';
case 'toCurrencyCode': case 'toCurrencyCode':
return 'USD'; return 'USD';
case 'accountList': case 'accountList':
return '1,6'; return '1,6';
case 'budgetList': case 'budgetList':
return '1,2'; return '1,2';
case 'categoryList': case 'categoryList':
return '1,2'; return '1,2';
case 'doubleList': case 'doubleList':
return '1,2'; return '1,2';
case 'tagList': case 'tagList':
return '1,2'; return '1,2';
case 'tag': case 'tag':
return '1'; return '1';
case 'piggyBank': case 'piggyBank':
return '1'; return '1';
case 'objectGroup': case 'objectGroup':
return '1'; return '1';
case 'route': case 'route':
return 'accounts'; return 'accounts';
case 'specificPage': case 'specificPage':
return 'show'; return 'show';
case 'recurrence': case 'recurrence':
return '1'; return '1';
case 'tj': case 'tj':
return '1'; return '1';
case 'reportType': case 'reportType':
return 'default'; return 'default';
case 'ruleGroup': case 'ruleGroup':
return '1'; return '1';
case 'rule': case 'rule':
return '1'; return '1';
case 'tagOrId': case 'tagOrId':
return '1'; return '1';
case 'transactionGroup': case 'transactionGroup':
return '1'; return '1';
case 'journalList': case 'journalList':
return '1,2'; return '1,2';
case 'transactionType': case 'transactionType':
return 'withdrawal'; return 'withdrawal';
case 'journalLink': case 'journalLink':
return '1'; return '1';
case 'webhook': case 'webhook':
return '1'; return '1';
case 'user': case 'user':
return '1'; return '1';
case 'linkType': case 'linkType':
return '1'; return '1';
case 'userGroup': case 'userGroup':
return '1'; return '1';

View File

@@ -62,31 +62,32 @@ class BoxController extends Controller
{ {
// Cache result, return cache if present. // Cache result, return cache if present.
/** @var Carbon $start */ /** @var Carbon $start */
$start = session('start', today(config('app.timezone'))->startOfMonth()); $start = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth()); $end = session('end', today(config('app.timezone'))->endOfMonth());
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
$cache->addProperty('box-balance'); $cache->addProperty('box-balance');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
// prep some arrays: // prep some arrays:
$incomes = []; $incomes = [];
$expenses = []; $expenses = [];
$sums = []; $sums = [];
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
// collect income of user: // collect income of user:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end) $collector->setRange($start, $end)
->setTypes([TransactionType::DEPOSIT]); ->setTypes([TransactionType::DEPOSIT])
$set = $collector->getExtractedJournals(); ;
$set = $collector->getExtractedJournals();
/** @var array $journal */ /** @var array $journal */
foreach ($set as $journal) { foreach ($set as $journal) {
@@ -102,8 +103,9 @@ class BoxController extends Controller
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end) $collector->setRange($start, $end)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); ->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
$set = $collector->getExtractedJournals(); ;
$set = $collector->getExtractedJournals();
/** @var array $journal */ /** @var array $journal */
foreach ($set as $journal) { foreach ($set as $journal) {
@@ -116,7 +118,7 @@ class BoxController extends Controller
} }
// format amounts: // format amounts:
$keys = array_keys($sums); $keys = array_keys($sums);
foreach ($keys as $currencyId) { foreach ($keys as $currencyId) {
$currency = $repository->find($currencyId); $currency = $repository->find($currencyId);
$sums[$currencyId] = app('amount')->formatAnything($currency, $sums[$currencyId], false); $sums[$currencyId] = app('amount')->formatAnything($currency, $sums[$currencyId], false);
@@ -130,7 +132,7 @@ class BoxController extends Controller
$expenses[$currency->id] = app('amount')->formatAnything($currency, '0', false); $expenses[$currency->id] = app('amount')->formatAnything($currency, '0', false);
} }
$response = [ $response = [
'incomes' => $incomes, 'incomes' => $incomes,
'expenses' => $expenses, 'expenses' => $expenses,
'sums' => $sums, 'sums' => $sums,
@@ -147,7 +149,7 @@ class BoxController extends Controller
*/ */
public function netWorth(): JsonResponse public function netWorth(): JsonResponse
{ {
$date = today(config('app.timezone'))->endOfDay(); $date = today(config('app.timezone'))->endOfDay();
// start and end in the future? use $end // start and end in the future? use $end
if ($this->notInSessionRange($date)) { if ($this->notInSessionRange($date)) {
@@ -156,7 +158,7 @@ class BoxController extends Controller
} }
/** @var NetWorthInterface $netWorthHelper */ /** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class); $netWorthHelper = app(NetWorthInterface::class);
$netWorthHelper->setUser(auth()->user()); $netWorthHelper->setUser(auth()->user());
/** @var AccountRepositoryInterface $accountRepository */ /** @var AccountRepositoryInterface $accountRepository */
@@ -167,7 +169,7 @@ class BoxController extends Controller
app('log')->debug(sprintf('Found %d accounts.', $allAccounts->count())); app('log')->debug(sprintf('Found %d accounts.', $allAccounts->count()));
// filter list on preference of being included. // filter list on preference of being included.
$filtered = $allAccounts->filter( $filtered = $allAccounts->filter(
static function (Account $account) use ($accountRepository) { static function (Account $account) use ($accountRepository) {
$includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth'); $includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth');
$result = null === $includeNetWorth ? true : '1' === $includeNetWorth; $result = null === $includeNetWorth ? true : '1' === $includeNetWorth;
@@ -179,15 +181,15 @@ class BoxController extends Controller
} }
); );
$netWorthSet = $netWorthHelper->byAccounts($filtered, $date); $netWorthSet = $netWorthHelper->byAccounts($filtered, $date);
$return = []; $return = [];
foreach ($netWorthSet as $key => $data) { foreach ($netWorthSet as $key => $data) {
if ('native' === $key) { if ('native' === $key) {
continue; continue;
} }
$return[$data['currency_id']] = app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false); $return[$data['currency_id']] = app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false);
} }
$return = [ $return = [
'net_worths' => array_values($return), 'net_worths' => array_values($return),
]; ];

View File

@@ -61,7 +61,7 @@ class Account extends Model
'virtual_balance' => 'string', 'virtual_balance' => 'string',
]; ];
protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban','native_virtual_balance']; protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance'];
protected $hidden = ['encrypted']; protected $hidden = ['encrypted'];
private bool $joinedAccountTypes = false; private bool $joinedAccountTypes = false;

View File

@@ -57,7 +57,7 @@ class BudgetLimit extends Model
'deleted' => Deleted::class, 'deleted' => Deleted::class,
]; ];
protected $fillable = ['budget_id', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'amount', 'transaction_currency_id','native_amount']; protected $fillable = ['budget_id', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'amount', 'transaction_currency_id', 'native_amount'];
/** /**
* Route binder. Converts the key in the URL to the specified object (or throw 404). * Route binder. Converts the key in the URL to the specified object (or throw 404).

View File

@@ -61,7 +61,8 @@ class BillRepository implements BillRepositoryInterface
$search->whereLike('name', sprintf('%%%s', $query)); $search->whereLike('name', sprintf('%%%s', $query));
} }
$search->orderBy('name', 'ASC') $search->orderBy('name', 'ASC')
->where('active', true); ->where('active', true)
;
return $search->take($limit)->get(); return $search->take($limit)->get();
} }
@@ -73,7 +74,8 @@ class BillRepository implements BillRepositoryInterface
$search->whereLike('name', sprintf('%s%%', $query)); $search->whereLike('name', sprintf('%s%%', $query));
} }
$search->orderBy('name', 'ASC') $search->orderBy('name', 'ASC')
->where('active', true); ->where('active', true)
;
return $search->take($limit)->get(); return $search->take($limit)->get();
} }
@@ -156,7 +158,7 @@ class BillRepository implements BillRepositoryInterface
*/ */
public function getAttachments(Bill $bill): Collection public function getAttachments(Bill $bill): Collection
{ {
$set = $bill->attachments()->get(); $set = $bill->attachments()->get();
/** @var \Storage $disk */ /** @var \Storage $disk */
$disk = \Storage::disk('upload'); $disk = \Storage::disk('upload');
@@ -175,9 +177,10 @@ class BillRepository implements BillRepositoryInterface
public function getBills(): Collection public function getBills(): Collection
{ {
return $this->user->bills() return $this->user->bills()
->orderBy('order', 'ASC') ->orderBy('order', 'ASC')
->orderBy('active', 'DESC') ->orderBy('active', 'DESC')
->orderBy('name', 'ASC')->get(); ->orderBy('name', 'ASC')->get()
;
} }
public function getBillsForAccounts(Collection $accounts): Collection public function getBillsForAccounts(Collection $accounts): Collection
@@ -201,24 +204,25 @@ class BillRepository implements BillRepositoryInterface
$ids = $accounts->pluck('id')->toArray(); $ids = $accounts->pluck('id')->toArray();
return $this->user->bills() return $this->user->bills()
->leftJoin( ->leftJoin(
'transaction_journals', 'transaction_journals',
static function (JoinClause $join): void { static function (JoinClause $join): void {
$join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at'); $join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at');
} }
) )
->leftJoin( ->leftJoin(
'transactions', 'transactions',
static function (JoinClause $join): void { static function (JoinClause $join): void {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
} }
) )
->whereIn('transactions.account_id', $ids) ->whereIn('transactions.account_id', $ids)
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->orderBy('bills.active', 'DESC') ->orderBy('bills.active', 'DESC')
->orderBy('bills.name', 'ASC') ->orderBy('bills.name', 'ASC')
->groupBy($fields) ->groupBy($fields)
->get($fields); ->get($fields)
;
} }
/** /**
@@ -243,7 +247,7 @@ class BillRepository implements BillRepositoryInterface
public function getOverallAverage(Bill $bill): array public function getOverallAverage(Bill $bill): array
{ {
/** @var JournalRepositoryInterface $repos */ /** @var JournalRepositoryInterface $repos */
$repos = app(JournalRepositoryInterface::class); $repos = app(JournalRepositoryInterface::class);
$repos->setUser($this->user); $repos->setUser($this->user);
// get and sort on currency // get and sort on currency
@@ -256,7 +260,7 @@ class BillRepository implements BillRepositoryInterface
$transaction = $journal->transactions()->where('amount', '<', 0)->first(); $transaction = $journal->transactions()->where('amount', '<', 0)->first();
$currencyId = (int) $journal->transaction_currency_id; $currencyId = (int) $journal->transaction_currency_id;
$currency = $journal->transactionCurrency; $currency = $journal->transactionCurrency;
$result[$currencyId] ??= [ $result[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'count' => 0, 'count' => 0,
'avg' => '0', 'avg' => '0',
@@ -281,7 +285,7 @@ class BillRepository implements BillRepositoryInterface
return $result; return $result;
} }
public function setUser(null | Authenticatable | User $user): void public function setUser(null|Authenticatable|User $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;
@@ -291,8 +295,9 @@ class BillRepository implements BillRepositoryInterface
public function getPaginator(int $size): LengthAwarePaginator public function getPaginator(int $size): LengthAwarePaginator
{ {
return $this->user->bills() return $this->user->bills()
->orderBy('active', 'DESC') ->orderBy('active', 'DESC')
->orderBy('name', 'ASC')->paginate($size); ->orderBy('name', 'ASC')->paginate($size)
;
} }
/** /**
@@ -305,13 +310,14 @@ class BillRepository implements BillRepositoryInterface
Log::debug(sprintf('Search for linked journals between %s and %s', $start->toW3cString(), $end->toW3cString())); Log::debug(sprintf('Search for linked journals between %s and %s', $start->toW3cString(), $end->toW3cString()));
return $bill->transactionJournals() return $bill->transactionJournals()
->before($end)->after($start)->get( ->before($end)->after($start)->get(
[ [
'transaction_journals.id', 'transaction_journals.id',
'transaction_journals.date', 'transaction_journals.date',
'transaction_journals.transaction_group_id', 'transaction_journals.transaction_group_id',
] ]
); )
;
} }
/** /**
@@ -320,10 +326,11 @@ class BillRepository implements BillRepositoryInterface
public function getRulesForBill(Bill $bill): Collection public function getRulesForBill(Bill $bill): Collection
{ {
return $this->user->rules() return $this->user->rules()
->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id')
->where('rule_actions.action_type', 'link_to_bill') ->where('rule_actions.action_type', 'link_to_bill')
->where('rule_actions.action_value', $bill->name) ->where('rule_actions.action_value', $bill->name)
->get(['rules.*']); ->get(['rules.*'])
;
} }
/** /**
@@ -334,15 +341,16 @@ class BillRepository implements BillRepositoryInterface
*/ */
public function getRulesForBills(Collection $collection): array public function getRulesForBills(Collection $collection): array
{ {
$rules = $this->user->rules() $rules = $this->user->rules()
->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id')
->where('rule_actions.action_type', 'link_to_bill') ->where('rule_actions.action_type', 'link_to_bill')
->get(['rules.id', 'rules.title', 'rule_actions.action_value', 'rules.active']); ->get(['rules.id', 'rules.title', 'rule_actions.action_value', 'rules.active'])
$array = []; ;
$array = [];
/** @var Rule $rule */ /** @var Rule $rule */
foreach ($rules as $rule) { foreach ($rules as $rule) {
$array[$rule->action_value] ??= []; $array[$rule->action_value] ??= [];
$array[$rule->action_value][] = ['id' => $rule->id, 'title' => $rule->title, 'active' => $rule->active]; $array[$rule->action_value][] = ['id' => $rule->id, 'title' => $rule->title, 'active' => $rule->active];
} }
$return = []; $return = [];
@@ -356,27 +364,28 @@ class BillRepository implements BillRepositoryInterface
public function getYearAverage(Bill $bill, Carbon $date): array public function getYearAverage(Bill $bill, Carbon $date): array
{ {
/** @var JournalRepositoryInterface $repos */ /** @var JournalRepositoryInterface $repos */
$repos = app(JournalRepositoryInterface::class); $repos = app(JournalRepositoryInterface::class);
$repos->setUser($this->user); $repos->setUser($this->user);
// get and sort on currency // get and sort on currency
$result = []; $result = [];
$journals = $bill->transactionJournals() $journals = $bill->transactionJournals()
->where('date', '>=', $date->year . '-01-01 00:00:00') ->where('date', '>=', $date->year.'-01-01 00:00:00')
->where('date', '<=', $date->year . '-12-31 23:59:59') ->where('date', '<=', $date->year.'-12-31 23:59:59')
->get(); ->get()
;
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
/** @var null|Transaction $transaction */ /** @var null|Transaction $transaction */
$transaction = $journal->transactions()->where('amount', '<', 0)->first(); $transaction = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $transaction) { if (null === $transaction) {
continue; continue;
} }
$currencyId = (int) $journal->transaction_currency_id; $currencyId = (int) $journal->transaction_currency_id;
$currency = $journal->transactionCurrency; $currency = $journal->transactionCurrency;
$result[$currencyId] ??= [ $result[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'count' => 0, 'count' => 0,
'avg' => '0', 'avg' => '0',
@@ -420,7 +429,7 @@ class BillRepository implements BillRepositoryInterface
*/ */
public function nextExpectedMatch(Bill $bill, Carbon $date): Carbon public function nextExpectedMatch(Bill $bill, Carbon $date): Carbon
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($bill->id); $cache->addProperty($bill->id);
$cache->addProperty('nextExpectedMatch'); $cache->addProperty('nextExpectedMatch');
$cache->addProperty($date); $cache->addProperty($date);
@@ -428,17 +437,17 @@ class BillRepository implements BillRepositoryInterface
return $cache->get(); return $cache->get();
} }
// find the most recent date for this bill NOT in the future. Cache this date: // find the most recent date for this bill NOT in the future. Cache this date:
$start = clone $bill->date; $start = clone $bill->date;
$start->startOfDay(); $start->startOfDay();
app('log')->debug('nextExpectedMatch: Start is ' . $start->format('Y-m-d')); app('log')->debug('nextExpectedMatch: Start is '.$start->format('Y-m-d'));
while ($start < $date) { while ($start < $date) {
app('log')->debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d H:i:s'), $date->format('Y-m-d H:i:s'))); app('log')->debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d H:i:s'), $date->format('Y-m-d H:i:s')));
$start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
app('log')->debug('Start is now ' . $start->format('Y-m-d H:i:s')); app('log')->debug('Start is now '.$start->format('Y-m-d H:i:s'));
} }
$end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
$end->endOfDay(); $end->endOfDay();
// see if the bill was paid in this period. // see if the bill was paid in this period.
@@ -450,8 +459,8 @@ class BillRepository implements BillRepositoryInterface
$start = clone $end; $start = clone $end;
$end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
} }
app('log')->debug('nextExpectedMatch: Final start is ' . $start->format('Y-m-d')); app('log')->debug('nextExpectedMatch: Final start is '.$start->format('Y-m-d'));
app('log')->debug('nextExpectedMatch: Matching end is ' . $end->format('Y-m-d')); app('log')->debug('nextExpectedMatch: Matching end is '.$end->format('Y-m-d'));
$cache->store($start); $cache->store($start);
@@ -512,8 +521,8 @@ class BillRepository implements BillRepositoryInterface
foreach ($bills as $bill) { foreach ($bills as $bill) {
/** @var Collection $set */ /** @var Collection $set */
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); $set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
$currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; $currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency;
$return[(int) $currency->id] ??= [ $return[(int) $currency->id] ??= [
'id' => (string) $currency->id, 'id' => (string) $currency->id,
'name' => $currency->name, 'name' => $currency->name,
@@ -522,7 +531,8 @@ class BillRepository implements BillRepositoryInterface
'decimal_places' => $currency->decimal_places, 'decimal_places' => $currency->decimal_places,
'sum' => '0', 'sum' => '0',
]; ];
$setAmount = '0'; $setAmount = '0';
/** @var TransactionJournal $transactionJournal */ /** @var TransactionJournal $transactionJournal */
foreach ($set as $transactionJournal) { foreach ($set as $transactionJournal) {
$setAmount = bcadd($setAmount, Amount::getAmountFromJournalObject($transactionJournal)); $setAmount = bcadd($setAmount, Amount::getAmountFromJournalObject($transactionJournal));
@@ -538,10 +548,10 @@ class BillRepository implements BillRepositoryInterface
public function getActiveBills(): Collection public function getActiveBills(): Collection
{ {
return $this->user->bills() return $this->user->bills()
->where('active', true) ->where('active', true)
->orderBy('bills.name', 'ASC') ->orderBy('bills.name', 'ASC')
->get(['bills.*', \DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount')]) // @phpstan-ignore-line ->get(['bills.*', \DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount')]) // @phpstan-ignore-line
; ;
} }
public function sumUnpaidInRange(Carbon $start, Carbon $end): array public function sumUnpaidInRange(Carbon $start, Carbon $end): array
@@ -555,21 +565,21 @@ class BillRepository implements BillRepositoryInterface
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
// app('log')->debug(sprintf('Processing bill #%d ("%s")', $bill->id, $bill->name)); // app('log')->debug(sprintf('Processing bill #%d ("%s")', $bill->id, $bill->name));
$dates = $this->getPayDatesInRange($bill, $start, $end); $dates = $this->getPayDatesInRange($bill, $start, $end);
$count = $bill->transactionJournals()->after($start)->before($end)->count(); $count = $bill->transactionJournals()->after($start)->before($end)->count();
$total = $dates->count() - $count; $total = $dates->count() - $count;
// app('log')->debug(sprintf('Pay dates: %d, count: %d, left: %d', $dates->count(), $count, $total)); // app('log')->debug(sprintf('Pay dates: %d, count: %d, left: %d', $dates->count(), $count, $total));
// app('log')->debug('dates', $dates->toArray()); // app('log')->debug('dates', $dates->toArray());
$minField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_min' : 'amount_min'; $minField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_min' : 'amount_min';
$maxField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_max' : 'amount_max'; $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)); // Log::debug(sprintf('min field is %s, max field is %s', $minField, $maxField));
if ($total > 0) { if ($total > 0) {
$currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; $currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency;
$average = bcdiv(bcadd($bill->$maxField, $bill->$minField), '2'); $average = bcdiv(bcadd($bill->{$maxField}, $bill->{$minField}), '2');
Log::debug(sprintf('Amount to pay is %s %s (%d times)', $currency->code, $average, $total)); Log::debug(sprintf('Amount to pay is %s %s (%d times)', $currency->code, $average, $total));
$return[$currency->id] ??= [ $return[$currency->id] ??= [
'id' => (string) $currency->id, 'id' => (string) $currency->id,
'name' => $currency->name, 'name' => $currency->name,
'symbol' => $currency->symbol, 'symbol' => $currency->symbol,
@@ -607,7 +617,7 @@ class BillRepository implements BillRepositoryInterface
// app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); // app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
$currentStart = clone $nextExpectedMatch; $currentStart = clone $nextExpectedMatch;
} }
return $set; return $set;

View File

@@ -47,9 +47,9 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
/** @var AvailableBudget $availableBudget */ /** @var AvailableBudget $availableBudget */
foreach ($availableBudgets as $availableBudget) { foreach ($availableBudgets as $availableBudget) {
$start = $availableBudget->start_date->format('Y-m-d'); $start = $availableBudget->start_date->format('Y-m-d');
$end = $availableBudget->end_date->format('Y-m-d'); $end = $availableBudget->end_date->format('Y-m-d');
$key = sprintf('%s-%s-%s', $availableBudget->transaction_currency_id, $start, $end); $key = sprintf('%s-%s-%s', $availableBudget->transaction_currency_id, $start, $end);
if (array_key_exists($key, $exists)) { if (array_key_exists($key, $exists)) {
app('log')->debug(sprintf('Found duplicate AB: %s %s, %s-%s. Has been deleted', $availableBudget->transaction_currency_id, $availableBudget->amount, $start, $end)); app('log')->debug(sprintf('Found duplicate AB: %s %s, %s-%s. Has been deleted', $availableBudget->transaction_currency_id, $availableBudget->amount, $start, $end));
$availableBudget->delete(); $availableBudget->delete();
@@ -101,21 +101,23 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?AvailableBudget public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?AvailableBudget
{ {
return $this->user->availableBudgets() return $this->user->availableBudgets()
->where('transaction_currency_id', $currency->id) ->where('transaction_currency_id', $currency->id)
->where('start_date', $start->format('Y-m-d')) ->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d')) ->where('end_date', $end->format('Y-m-d'))
->first(); ->first()
;
} }
public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string
{ {
$amount = '0'; $amount = '0';
/** @var null|AvailableBudget $availableBudget */ /** @var null|AvailableBudget $availableBudget */
$availableBudget = $this->user->availableBudgets() $availableBudget = $this->user->availableBudgets()
->where('transaction_currency_id', $currency->id) ->where('transaction_currency_id', $currency->id)
->where('start_date', $start->format('Y-m-d')) ->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->first(); ->where('end_date', $end->format('Y-m-d'))->first()
;
if (null !== $availableBudget) { if (null !== $availableBudget) {
$amount = $availableBudget->amount; $amount = $availableBudget->amount;
} }
@@ -127,20 +129,22 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
{ {
$return = []; $return = [];
$availableBudgets = $this->user->availableBudgets() $availableBudgets = $this->user->availableBudgets()
->where('start_date', $start->format('Y-m-d')) ->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->get(); ->where('end_date', $end->format('Y-m-d'))->get()
;
// use native amount if necessary? // use native amount if necessary?
$convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data; $convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getDefaultCurrency();
/** @var AvailableBudget $availableBudget */ /** @var AvailableBudget $availableBudget */
foreach ($availableBudgets as $availableBudget) { foreach ($availableBudgets as $availableBudget) {
$currencyId = $convertToNative && $availableBudget->transaction_currency_id !== $default->id ? $default->id : $availableBudget->transaction_currency_id; $currencyId = $convertToNative && $availableBudget->transaction_currency_id !== $default->id ? $default->id : $availableBudget->transaction_currency_id;
$field = $convertToNative && $availableBudget->transaction_currency_id !== $default->id ? 'native_amount' : 'amount'; $field = $convertToNative && $availableBudget->transaction_currency_id !== $default->id ? 'native_amount' : 'amount';
$return[$currencyId] = $return[$currencyId] ?? '0'; $return[$currencyId] ??= '0';
$return[$currencyId] = bcadd($return[$currencyId], $availableBudget->$field); $return[$currencyId] = bcadd($return[$currencyId], $availableBudget->{$field});
} }
return $return; return $return;
} }
@@ -175,9 +179,10 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection
{ {
return $this->user->availableBudgets() return $this->user->availableBudgets()
->where('start_date', '=', $start->format('Y-m-d')) ->where('start_date', '=', $start->format('Y-m-d'))
->where('end_date', '=', $end->format('Y-m-d')) ->where('end_date', '=', $end->format('Y-m-d'))
->get(); ->get()
;
} }
public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget
@@ -186,7 +191,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
->availableBudgets() ->availableBudgets()
->where('transaction_currency_id', $currency->id) ->where('transaction_currency_id', $currency->id)
->where('start_date', $start->format('Y-m-d')) ->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->first(); ->where('end_date', $end->format('Y-m-d'))->first()
;
} }
/** /**
@@ -194,12 +200,13 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
*/ */
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget
{ {
$availableBudget = $this->user->availableBudgets() $availableBudget = $this->user->availableBudgets()
->where('transaction_currency_id', $currency->id) ->where('transaction_currency_id', $currency->id)
->where('start_date', $start->format('Y-m-d')) ->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->first(); ->where('end_date', $end->format('Y-m-d'))->first()
;
if (null === $availableBudget) { if (null === $availableBudget) {
$availableBudget = new AvailableBudget(); $availableBudget = new AvailableBudget();
$availableBudget->user()->associate($this->user); $availableBudget->user()->associate($this->user);
$availableBudget->transactionCurrency()->associate($currency); $availableBudget->transactionCurrency()->associate($currency);
$availableBudget->start_date = $start->startOfDay()->format('Y-m-d'); // @phpstan-ignore-line $availableBudget->start_date = $start->startOfDay()->format('Y-m-d'); // @phpstan-ignore-line
@@ -213,7 +220,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
return $availableBudget; return $availableBudget;
} }
public function setUser(null | Authenticatable | User $user): void public function setUser(null|Authenticatable|User $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;
@@ -226,7 +233,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
if ($start instanceof Carbon) { if ($start instanceof Carbon) {
$start = $data['start']->startOfDay(); $start = $data['start']->startOfDay();
} }
$end = $data['end']; $end = $data['end'];
if ($end instanceof Carbon) { if ($end instanceof Carbon) {
$end = $data['end']->endOfDay(); $end = $data['end']->endOfDay();
} }

View File

@@ -63,7 +63,7 @@ class OperationsRepository implements OperationsRepositoryInterface
++$count; ++$count;
app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total));
} }
$avg = $total; $avg = $total;
if ($count > 0) { if ($count > 0) {
$avg = bcdiv($total, (string) $count); $avg = bcdiv($total, (string) $count);
} }
@@ -85,21 +85,21 @@ class OperationsRepository implements OperationsRepositoryInterface
// get all transactions: // get all transactions:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end); $collector->setAccounts($accounts)->setRange($start, $end);
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// loop transactions: // loop transactions:
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
// prep data array for currency: // prep data array for currency:
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$budgetName = $journal['budget_name']; $budgetName = $journal['budget_name'];
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$key = sprintf('%d-%d', $budgetId, $currencyId); $key = sprintf('%d-%d', $budgetId, $currencyId);
$data[$key] ??= [ $data[$key] ??= [
'id' => $budgetId, 'id' => $budgetId,
'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']), 'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']),
'sum' => '0', 'sum' => '0',
@@ -137,13 +137,13 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setBudgets($this->getBudgets()); $collector->setBudgets($this->getBudgets());
} }
$collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation(); $collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$budgetName = (string) $journal['budget_name']; $budgetName = (string) $journal['budget_name'];
// catch "no category" entries. // catch "no category" entries.
if (0 === $budgetId) { if (0 === $budgetId) {
@@ -151,7 +151,7 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'budgets' => [], 'budgets' => [],
'currency_id' => $currencyId, 'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -186,7 +186,7 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array; return $array;
} }
public function setUser(null | Authenticatable | User $user): void public function setUser(null|Authenticatable|User $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;
@@ -210,8 +210,7 @@ class OperationsRepository implements OperationsRepositoryInterface
?Collection $accounts = null, ?Collection $accounts = null,
?Collection $budgets = null, ?Collection $budgets = null,
?TransactionCurrency $currency = null ?TransactionCurrency $currency = null
): array ): array {
{
Log::debug('Start of sumExpenses.'); Log::debug('Start of sumExpenses.');
// this collector excludes all transfers TO liabilities (which are also withdrawals) // 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. // because those expenses only become expenses once they move from the liability to the friend.
@@ -219,10 +218,10 @@ class OperationsRepository implements OperationsRepositoryInterface
// 2024-12-24 disable the exclusion for now. // 2024-12-24 disable the exclusion for now.
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities'));
$selection = new Collection(); $selection = new Collection();
// default currency information for native stuff. // default currency information for native stuff.
$convertToNative = app('preferences')->get('convert_to_native', false)->data; $convertToNative = app('preferences')->get('convert_to_native', false)->data;
@@ -236,11 +235,12 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user) $collector->setUser($this->user)
->setRange($start, $end) ->setRange($start, $end)
// ->excludeDestinationAccounts($selection) // ->excludeDestinationAccounts($selection)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); ->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
if (null !== $accounts) { if (null !== $accounts) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -253,46 +253,46 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setNormalCurrency($currency); $collector->setNormalCurrency($currency);
} }
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// same but for transactions in the foreign currency: // same but for transactions in the foreign currency:
if (null !== $currency) { if (null !== $currency) {
Log::debug('STOP looking for transactions in the foreign currency.'); Log::debug('STOP looking for transactions in the foreign currency.');
// Log::debug(sprintf('Look for transactions with foreign currency %s', $currency->code)); // Log::debug(sprintf('Look for transactions with foreign currency %s', $currency->code));
// // app('log')->debug(sprintf('Currency is "%s".', $currency->name)); // // app('log')->debug(sprintf('Currency is "%s".', $currency->name));
// /** @var GroupCollectorInterface $collector */ // /** @var GroupCollectorInterface $collector */
// $collector = app(GroupCollectorInterface::class); // $collector = app(GroupCollectorInterface::class);
// $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setForeignCurrency($currency)->setBudgets($budgets); // $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setForeignCurrency($currency)->setBudgets($budgets);
// //
// if (null !== $accounts) { // if (null !== $accounts) {
// $collector->setAccounts($accounts); // $collector->setAccounts($accounts);
// } // }
// $result = $collector->getExtractedJournals(); // $result = $collector->getExtractedJournals();
// // app('log')->debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code)); // // app('log')->debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code));
// // do not use array_merge because you want keys to overwrite (otherwise you get double results): // // do not use array_merge because you want keys to overwrite (otherwise you get double results):
// Log::debug(sprintf('Found %d extra journals in foreign currency.', count($result))); // Log::debug(sprintf('Found %d extra journals in foreign currency.', count($result)));
// $journals = $result + $journals; // $journals = $result + $journals;
} }
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
// Log::debug(sprintf('Journal #%d.', $journal['transaction_journal_id'])); // Log::debug(sprintf('Journal #%d.', $journal['transaction_journal_id']));
// Log::debug(sprintf('Amounts: %1$s %2$s (amount), %3$s %4$s (foreign_amount), %5$s %6$s (native_amount) %5$s %7$s (foreign native amount)', // Log::debug(sprintf('Amounts: %1$s %2$s (amount), %3$s %4$s (foreign_amount), %5$s %6$s (native_amount) %5$s %7$s (foreign native amount)',
// $journal['currency_code'], $journal['amount'], $journal['foreign_currency_code'], $journal['foreign_amount'], // $journal['currency_code'], $journal['amount'], $journal['foreign_currency_code'], $journal['foreign_amount'],
// $default->code, $journal['native_amount'], $journal['native_foreign_amount']) // $default->code, $journal['native_amount'], $journal['native_foreign_amount'])
// ); // );
// TODO same as in category::sumexpenses // TODO same as in category::sumexpenses
$amount = '0'; $amount = '0';
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol']; $currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code']; $currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places']; $currencyDecimalPlaces = $journal['currency_decimal_places'];
if ($convertToNative) { if ($convertToNative) {
$useNative = $default->id !== (int) $journal['currency_id']; $useNative = $default->id !== (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal); $amount = Amount::getAmountFromJournal($journal);
if($useNative) { if ($useNative) {
Log::debug(sprintf('Journal #%d switches to native amount (original is %s)', $journal['transaction_journal_id'], $journal['currency_code'])); Log::debug(sprintf('Journal #%d switches to native amount (original is %s)', $journal['transaction_journal_id'], $journal['currency_code']));
$currencyId = $default->id; $currencyId = $default->id;
$currencyName = $default->name; $currencyName = $default->name;
@@ -302,12 +302,12 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
} }
if (!$convertToNative) { if (!$convertToNative) {
$amount = $journal['amount']; $amount = $journal['amount'];
// if the amount is not in $currency (but should be), use the foreign_amount if that one is correct. // if the amount is not in $currency (but should be), use the foreign_amount if that one is correct.
// otherwise, ignore the transaction all together. // otherwise, ignore the transaction all together.
if (null !== $currency && $currencyId !== $currency->id && $currency->id === (int) $journal['foreign_currency_id']) { if (null !== $currency && $currencyId !== $currency->id && $currency->id === (int) $journal['foreign_currency_id']) {
Log::debug(sprintf('Journal #%d switches to foreign amount because it matches native.', $journal['transaction_journal_id'])); Log::debug(sprintf('Journal #%d switches to foreign amount because it matches native.', $journal['transaction_journal_id']));
$amount = $journal['foreign_amount']; $amount = $journal['foreign_amount'];
$currencyId = (int) $journal['foreign_currency_id']; $currencyId = (int) $journal['foreign_currency_id'];
$currencyName = $journal['foreign_currency_name']; $currencyName = $journal['foreign_currency_name'];
$currencySymbol = $journal['foreign_currency_symbol']; $currencySymbol = $journal['foreign_currency_symbol'];
@@ -315,7 +315,7 @@ class OperationsRepository implements OperationsRepositoryInterface
$currencyDecimalPlaces = $journal['foreign_currency_decimal_places']; $currencyDecimalPlaces = $journal['foreign_currency_decimal_places'];
} }
} }
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => $currencyId, 'currency_id' => $currencyId,
'currency_name' => $currencyName, 'currency_name' => $currencyName,
@@ -327,6 +327,7 @@ class OperationsRepository implements OperationsRepositoryInterface
Log::debug(sprintf('Journal #%d adds amount %s %s', $journal['transaction_journal_id'], $currencyCode, $amount)); Log::debug(sprintf('Journal #%d adds amount %s %s', $journal['transaction_journal_id'], $currencyCode, $amount));
} }
Log::debug('End of sumExpenses.', $array); Log::debug('End of sumExpenses.', $array);
return $array; return $array;
} }
} }

View File

@@ -145,26 +145,26 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array
{ {
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory();
if (null !== $accounts && $accounts->count() > 0) { if (null !== $accounts && $accounts->count() > 0) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
} }
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
// default currency information for native stuff. // default currency information for native stuff.
$convertToNative = app('preferences')->get('convert_to_native', false)->data; $convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getDefaultCurrency();
foreach ($journals as $journal) { foreach ($journals as $journal) {
// Almost the same as in \FireflyIII\Repositories\Budget\OperationsRepository::sumExpenses // Almost the same as in \FireflyIII\Repositories\Budget\OperationsRepository::sumExpenses
$amount = '0'; $amount = '0';
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol']; $currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code']; $currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places']; $currencyDecimalPlaces = $journal['currency_decimal_places'];
if ($convertToNative) { if ($convertToNative) {
$useNative = $default->id !== (int) $journal['currency_id']; $useNative = $default->id !== (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal); $amount = Amount::getAmountFromJournal($journal);
@@ -184,7 +184,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface
} }
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $currencyName, 'currency_name' => $currencyName,

View File

@@ -63,13 +63,13 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setCategories($this->getCategories()); $collector->setCategories($this->getCategories());
} }
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$categoryId = (int) $journal['category_id']; $categoryId = (int) $journal['category_id'];
$categoryName = (string) $journal['category_name']; $categoryName = (string) $journal['category_name'];
// catch "no category" entries. // catch "no category" entries.
if (0 === $categoryId) { if (0 === $categoryId) {
@@ -77,7 +77,7 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'categories' => [], 'categories' => [],
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -112,7 +112,7 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array; return $array;
} }
public function setUser(null | Authenticatable | User $user): void public function setUser(null|Authenticatable|User $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;
@@ -147,13 +147,13 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setCategories($this->getCategories()); $collector->setCategories($this->getCategories());
} }
$collector->withCategoryInformation()->withAccountInformation(); $collector->withCategoryInformation()->withAccountInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$categoryId = (int) $journal['category_id']; $categoryId = (int) $journal['category_id'];
$categoryName = (string) $journal['category_name']; $categoryName = (string) $journal['category_name'];
// catch "no category" entries. // catch "no category" entries.
if (0 === $categoryId) { if (0 === $categoryId) {
@@ -161,7 +161,7 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'categories' => [], 'categories' => [],
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -200,7 +200,8 @@ class OperationsRepository implements OperationsRepositoryInterface
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]) $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts); ->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts)
;
if (null !== $categories && $categories->count() > 0) { if (null !== $categories && $categories->count() > 0) {
$collector->setCategories($categories); $collector->setCategories($categories);
} }
@@ -208,13 +209,13 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setCategories($this->getCategories()); $collector->setCategories($this->getCategories());
} }
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$categoryId = (int) $journal['category_id']; $categoryId = (int) $journal['category_id'];
$categoryName = (string) $journal['category_name']; $categoryName = (string) $journal['category_name'];
// catch "no category" entries. // catch "no category" entries.
if (0 === $categoryId) { if (0 === $categoryId) {
@@ -222,7 +223,7 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'categories' => [], 'categories' => [],
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -262,7 +263,8 @@ class OperationsRepository implements OperationsRepositoryInterface
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]) $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts); ->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts)
;
if (null !== $categories && $categories->count() > 0) { if (null !== $categories && $categories->count() > 0) {
$collector->setCategories($categories); $collector->setCategories($categories);
} }
@@ -270,13 +272,13 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setCategories($this->getCategories()); $collector->setCategories($this->getCategories());
} }
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$categoryId = (int) $journal['category_id']; $categoryId = (int) $journal['category_id'];
$categoryName = (string) $journal['category_name']; $categoryName = (string) $journal['category_name'];
// catch "no category" entries. // catch "no category" entries.
if (0 === $categoryId) { if (0 === $categoryId) {
@@ -284,7 +286,7 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'categories' => [], 'categories' => [],
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -325,7 +327,7 @@ class OperationsRepository implements OperationsRepositoryInterface
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array
{ {
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
// default currency information for native stuff. // default currency information for native stuff.
@@ -339,19 +341,19 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
$collector->setCategories($categories); $collector->setCategories($categories);
$collector->withCategoryInformation(); $collector->withCategoryInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
Log::debug(sprintf('Collected %d journals', count($journals))); Log::debug(sprintf('Collected %d journals', count($journals)));
foreach ($journals as $journal) { foreach ($journals as $journal) {
// Almost the same as in \FireflyIII\Repositories\Budget\OperationsRepository::sumExpenses // Almost the same as in \FireflyIII\Repositories\Budget\OperationsRepository::sumExpenses
$amount = '0'; $amount = '0';
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol']; $currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code']; $currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places']; $currencyDecimalPlaces = $journal['currency_decimal_places'];
if ($convertToNative) { if ($convertToNative) {
$useNative = $default->id !== (int) $journal['currency_id']; $useNative = $default->id !== (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal); $amount = Amount::getAmountFromJournal($journal);
@@ -371,7 +373,7 @@ class OperationsRepository implements OperationsRepositoryInterface
} }
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $currencyName, 'currency_name' => $currencyName,
@@ -393,7 +395,8 @@ class OperationsRepository implements OperationsRepositoryInterface
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end) $collector->setUser($this->user)->setRange($start, $end)
->setTypes([TransactionType::DEPOSIT]); ->setTypes([TransactionType::DEPOSIT])
;
if (null !== $accounts && $accounts->count() > 0) { if (null !== $accounts && $accounts->count() > 0) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -402,12 +405,12 @@ class OperationsRepository implements OperationsRepositoryInterface
$categories = $this->getCategories(); $categories = $this->getCategories();
} }
$collector->setCategories($categories); $collector->setCategories($categories);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -429,7 +432,8 @@ class OperationsRepository implements OperationsRepositoryInterface
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end) $collector->setUser($this->user)->setRange($start, $end)
->setTypes([TransactionType::TRANSFER]); ->setTypes([TransactionType::TRANSFER])
;
if (null !== $accounts && $accounts->count() > 0) { if (null !== $accounts && $accounts->count() > 0) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -438,12 +442,12 @@ class OperationsRepository implements OperationsRepositoryInterface
$categories = $this->getCategories(); $categories = $this->getCategories();
} }
$collector->setCategories($categories); $collector->setCategories($categories);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],

View File

@@ -58,12 +58,13 @@ class Amount
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
$field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount'; $field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount';
$amount = $journal[$field] ?? '0'; $amount = $journal[$field] ?? '0';
//Log::debug(sprintf('Field is %s, amount is %s', $field, $amount)); // Log::debug(sprintf('Field is %s, amount is %s', $field, $amount));
// fallback, the transaction has a foreign amount in $currency. // fallback, the transaction has a foreign amount in $currency.
if ($convertToNative && null !== $journal['foreign_amount'] && $currency->id === (int)$journal['foreign_currency_id']) { if ($convertToNative && null !== $journal['foreign_amount'] && $currency->id === (int)$journal['foreign_currency_id']) {
$amount = $journal['foreign_amount']; $amount = $journal['foreign_amount'];
//Log::debug(sprintf('Overruled, amount is now %s', $amount)); // Log::debug(sprintf('Overruled, amount is now %s', $amount));
} }
return $amount; return $amount;
} }
@@ -73,23 +74,24 @@ class Amount
*/ */
public function getAmountFromJournalObject(TransactionJournal $journal): string public function getAmountFromJournalObject(TransactionJournal $journal): string
{ {
$convertToNative = app('preferences')->get('convert_to_native', false)->data; $convertToNative = app('preferences')->get('convert_to_native', false)->data;
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
$field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_amount' : 'amount'; $field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_amount' : 'amount';
/** @var null|Transaction $sourceTransaction */ /** @var null|Transaction $sourceTransaction */
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $sourceTransaction) { if (null === $sourceTransaction) {
return '0'; return '0';
} }
$amount = $sourceTransaction->$field; $amount = $sourceTransaction->{$field};
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) { if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead! // use foreign amount instead!
$amount = (string) $sourceTransaction->foreign_amount; // hard coded to be foreign amount. $amount = (string) $sourceTransaction->foreign_amount; // hard coded to be foreign amount.
} }
return $amount; return $amount;
} }
/** /**
* This method will properly format the given number, in color or "black and white", * 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. * as a currency, given two things: the currency required and the current locale.
@@ -100,15 +102,15 @@ class Amount
*/ */
public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string
{ {
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$rounded = app('steam')->bcround($amount, $decimalPlaces); $rounded = app('steam')->bcround($amount, $decimalPlaces);
$coloured ??= true; $coloured ??= true;
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); $fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol);
$fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces);
$fmt->setAttribute(\NumberFormatter::MAX_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 (true === $coloured) {
if (1 === bccomp($rounded, '0')) { if (1 === bccomp($rounded, '0')) {
@@ -154,7 +156,7 @@ class Amount
public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty('getDefaultCurrencyByGroup'); $cache->addProperty('getDefaultCurrencyByGroup');
$cache->addProperty($userGroup->id); $cache->addProperty($userGroup->id);
if ($cache->has()) { if ($cache->has()) {
@@ -217,20 +219,20 @@ class Amount
private function getLocaleInfo(): array private function getLocaleInfo(): array
{ {
// get config from preference, not from translation: // get config from preference, not from translation:
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale); $array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array); setlocale(LC_MONETARY, $array);
$info = localeconv(); $info = localeconv();
// correct variables // correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_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['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_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_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
$info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
@@ -253,7 +255,7 @@ class Amount
public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string
{ {
// negative first: // negative first:
$space = ' '; $space = ' ';
// require space between symbol and amount? // require space between symbol and amount?
if (false === $sepBySpace) { if (false === $sepBySpace) {
@@ -262,11 +264,11 @@ class Amount
// there are five possible positions for the "+" or "-" sign (if it is even used) // there are five possible positions for the "+" or "-" sign (if it is even used)
// pos_a and pos_e could be the ( and ) symbol. // pos_a and pos_e could be the ( and ) symbol.
$posA = ''; // before everything $posA = ''; // before everything
$posB = ''; // before currency symbol $posB = ''; // before currency symbol
$posC = ''; // after currency symbol $posC = ''; // after currency symbol
$posD = ''; // before amount $posD = ''; // before amount
$posE = ''; // after everything $posE = ''; // after everything
// format would be (currency before amount) // format would be (currency before amount)
// AB%sC_D%vE // AB%sC_D%vE
@@ -308,11 +310,11 @@ class Amount
} }
// default is amount before currency // 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) { if ($csPrecedes) {
// alternative is currency before amount // 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; return $format;

View File

@@ -48,7 +48,6 @@ class FrontpageChartGenerator
public bool $convertToNative = false; public bool $convertToNative = false;
public TransactionCurrency $default; public TransactionCurrency $default;
/** /**
* FrontpageChartGenerator constructor. * FrontpageChartGenerator constructor.
*/ */
@@ -100,10 +99,12 @@ class FrontpageChartGenerator
if (0 === $limits->count()) { if (0 === $limits->count()) {
$result = $this->noBudgetLimits($data, $budget); $result = $this->noBudgetLimits($data, $budget);
Log::debug(sprintf('Now DONE processing budget #%d ("%s")', $budget->id, $budget->name)); Log::debug(sprintf('Now DONE processing budget #%d ("%s")', $budget->id, $budget->name));
return $result; return $result;
} }
$result = $this->budgetLimits($data, $budget, $limits); $result = $this->budgetLimits($data, $budget, $limits);
Log::debug(sprintf('Now DONE processing budget #%d ("%s")', $budget->id, $budget->name)); Log::debug(sprintf('Now DONE processing budget #%d ("%s")', $budget->id, $budget->name));
return $result; return $result;
} }
@@ -132,6 +133,7 @@ class FrontpageChartGenerator
private function budgetLimits(array $data, Budget $budget, Collection $limits): array private function budgetLimits(array $data, Budget $budget, Collection $limits): array
{ {
Log::debug('Start processing budget limits.'); Log::debug('Start processing budget limits.');
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
foreach ($limits as $limit) { foreach ($limits as $limit) {
$data = $this->processLimit($data, $budget, $limit); $data = $this->processLimit($data, $budget, $limit);
@@ -156,8 +158,9 @@ class FrontpageChartGenerator
Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount)); Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount));
} }
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency); $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency);
Log::debug(sprintf('Spent array has %d entries.', count($spent))); Log::debug(sprintf('Spent array has %d entries.', count($spent)));
/** @var array $entry */ /** @var array $entry */
foreach ($spent as $entry) { foreach ($spent as $entry) {
// only spent the entry where the entry's currency matches the budget limit's currency // only spent the entry where the entry's currency matches the budget limit's currency
@@ -182,7 +185,7 @@ class FrontpageChartGenerator
*/ */
private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array
{ {
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
Log::debug(sprintf('Title is "%s"', $title)); Log::debug(sprintf('Title is "%s"', $title));
if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) { if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) {
$title = sprintf( $title = sprintf(
@@ -193,22 +196,22 @@ class FrontpageChartGenerator
$limit->end_date->isoFormat($this->monthAndDayFormat) $limit->end_date->isoFormat($this->monthAndDayFormat)
); );
} }
$useNative = $this->convertToNative && $this->default->id !== $limit->transaction_currency_id; $useNative = $this->convertToNative && $this->default->id !== $limit->transaction_currency_id;
$amount = $limit->amount; $amount = $limit->amount;
if($useNative) { if ($useNative) {
$amount = $limit->native_amount; $amount = $limit->native_amount;
} }
$sumSpent = bcmul($entry['sum'], '-1'); // spent $sumSpent = bcmul($entry['sum'], '-1'); // spent
$data[0]['entries'][$title] ??= '0'; $data[0]['entries'][$title] ??= '0';
$data[1]['entries'][$title] ??= '0'; $data[1]['entries'][$title] ??= '0';
$data[2]['entries'][$title] ??= '0'; $data[2]['entries'][$title] ??= '0';
$data[0]['entries'][$title] = bcadd($data[0]['entries'][$title], 1 === bccomp($sumSpent, $amount) ? $amount : $sumSpent); // spent $data[0]['entries'][$title] = bcadd($data[0]['entries'][$title], 1 === bccomp($sumSpent, $amount) ? $amount : $sumSpent); // spent
$data[1]['entries'][$title] = bcadd($data[1]['entries'][$title],1 === bccomp($amount, $sumSpent) ? bcadd($entry['sum'], $amount) : '0'); // left to spent $data[1]['entries'][$title] = bcadd($data[1]['entries'][$title], 1 === bccomp($amount, $sumSpent) ? bcadd($entry['sum'], $amount) : '0'); // left to spent
$data[2]['entries'][$title] = bcadd($data[2]['entries'][$title],1 === bccomp($amount, $sumSpent) ? '0' : bcmul(bcadd($entry['sum'], $amount), '-1')); // overspent $data[2]['entries'][$title] = bcadd($data[2]['entries'][$title], 1 === bccomp($amount, $sumSpent) ? '0' : bcmul(bcadd($entry['sum'], $amount), '-1')); // overspent
Log::debug(sprintf('Amount [spent] is now %s.', $data[0]['entries'][$title])); Log::debug(sprintf('Amount [spent] is now %s.', $data[0]['entries'][$title]));
Log::debug(sprintf('Amount [left] is now %s.', $data[1]['entries'][$title])); Log::debug(sprintf('Amount [left] is now %s.', $data[1]['entries'][$title]));

View File

@@ -67,11 +67,11 @@ class FrontpageChartGenerator
public function generate(): array public function generate(): array
{ {
Log::debug('Now in generate()'); Log::debug('Now in generate()');
$categories = $this->repository->getCategories(); $categories = $this->repository->getCategories();
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
// get expenses + income per category: // get expenses + income per category:
$collection = []; $collection = [];
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
@@ -82,10 +82,10 @@ class FrontpageChartGenerator
// collect for no-category: // collect for no-category:
$collection[] = $this->collectNoCatExpenses($accounts); $collection[] = $this->collectNoCatExpenses($accounts);
$tempData = array_merge(...$collection); $tempData = array_merge(...$collection);
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($tempData, 'sum_float'); $amounts = array_column($tempData, 'sum_float');
array_multisort($amounts, SORT_ASC, $tempData); array_multisort($amounts, SORT_ASC, $tempData);
$currencyData = $this->createCurrencyGroups($tempData); $currencyData = $this->createCurrencyGroups($tempData);

View File

@@ -30,7 +30,6 @@ use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use FireflyIII\User;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -101,8 +100,8 @@ class ExchangeRateConverter
*/ */
private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{ {
$key = $this->getCacheKey($from, $to, $date); $key = $this->getCacheKey($from, $to, $date);
$res = Cache::get($key, null); $res = Cache::get($key, null);
// find in cache // find in cache
if (null !== $res) { if (null !== $res) {
@@ -112,7 +111,7 @@ class ExchangeRateConverter
} }
// find in database // find in database
$rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
Cache::forever($key, $rate); Cache::forever($key, $rate);
Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d'))); Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
@@ -121,7 +120,7 @@ class ExchangeRateConverter
} }
// find reverse in database // find reverse in database
$rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
$rate = bcdiv('1', $rate); $rate = bcdiv('1', $rate);
Cache::forever($key, $rate); Cache::forever($key, $rate);
@@ -159,7 +158,7 @@ class ExchangeRateConverter
if ($from === $to) { if ($from === $to) {
return '1'; return '1';
} }
$key = sprintf('cer-%d-%d-%s', $from, $to, $date); $key = sprintf('cer-%d-%d-%s', $from, $to, $date);
// perhaps the rate has been cached during this particular run // perhaps the rate has been cached during this particular run
$preparedRate = $this->prepared[$date][$from][$to] ?? null; $preparedRate = $this->prepared[$date][$from][$to] ?? null;
@@ -169,7 +168,7 @@ class ExchangeRateConverter
return $preparedRate; return $preparedRate;
} }
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($key); $cache->addProperty($key);
if ($cache->has()) { if ($cache->has()) {
$rate = $cache->get(); $rate = $cache->get();
@@ -182,14 +181,15 @@ class ExchangeRateConverter
} }
/** @var null|CurrencyExchangeRate $result */ /** @var null|CurrencyExchangeRate $result */
$result = $this->userGroup->currencyExchangeRates() $result = $this->userGroup->currencyExchangeRates()
->where('from_currency_id', $from) ->where('from_currency_id', $from)
->where('to_currency_id', $to) ->where('to_currency_id', $to)
->where('date', '<=', $date) ->where('date', '<=', $date)
->orderBy('date', 'DESC') ->orderBy('date', 'DESC')
->first(); ->first()
;
++$this->queryCount; ++$this->queryCount;
$rate = (string) $result?->rate; $rate = (string) $result?->rate;
if ('' === $rate) { if ('' === $rate) {
app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date)); app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date));
@@ -229,13 +229,13 @@ class ExchangeRateConverter
if ($euroId === $currency->id) { if ($euroId === $currency->id) {
return '1'; return '1';
} }
$rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
// app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate));
return $rate; return $rate;
} }
$rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
return bcdiv('1', $rate); return bcdiv('1', $rate);
// app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate));
@@ -264,7 +264,7 @@ class ExchangeRateConverter
if ($cache->has()) { if ($cache->has()) {
return (int) $cache->get(); return (int) $cache->get();
} }
$euro = TransactionCurrency::whereCode('EUR')->first(); $euro = TransactionCurrency::whereCode('EUR')->first();
++$this->queryCount; ++$this->queryCount;
if (null === $euro) { if (null === $euro) {
throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.'); throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.');
@@ -286,13 +286,14 @@ class ExchangeRateConverter
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
Log::debug(sprintf('Preparing for %s to %s between %s and %s', $from->code, $to->code, $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('Preparing for %s to %s between %s and %s', $from->code, $to->code, $start->format('Y-m-d'), $end->format('Y-m-d')));
$set = $this->userGroup $set = $this->userGroup
->currencyExchangeRates() ->currencyExchangeRates()
->where('from_currency_id', $from->id) ->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id) ->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d')) ->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d')) ->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get(); ->orderBy('date', 'DESC')->get()
;
++$this->queryCount; ++$this->queryCount;
if (0 === $set->count()) { if (0 === $set->count()) {
Log::debug('No prepared rates found in this period, use the fallback'); Log::debug('No prepared rates found in this period, use the fallback');
@@ -306,10 +307,10 @@ class ExchangeRateConverter
$this->isPrepared = true; $this->isPrepared = true;
// so there is a fallback just in case. Now loop the set of rates we DO have. // so there is a fallback just in case. Now loop the set of rates we DO have.
$temp = []; $temp = [];
$count = 0; $count = 0;
foreach ($set as $rate) { foreach ($set as $rate) {
$date = $rate->date->format('Y-m-d'); $date = $rate->date->format('Y-m-d');
$temp[$date] ??= [ $temp[$date] ??= [
$from->id => [ $from->id => [
$to->id => $rate->rate, $to->id => $rate->rate,
@@ -318,11 +319,11 @@ class ExchangeRateConverter
++$count; ++$count;
} }
Log::debug(sprintf('Found %d rates in this period.', $count)); Log::debug(sprintf('Found %d rates in this period.', $count));
$currentStart = clone $start; $currentStart = clone $start;
while ($currentStart->lte($end)) { while ($currentStart->lte($end)) {
$currentDate = $currentStart->format('Y-m-d'); $currentDate = $currentStart->format('Y-m-d');
$this->prepared[$currentDate] ??= []; $this->prepared[$currentDate] ??= [];
$fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0'; $fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0';
if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) { if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) {
// fill from temp or fallback or from temp (see before) // fill from temp or fallback or from temp (see before)
$this->prepared[$currentDate][$from->id][$to->id] = $fallback; $this->prepared[$currentDate][$from->id][$to->id] = $fallback;

View File

@@ -39,6 +39,7 @@ trait BasicDataSupport
protected function isInArray(array $array, int $entryId) protected function isInArray(array $array, int $entryId)
{ {
$key = $this->convertToNative ? 'native_balance' : 'balance'; $key = $this->convertToNative ? 'native_balance' : 'balance';
return $array[$entryId][$key] ?? '0'; return $array[$entryId][$key] ?? '0';
} }

View File

@@ -54,7 +54,7 @@ trait ChartGeneration
$cache->addProperty($accounts); $cache->addProperty($accounts);
$cache->addProperty($convertToNative); $cache->addProperty($convertToNative);
if ($cache->has()) { if ($cache->has()) {
//return $cache->get(); // return $cache->get();
} }
app('log')->debug('Regenerate chart.account.account-balance-chart from scratch.'); app('log')->debug('Regenerate chart.account.account-balance-chart from scratch.');
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
@@ -70,10 +70,10 @@ trait ChartGeneration
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
$currency = $accountRepos->getAccountCurrency($account) ?? $default; $currency = $accountRepos->getAccountCurrency($account) ?? $default;
$useNative = $convertToNative && $default->id !== $currency->id; $useNative = $convertToNative && $default->id !== $currency->id;
$field =$useNative ? 'native_balance' : 'balance'; $field = $useNative ? 'native_balance' : 'balance';
$currency = $useNative ? $default : $currency; $currency = $useNative ? $default : $currency;
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,

View File

@@ -38,8 +38,8 @@ class Steam
{ {
public function getAccountCurrency(Account $account): ?TransactionCurrency public function getAccountCurrency(Account $account): ?TransactionCurrency
{ {
$type = $account->accountType->type; $type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types'); $list = config('firefly.valid_currency_account_types');
// return null if not in this list. // return null if not in this list.
if (!in_array($type, $list, true)) { if (!in_array($type, $list, true)) {
@@ -62,7 +62,7 @@ class Steam
$value = (string) ($transaction[$key] ?? '0'); $value = (string) ($transaction[$key] ?? '0');
$value = '' === $value ? '0' : $value; $value = '' === $value ? '0' : $value;
$sum = bcadd($sum, $value); $sum = bcadd($sum, $value);
//Log::debug(sprintf('Add value from "%s": %s', $key, $value)); // Log::debug(sprintf('Add value from "%s": %s', $key, $value));
} }
Log::debug(sprintf('Sum of "%s"-fields is %s', $key, $sum)); Log::debug(sprintf('Sum of "%s"-fields is %s', $key, $sum));
@@ -76,7 +76,7 @@ class Steam
$end->addDay()->endOfDay(); $end->addDay()->endOfDay();
// set up cache // set up cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty('final-balance-in-range'); $cache->addProperty('final-balance-in-range');
$cache->addProperty($start); $cache->addProperty($start);
@@ -85,51 +85,52 @@ class Steam
// return $cache->get(); // return $cache->get();
} }
$balances = []; $balances = [];
$formatted = $start->format('Y-m-d'); $formatted = $start->format('Y-m-d');
$startBalance = $this->finalAccountBalance($account, $start); $startBalance = $this->finalAccountBalance($account, $start);
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$currency = $this->getAccountCurrency($account) ?? $defaultCurrency; $currency = $this->getAccountCurrency($account) ?? $defaultCurrency;
$currencies = [ $currencies = [
$currency->id => $currency, $currency->id => $currency,
$defaultCurrency->id => $defaultCurrency, $defaultCurrency->id => $defaultCurrency,
]; ];
$startBalance[$defaultCurrency->code] ??= '0'; $startBalance[$defaultCurrency->code] ??= '0';
$startBalance[$currency->code] ??= '0'; $startBalance[$currency->code] ??= '0';
$balances[$formatted] = $startBalance; $balances[$formatted] = $startBalance;
// sums up the balance changes per day, for foreign, native and normal amounts. // sums up the balance changes per day, for foreign, native and normal amounts.
$set = $account->transactions() $set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->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', '>=', $start->format('Y-m-d H:i:s'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s'))
->groupBy('transaction_journals.date') ->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id') ->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id') ->groupBy('transactions.foreign_currency_id')
->orderBy('transaction_journals.date', 'ASC') ->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->get( ->get(
[ // @phpstan-ignore-line [ // @phpstan-ignore-line
'transaction_journals.date', 'transaction_journals.date',
'transactions.transaction_currency_id', 'transactions.transaction_currency_id',
\DB::raw('SUM(transactions.amount) AS modified'), \DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id', 'transactions.foreign_currency_id',
\DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), \DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
\DB::raw('SUM(transactions.native_amount) AS modified_native'), \DB::raw('SUM(transactions.native_amount) AS modified_native'),
] ]
); )
;
$currentBalance = $startBalance; $currentBalance = $startBalance;
/** @var Transaction $entry */ /** @var Transaction $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
// normal, native and foreign amount // normal, native and foreign amount
$carbon = new Carbon($entry->date, $entry->date_tz); $carbon = new Carbon($entry->date, $entry->date_tz);
$modified = (string) (null === $entry->modified ? '0' : $entry->modified); $modified = (string) (null === $entry->modified ? '0' : $entry->modified);
$foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); $foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign);
$nativeModified = (string) (null === $entry->modified_native ? '0' : $entry->modified_native); $nativeModified = (string) (null === $entry->modified_native ? '0' : $entry->modified_native);
// add "modified" to amount if the currency id matches the account currency id. // add "modified" to amount if the currency id matches the account currency id.
if ($entry->transaction_currency_id === $currency->id) { if ($entry->transaction_currency_id === $currency->id) {
@@ -138,7 +139,7 @@ class Steam
} }
// always add the native balance, even if it ends up at zero. // always add the native balance, even if it ends up at zero.
$currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified); $currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified);
// add modified foreign to the array // add modified foreign to the array
if (null !== $entry->foreign_currency_id) { if (null !== $entry->foreign_currency_id) {
@@ -184,10 +185,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;
@@ -272,7 +273,6 @@ class Steam
* Wil je niks weten van native currencies, pak je: * Wil je niks weten van native currencies, pak je:
* *
* Eerst een som van alle transacties gegroepeerd op currency. Einde. * Eerst een som van alle transacties gegroepeerd op currency. Einde.
*
*/ */
public function finalAccountBalance(Account $account, Carbon $date): array public function finalAccountBalance(Account $account, Carbon $date): array
{ {
@@ -282,67 +282,71 @@ class Steam
$accountCurrency = $this->getAccountCurrency($account); $accountCurrency = $this->getAccountCurrency($account);
$hasCurrency = null !== $accountCurrency; $hasCurrency = null !== $accountCurrency;
$currency = $hasCurrency ? $accountCurrency : $native; $currency = $hasCurrency ? $accountCurrency : $native;
$return = []; $return = [];
// first, the "balance", as described earlier. // first, the "balance", as described earlier.
if ($convertToNative) { if ($convertToNative) {
// normal balance // normal balance
$return['balance'] = (string) $account->transactions() $return['balance'] = (string) $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_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'))
->where('transactions.transaction_currency_id', $native->id) ->where('transactions.transaction_currency_id', $native->id)
->sum('transactions.amount'); ->sum('transactions.amount')
;
// plus virtual balance, if the account has a virtual_balance in the native currency // plus virtual balance, if the account has a virtual_balance in the native currency
if($native->id === $accountCurrency?->id) { if ($native->id === $accountCurrency?->id) {
$return['balance'] = bcadd('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance, $return['balance']); $return['balance'] = bcadd('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance, $return['balance']);
} }
Log::debug(sprintf('balance is (%s only) %s (with virtual balance)', $native->code, $return['balance'])); Log::debug(sprintf('balance is (%s only) %s (with virtual balance)', $native->code, $return['balance']));
// native balance // native balance
$return['native_balance'] = (string) $account->transactions() $return['native_balance'] = (string) $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_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'))
->whereNot('transactions.transaction_currency_id', $native->id) ->whereNot('transactions.transaction_currency_id', $native->id)
->sum('transactions.native_amount'); ->sum('transactions.native_amount')
;
// plus native virtual balance. // plus native virtual balance.
$return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']); $return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']);
Log::debug(sprintf('native_balance is (all transactions to %s) %s (with virtual balance)', $native->code, $return['native_balance'])); Log::debug(sprintf('native_balance is (all transactions to %s) %s (with virtual balance)', $native->code, $return['native_balance']));
// plus foreign transactions in THIS currency. // plus foreign transactions in THIS currency.
$sum = (string) $account->transactions() $sum = (string) $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_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'))
->whereNot('transactions.transaction_currency_id', $native->id) ->whereNot('transactions.transaction_currency_id', $native->id)
->where('transactions.foreign_currency_id', $native->id) ->where('transactions.foreign_currency_id', $native->id)
->sum('transactions.foreign_amount'); ->sum('transactions.foreign_amount')
;
$return['native_balance'] = bcadd($return['native_balance'], $sum); $return['native_balance'] = bcadd($return['native_balance'], $sum);
Log::debug(sprintf('Foreign amount transactions add (%s only) %s, total native_balance is now %s', $native->code, $sum, $return['native_balance'])); Log::debug(sprintf('Foreign amount transactions add (%s only) %s, total native_balance is now %s', $native->code, $sum, $return['native_balance']));
} }
// balance(s) in other (all) currencies. // balance(s) in other (all) currencies.
$array = $account->transactions() $array = $account->transactions()
->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 the account has no own currency preference, drop balance in favor of native balance // if the account has no own currency preference, drop balance in favor of native balance
if ($hasCurrency && !$convertToNative) { if ($hasCurrency && !$convertToNative) {
$return['balance'] = $others[$currency->code] ?? '0'; $return['balance'] = $others[$currency->code] ?? '0';
$return['native_balance'] = $others[$currency->code] ?? '0'; $return['native_balance'] = $others[$currency->code] ?? '0';
Log::debug(sprintf('Set balance + native_balance to %s', $return['balance'])); Log::debug(sprintf('Set balance + native_balance to %s', $return['balance']));
} }
// if the currency is the same as the native currency, set the native_balance to the balance for consistency. // if the currency is the same as the native currency, set the native_balance to the balance for consistency.
// if($currency->id === $native->id) { // if($currency->id === $native->id) {
// $return['native_balance'] = $return['balance']; // $return['native_balance'] = $return['balance'];
// } // }
if (!$hasCurrency && array_key_exists('balance', $return) && array_key_exists('native_balance', $return)) { if (!$hasCurrency && array_key_exists('balance', $return) && array_key_exists('native_balance', $return)) {
Log::debug('Account has no currency preference, dropping balance in favor of native balance.'); Log::debug('Account has no currency preference, dropping balance in favor of native balance.');
$sum = bcadd($return['balance'], $return['native_balance']); $sum = bcadd($return['balance'], $return['native_balance']);
Log::debug(sprintf('%s + %s = %s', $return['balance'], $return['native_balance'], $sum)); Log::debug(sprintf('%s + %s = %s', $return['balance'], $return['native_balance'], $sum));
$return['native_balance'] = $sum; $return['native_balance'] = $sum;
unset($return['balance']); unset($return['balance']);
@@ -388,15 +392,15 @@ class Steam
{ {
$list = []; $list = [];
$set = auth()->user()->transactions() $set = auth()->user()->transactions()
->whereIn('transactions.account_id', $accounts) ->whereIn('transactions.account_id', $accounts)
->groupBy(['transactions.account_id', 'transaction_journals.user_id']) ->groupBy(['transactions.account_id', 'transaction_journals.user_id'])
->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line ->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
; ;
/** @var Transaction $entry */ /** @var Transaction $entry */
foreach ($set as $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')); $date->setTimezone(config('app.timezone'));
$list[(int) $entry->account_id] = $date; $list[(int) $entry->account_id] = $date;
} }
@@ -471,9 +475,9 @@ class Steam
public function getSafeUrl(string $unknownUrl, string $safeUrl): string public function getSafeUrl(string $unknownUrl, string $safeUrl): string
{ {
// Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
$returnUrl = $safeUrl; $returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST); $unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST); $safeHost = parse_url($safeUrl, PHP_URL_HOST);
if (null !== $unknownHost && $unknownHost === $safeHost) { if (null !== $unknownHost && $unknownHost === $safeHost) {
$returnUrl = $unknownUrl; $returnUrl = $unknownUrl;
@@ -510,7 +514,7 @@ class Steam
*/ */
public function floatalize(string $value): string public function floatalize(string $value): string
{ {
$value = strtoupper($value); $value = strtoupper($value);
if (!str_contains($value, 'E')) { if (!str_contains($value, 'E')) {
return $value; return $value;
} }

View File

@@ -75,7 +75,7 @@ class AmountFormat extends AbstractExtension
$this->formatAmountByAccount(), $this->formatAmountByAccount(),
$this->formatAmountBySymbol(), $this->formatAmountBySymbol(),
$this->formatAmountByCurrency(), $this->formatAmountByCurrency(),
$this->formatAmountByCode() $this->formatAmountByCode(),
]; ];
} }
@@ -111,6 +111,7 @@ class AmountFormat extends AbstractExtension
'formatAmountByCode', 'formatAmountByCode',
static function (string $amount, string $code, ?bool $coloured = null): string { static function (string $amount, string $code, ?bool $coloured = null): string {
$coloured ??= true; $coloured ??= true;
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
$currency = TransactionCurrency::whereCode($code)->first(); $currency = TransactionCurrency::whereCode($code)->first();
@@ -128,8 +129,8 @@ class AmountFormat extends AbstractExtension
return new TwigFunction( return new TwigFunction(
'formatAmountBySymbol', 'formatAmountBySymbol',
static function (string $amount, string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string { static function (string $amount, string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string {
$decimalPlaces ??= 2; $decimalPlaces ??= 2;
$coloured ??= true; $coloured ??= true;
$currency = new TransactionCurrency(); $currency = new TransactionCurrency();
$currency->symbol = $symbol; $currency->symbol = $symbol;
$currency->decimal_places = $decimalPlaces; $currency->decimal_places = $decimalPlaces;

View File

@@ -69,9 +69,9 @@ class General extends AbstractExtension
$date = session('end', today(config('app.timezone'))->endOfMonth()); $date = session('end', today(config('app.timezone'))->endOfMonth());
$info = Steam::finalAccountBalance($account, $date); $info = Steam::finalAccountBalance($account, $date);
$currency = Steam::getAccountCurrency($account); $currency = Steam::getAccountCurrency($account);
$default = Amount::getDefaultCurrency(); $default = Amount::getDefaultCurrency();
$convertToNative = app('preferences')->get('convert_to_native', false)->data; $convertToNative = app('preferences')->get('convert_to_native', false)->data;
$useNative = $convertToNative && $default->id !== $currency->id; $useNative = $convertToNative && $default->id !== $currency->id;
$strings = []; $strings = [];
foreach ($info as $key => $balance) { foreach ($info as $key => $balance) {
if ('balance' === $key) { if ('balance' === $key) {
@@ -79,6 +79,7 @@ class General extends AbstractExtension
if (!$useNative) { if (!$useNative) {
$strings[] = app('amount')->formatAnything($currency, $balance, false); $strings[] = app('amount')->formatAnything($currency, $balance, false);
} }
continue; continue;
} }
if ('native_balance' === $key) { if ('native_balance' === $key) {

12
composer.lock generated
View File

@@ -10845,16 +10845,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "barryvdh/laravel-debugbar", "name": "barryvdh/laravel-debugbar",
"version": "v3.14.9", "version": "v3.14.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git", "url": "https://github.com/barryvdh/laravel-debugbar.git",
"reference": "2e805a6bd4e1aa83774316bb062703c65d0691ef" "reference": "56b9bd235e3fe62e250124804009ce5bab97cc63"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/2e805a6bd4e1aa83774316bb062703c65d0691ef", "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/56b9bd235e3fe62e250124804009ce5bab97cc63",
"reference": "2e805a6bd4e1aa83774316bb062703c65d0691ef", "reference": "56b9bd235e3fe62e250124804009ce5bab97cc63",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -10913,7 +10913,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/barryvdh/laravel-debugbar/issues", "issues": "https://github.com/barryvdh/laravel-debugbar/issues",
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.14.9" "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.14.10"
}, },
"funding": [ "funding": [
{ {
@@ -10925,7 +10925,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-11-25T14:51:20+00:00" "time": "2024-12-23T10:10:42+00:00"
}, },
{ {
"name": "barryvdh/laravel-ide-helper", "name": "barryvdh/laravel-ide-helper",

24
package-lock.json generated
View File

@@ -3621,9 +3621,9 @@
} }
}, },
"node_modules/admin-lte": { "node_modules/admin-lte": {
"version": "4.0.0-beta2", "version": "4.0.0-beta3",
"resolved": "https://registry.npmjs.org/admin-lte/-/admin-lte-4.0.0-beta2.tgz", "resolved": "https://registry.npmjs.org/admin-lte/-/admin-lte-4.0.0-beta3.tgz",
"integrity": "sha512-Ofav0BKnCnz+IeeXrHQZ6JWnHouwv+fDYyfagRpjfFaMBmYCljA2Qo1+fCGkJuJn/SfNPhFpJhbUt+l2tH0LwA==", "integrity": "sha512-q2VoAOu1DtZ7z41M2gQ05VMNYkFCAMxFU+j/HUMwCOlr/e3VhO+qww2SGJw4OxBw5nZQ7YV78+wK2RiB7ConzQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/ag-charts-types": { "node_modules/ag-charts-types": {
@@ -3702,9 +3702,9 @@
} }
}, },
"node_modules/alpinejs": { "node_modules/alpinejs": {
"version": "3.14.7", "version": "3.14.8",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.7.tgz", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.8.tgz",
"integrity": "sha512-ScnbydNBcWVnCiVupD3wWUvoMPm8244xkvDNMxVCspgmap9m4QuJ7pjc+77UtByU+1+Ejg0wzYkP4mQaOMcvng==", "integrity": "sha512-wT2fuP2DXpGk/jKaglwy7S/IJpm1FD+b7U6zUrhwErjoq5h27S4dxkJEXVvhbdwyPv9U+3OkUuNLkZT4h2Kfrg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/reactivity": "~3.1.1" "@vue/reactivity": "~3.1.1"
@@ -5646,9 +5646,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.75", "version": "1.5.76",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz",
"integrity": "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==", "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -6104,9 +6104,9 @@
} }
}, },
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.17.1", "version": "1.18.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {

View File

@@ -80,7 +80,7 @@ Route::group(
Route::group( Route::group(
['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System'], ['middleware' => 'binders-only', 'namespace' => 'FireflyIII\Http\Controllers\System'],
static function (): void { static function (): void {
//Route::get('offline', static fn () => view('errors.offline')); // Route::get('offline', static fn () => view('errors.offline'));
Route::get('health', ['uses' => 'HealthcheckController@check', 'as' => 'healthcheck']); Route::get('health', ['uses' => 'HealthcheckController@check', 'as' => 'healthcheck']);
} }
); );