mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 10:47:00 +00:00 
			
		
		
		
	Various updates to display native/foreign amounts.
This commit is contained in:
		| @@ -40,6 +40,7 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; | ||||
| use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| 
 | ||||
| /** | ||||
|  * Class BasicController | ||||
| @@ -91,24 +92,24 @@ class BasicController extends Controller | ||||
|     public function basic(DateRequest $request): JsonResponse | ||||
|     { | ||||
|         // parameters for boxes:
 | ||||
|         $dates        = $request->getAll(); | ||||
|         $start        = $dates['start']; | ||||
|         $end          = $dates['end']; | ||||
|         $code         = $request->get('currency_code'); | ||||
|         $dates = $request->getAll(); | ||||
|         $start = $dates['start']; | ||||
|         $end   = $dates['end']; | ||||
|         $code  = $request->get('currency_code'); | ||||
| 
 | ||||
|         // balance information:
 | ||||
|         $balanceData  = $this->getBalanceInformation($start, $end); | ||||
|         $billData     = $this->getBillInformation($start, $end); | ||||
|         $spentData    = $this->getLeftToSpendInfo($start, $end); | ||||
|         $netWorthData = $this->getNetWorthInfo($start, $end); | ||||
|         //        $balanceData  = [];
 | ||||
|         //        $billData     = [];
 | ||||
|         //        $spentData    = [];
 | ||||
|         //        $netWorthData = [];
 | ||||
|         $balanceData  = []; | ||||
|         $billData     = []; | ||||
| //                $spentData    = [];
 | ||||
|         $netWorthData = []; | ||||
|         $total        = array_merge($balanceData, $billData, $spentData, $netWorthData); | ||||
| 
 | ||||
|         // give new keys
 | ||||
|         $return       = []; | ||||
|         $return = []; | ||||
|         foreach ($total as $entry) { | ||||
|             if (null === $code || ($code === $entry['currency_code'])) { | ||||
|                 $return[$entry['key']] = $entry; | ||||
| @@ -121,17 +122,17 @@ class BasicController extends Controller | ||||
|     private function getBalanceInformation(Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         // prep some arrays:
 | ||||
|         $incomes   = []; | ||||
|         $expenses  = []; | ||||
|         $sums      = []; | ||||
|         $return    = []; | ||||
|         $incomes  = []; | ||||
|         $expenses = []; | ||||
|         $sums     = []; | ||||
|         $return   = []; | ||||
| 
 | ||||
|         // collect income of user using the new group collector.
 | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::DEPOSIT->value]); | ||||
| 
 | ||||
|         $set       = $collector->getExtractedJournals(); | ||||
|         $set = $collector->getExtractedJournals(); | ||||
| 
 | ||||
|         /** @var array $transactionJournal */ | ||||
|         foreach ($set as $transactionJournal) { | ||||
| @@ -149,7 +150,7 @@ class BasicController extends Controller | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); | ||||
|         $set       = $collector->getExtractedJournals(); | ||||
|         $set = $collector->getExtractedJournals(); | ||||
| 
 | ||||
|         /** @var array $transactionJournal */ | ||||
|         foreach ($set as $transactionJournal) { | ||||
| @@ -161,7 +162,7 @@ class BasicController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         // format amounts:
 | ||||
|         $keys      = array_keys($sums); | ||||
|         $keys = array_keys($sums); | ||||
|         foreach ($keys as $currencyId) { | ||||
|             $currency = $this->currencyRepos->find($currencyId); | ||||
|             if (null === $currency) { | ||||
| @@ -178,8 +179,8 @@ class BasicController extends Controller | ||||
|                 'currency_decimal_places' => $currency->decimal_places, | ||||
|                 'value_parsed'            => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false), | ||||
|                 'local_icon'              => 'balance-scale', | ||||
|                 'sub_title'               => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false). | ||||
|                                              ' + '.app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), | ||||
|                 'sub_title'               => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) . | ||||
|                                              ' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), | ||||
|             ]; | ||||
|             $return[] = [ | ||||
|                 'key'                     => sprintf('spent-in-%s', $currency->code), | ||||
| @@ -220,7 +221,7 @@ class BasicController extends Controller | ||||
|         $paidAmount   = $this->billRepository->sumPaidInRange($start, $end); | ||||
|         $unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end); | ||||
| 
 | ||||
|         $return       = []; | ||||
|         $return = []; | ||||
| 
 | ||||
|         /** | ||||
|          * @var array $info | ||||
| @@ -274,20 +275,23 @@ class BasicController extends Controller | ||||
|         $available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end); | ||||
|         $budgets   = $this->budgetRepository->getActiveBudgets(); | ||||
|         $spent     = $this->opsRepository->sumExpenses($start, $end, null, $budgets); | ||||
|         $days      = (int) $today->diffInDays($end, true) + 1; | ||||
|         Log::debug(sprintf('Now in getLeftToSpendInfo("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); | ||||
| 
 | ||||
|         foreach ($spent as $row) { | ||||
|             // either an amount was budgeted or 0 is available.
 | ||||
|             $amount          = (string) ($available[$row['currency_id']] ?? '0'); | ||||
|             $currencyId      = $row['currency_id']; | ||||
|             $amount          = (string) ($available[$currencyId] ?? '0'); | ||||
|             $spentInCurrency = $row['sum']; | ||||
|             $leftToSpend     = bcadd($amount, $spentInCurrency); | ||||
| 
 | ||||
|             $days            = (int) $today->diffInDays($end, true) + 1; | ||||
|             $perDay          = '0'; | ||||
|             if (0 !== $days && bccomp($leftToSpend, '0') > -1) { | ||||
|                 $perDay = bcdiv($leftToSpend, (string) $days); | ||||
|             } | ||||
| 
 | ||||
|             $return[]        = [ | ||||
|             Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum'])); | ||||
| 
 | ||||
|             $return[] = [ | ||||
|                 'key'                     => sprintf('left-to-spend-in-%s', $row['currency_code']), | ||||
|                 'title'                   => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]), | ||||
|                 'monetary_value'          => $leftToSpend, | ||||
| @@ -312,8 +316,8 @@ class BasicController extends Controller | ||||
|     private function getNetWorthInfo(Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         /** @var User $user */ | ||||
|         $user           = auth()->user(); | ||||
|         $date           = today(config('app.timezone'))->startOfDay(); | ||||
|         $user = auth()->user(); | ||||
|         $date = today(config('app.timezone'))->startOfDay(); | ||||
|         // start and end in the future? use $end
 | ||||
|         if ($this->notInDateRange($date, $start, $end)) { | ||||
|             /** @var Carbon $date */ | ||||
| @@ -323,12 +327,12 @@ class BasicController extends Controller | ||||
|         /** @var NetWorthInterface $netWorthHelper */ | ||||
|         $netWorthHelper = app(NetWorthInterface::class); | ||||
|         $netWorthHelper->setUser($user); | ||||
|         $allAccounts    = $this->accountRepository->getActiveAccountsByType( | ||||
|         $allAccounts = $this->accountRepository->getActiveAccountsByType( | ||||
|             [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT] | ||||
|         ); | ||||
| 
 | ||||
|         // filter list on preference of being included.
 | ||||
|         $filtered       = $allAccounts->filter( | ||||
|         $filtered = $allAccounts->filter( | ||||
|             function (Account $account) { | ||||
|                 $includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth'); | ||||
| 
 | ||||
| @@ -336,13 +340,13 @@ class BasicController extends Controller | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
|         $netWorthSet    = $netWorthHelper->byAccounts($filtered, $date); | ||||
|         $return         = []; | ||||
|         $netWorthSet = $netWorthHelper->byAccounts($filtered, $date); | ||||
|         $return      = []; | ||||
|         foreach ($netWorthSet as $key => $data) { | ||||
|             if ('native' === $key) { | ||||
|                 continue; | ||||
|             } | ||||
|             $amount   = $data['balance']; | ||||
|             $amount = $data['balance']; | ||||
|             if (0 === bccomp($amount, '0')) { | ||||
|                 continue; | ||||
|             } | ||||
|   | ||||
| @@ -210,11 +210,19 @@ class RecalculateNativeAmounts extends Command | ||||
|         $set                              = DB::table('transactions') | ||||
|             ->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|             ->where('transaction_journals.user_group_id', $userGroup->id) | ||||
|             ->where(static function (DatabaseBuilder $q) use ($currency): void { | ||||
|                 $q->whereNot('transactions.transaction_currency_id', $currency->id) | ||||
|                     ->orWhereNot('transactions.foreign_currency_id', $currency->id) | ||||
|                 ; | ||||
| 
 | ||||
|             ->where(function(DatabaseBuilder $q1) use ($currency) { | ||||
|                 $q1->where(function(DatabaseBuilder $q2) use ($currency) { | ||||
|                     $q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id'); | ||||
|                 })->orWhere(function(DatabaseBuilder $q3) use ($currency) { | ||||
|                     $q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id); | ||||
|                 }); | ||||
|             }) | ||||
| //            ->where(static function (DatabaseBuilder $q) use ($currency): void {
 | ||||
| //                $q->whereNot('transactions.transaction_currency_id', $currency->id)
 | ||||
| //                    ->whereNot('transactions.foreign_currency_id', $currency->id)
 | ||||
| //                ;
 | ||||
| //            })
 | ||||
|             ->get(['transactions.id']) | ||||
|         ; | ||||
|         TransactionObserver::$recalculate = false; | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class TransactionObserver | ||||
|         $transaction->native_amount         = null; | ||||
|         $transaction->native_foreign_amount = null; | ||||
|         // first normal amount
 | ||||
|         if ($transaction->transactionCurrency->id !== $userCurrency->id) { | ||||
|         if ($transaction->transactionCurrency->id !== $userCurrency->id && (null === $transaction->foreign_currency_id || (null !== $transaction->foreign_currency_id && $transaction->foreign_currency_id !== $userCurrency->id))) { | ||||
|             $converter                  = new ExchangeRateConverter(); | ||||
|             $converter->setIgnoreSettings(true); | ||||
|             $transaction->native_amount = $converter->convert($transaction->transactionCurrency, $userCurrency, $transaction->transactionJournal->date, $transaction->amount); | ||||
|   | ||||
| @@ -47,108 +47,12 @@ class BoxController extends Controller | ||||
|     use DateCalculation; | ||||
| 
 | ||||
|     /** | ||||
|      * This box has three types of info to display: | ||||
|      * 0) If the user has available amount this period and has overspent: overspent box. | ||||
|      * 1) If the user has available amount this period and has NOT overspent: left to spend box. | ||||
|      * 2) if the user has no available amount set this period: spent per day | ||||
|      * | ||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) | ||||
|      * Deprecated method, no longer in use. | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function available(): JsonResponse | ||||
|     { | ||||
|         app('log')->debug('Now in available()'); | ||||
| 
 | ||||
|         /** @var OperationsRepositoryInterface $opsRepository */ | ||||
|         $opsRepository     = app(OperationsRepositoryInterface::class); | ||||
| 
 | ||||
|         /** @var AvailableBudgetRepositoryInterface $abRepository */ | ||||
|         $abRepository      = app(AvailableBudgetRepositoryInterface::class); | ||||
|         $abRepository->cleanup(); | ||||
| 
 | ||||
|         /** @var Carbon $start */ | ||||
|         $start             = session('start', today(config('app.timezone'))->startOfMonth()); | ||||
| 
 | ||||
|         /** @var Carbon $end */ | ||||
|         $end               = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $today             = today(config('app.timezone')); | ||||
|         $display           = 2; // see method docs.
 | ||||
|         $boxTitle          = (string) trans('firefly.spent'); | ||||
| 
 | ||||
|         $cache             = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($today); | ||||
|         $cache->addProperty('box-available'); | ||||
|         if ($cache->has()) { | ||||
|             return response()->json($cache->get()); | ||||
|         } | ||||
|         $leftPerDayAmount  = '0'; | ||||
|         $leftToSpendAmount = '0'; | ||||
| 
 | ||||
|         $currency          = app('amount')->getDefaultCurrency(); | ||||
|         app('log')->debug(sprintf('Default currency is %s', $currency->code)); | ||||
|         $availableBudgets  = $abRepository->getAvailableBudgetsByExactDate($start, $end); | ||||
|         app('log')->debug(sprintf('Found %d available budget(s)', $availableBudgets->count())); | ||||
|         $availableBudgets  = $availableBudgets->filter( | ||||
|             static function (AvailableBudget $availableBudget) use ($currency) { // @phpstan-ignore-line
 | ||||
|                 if ($availableBudget->transaction_currency_id === $currency->id) { | ||||
|                     app('log')->debug(sprintf( | ||||
|                         'Will include AB #%d: from %s-%s amount %s', | ||||
|                         $availableBudget->id, | ||||
|                         $availableBudget->start_date->format('Y-m-d'), | ||||
|                         $availableBudget->end_date->format('Y-m-d'), | ||||
|                         $availableBudget->amount | ||||
|                     )); | ||||
| 
 | ||||
|                     return $availableBudget; | ||||
|                 } | ||||
| 
 | ||||
|                 return null; | ||||
|             } | ||||
|         ); | ||||
|         app('log')->debug(sprintf('Filtered back to %d available budgets', $availableBudgets->count())); | ||||
|         // spent in this period, in budgets, for default currency.
 | ||||
|         // also calculate spent per day.
 | ||||
|         $spent             = $opsRepository->sumExpenses($start, $end, null, null, $currency); | ||||
|         $spentAmount       = $spent[$currency->id]['sum'] ?? '0'; | ||||
|         app('log')->debug(sprintf('Spent for default currency for all budgets in this period: %s', $spentAmount)); | ||||
| 
 | ||||
|         $days              = (int) ($today->between($start, $end) ? $today->diffInDays($start, true) + 1 : $end->diffInDays($start, true) + 1); | ||||
|         app('log')->debug(sprintf('Number of days left: %d', $days)); | ||||
|         $spentPerDay       = bcdiv($spentAmount, (string) $days); | ||||
|         app('log')->debug(sprintf('Available to spend per day: %s', $spentPerDay)); | ||||
|         if ($availableBudgets->count() > 0) { | ||||
|             $display           = 0; // assume user overspent
 | ||||
|             $boxTitle          = (string) trans('firefly.overspent'); | ||||
|             $totalAvailableSum = (string) $availableBudgets->sum('amount'); | ||||
|             app('log')->debug(sprintf('Total available sum is %s', $totalAvailableSum)); | ||||
|             // calculate with available budget.
 | ||||
|             $leftToSpendAmount = bcadd($totalAvailableSum, $spentAmount); | ||||
|             app('log')->debug(sprintf('So left to spend is %s', $leftToSpendAmount)); | ||||
|             if (bccomp($leftToSpendAmount, '0') >= 0) { | ||||
|                 app('log')->debug('Left to spend is positive or zero!'); | ||||
|                 $boxTitle         = (string) trans('firefly.left_to_spend'); | ||||
|                 $activeDaysLeft   = $this->activeDaysLeft($start, $end);   // see method description.
 | ||||
|                 $display          = 1;                                     // not overspent
 | ||||
|                 $leftPerDayAmount = 0 === $activeDaysLeft ? $leftToSpendAmount : bcdiv($leftToSpendAmount, (string) $activeDaysLeft); | ||||
|                 app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $return            = [ | ||||
|             'display'       => $display, | ||||
|             'spent_total'   => app('amount')->formatAnything($currency, $spentAmount, false), | ||||
|             'spent_per_day' => app('amount')->formatAnything($currency, $spentPerDay, false), | ||||
|             'left_to_spend' => app('amount')->formatAnything($currency, $leftToSpendAmount, false), | ||||
|             'left_per_day'  => app('amount')->formatAnything($currency, $leftPerDayAmount, false), | ||||
|             'title'         => $boxTitle, | ||||
|         ]; | ||||
|         app('log')->debug('Final output', $return); | ||||
| 
 | ||||
|         $cache->store($return); | ||||
|         app('log')->debug('Now done with available()'); | ||||
| 
 | ||||
|         return response()->json($return); | ||||
|         return response()->json([]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -158,35 +62,38 @@ class BoxController extends Controller | ||||
|     { | ||||
|         // Cache result, return cache if present.
 | ||||
|         /** @var Carbon $start */ | ||||
|         $start     = session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|         $start = session('start', today(config('app.timezone'))->startOfMonth()); | ||||
| 
 | ||||
|         /** @var Carbon $end */ | ||||
|         $end       = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $cache     = new CacheProperties(); | ||||
|         $end             = session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $convertToNative = app('preferences')->get('convert_to_native', false)->data; | ||||
|         $cache           = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($convertToNative); | ||||
|         $cache->addProperty('box-balance'); | ||||
|         if ($cache->has()) { | ||||
|             return response()->json($cache->get()); | ||||
|         } | ||||
|         // prep some arrays:
 | ||||
|         $incomes   = []; | ||||
|         $expenses  = []; | ||||
|         $sums      = []; | ||||
|         $currency  = app('amount')->getDefaultCurrency(); | ||||
|         $incomes  = []; | ||||
|         $expenses = []; | ||||
|         $sums     = []; | ||||
|         $currency = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
| 
 | ||||
|         // collect income of user:
 | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setRange($start, $end) | ||||
|             ->setTypes([TransactionType::DEPOSIT]) | ||||
|         ; | ||||
|         $set       = $collector->getExtractedJournals(); | ||||
|                   ->setTypes([TransactionType::DEPOSIT]); | ||||
|         $set = $collector->getExtractedJournals(); | ||||
| 
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($set as $journal) { | ||||
|             $currencyId           = (int) $journal['currency_id']; | ||||
|             $amount               = $journal['amount'] ?? '0'; | ||||
|             $field                = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount'; | ||||
|             $currencyId           = $convertToNative ? $currency->id : (int) $journal['currency_id']; | ||||
|             $amount               = $journal[$field] ?? '0'; | ||||
|             $incomes[$currencyId] ??= '0'; | ||||
|             $incomes[$currencyId] = bcadd($incomes[$currencyId], app('steam')->positive($amount)); | ||||
|             $sums[$currencyId]    ??= '0'; | ||||
| @@ -197,21 +104,22 @@ class BoxController extends Controller | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setRange($start, $end) | ||||
|             ->setTypes([TransactionType::WITHDRAWAL]) | ||||
|         ; | ||||
|         $set       = $collector->getExtractedJournals(); | ||||
|                   ->setTypes([TransactionType::WITHDRAWAL]); | ||||
|         $set = $collector->getExtractedJournals(); | ||||
| 
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($set as $journal) { | ||||
|             $currencyId            = (int) $journal['currency_id']; | ||||
|             $field                 = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount'; | ||||
|             $currencyId            = $convertToNative ? $currency->id : (int) $journal['currency_id']; | ||||
|             $amount                = $journal[$field] ?? '0'; | ||||
|             $expenses[$currencyId] ??= '0'; | ||||
|             $expenses[$currencyId] = bcadd($expenses[$currencyId], $journal['amount'] ?? '0'); | ||||
|             $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); | ||||
|             $sums[$currencyId]     ??= '0'; | ||||
|             $sums[$currencyId]     = bcadd($sums[$currencyId], $journal['amount']); | ||||
|             $sums[$currencyId]     = bcadd($sums[$currencyId], $amount); | ||||
|         } | ||||
| 
 | ||||
|         // format amounts:
 | ||||
|         $keys      = array_keys($sums); | ||||
|         $keys = array_keys($sums); | ||||
|         foreach ($keys as $currencyId) { | ||||
|             $currency              = $repository->find($currencyId); | ||||
|             $sums[$currencyId]     = app('amount')->formatAnything($currency, $sums[$currencyId], false); | ||||
| @@ -225,7 +133,7 @@ class BoxController extends Controller | ||||
|             $expenses[$currency->id] = app('amount')->formatAnything($currency, '0', false); | ||||
|         } | ||||
| 
 | ||||
|         $response  = [ | ||||
|         $response = [ | ||||
|             'incomes'   => $incomes, | ||||
|             'expenses'  => $expenses, | ||||
|             'sums'      => $sums, | ||||
| @@ -242,7 +150,7 @@ class BoxController extends Controller | ||||
|      */ | ||||
|     public function netWorth(): JsonResponse | ||||
|     { | ||||
|         $date              = today(config('app.timezone'))->endOfDay(); | ||||
|         $date = today(config('app.timezone'))->endOfDay(); | ||||
| 
 | ||||
|         // start and end in the future? use $end
 | ||||
|         if ($this->notInSessionRange($date)) { | ||||
| @@ -251,7 +159,7 @@ class BoxController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         /** @var NetWorthInterface $netWorthHelper */ | ||||
|         $netWorthHelper    = app(NetWorthInterface::class); | ||||
|         $netWorthHelper = app(NetWorthInterface::class); | ||||
|         $netWorthHelper->setUser(auth()->user()); | ||||
| 
 | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
| @@ -262,7 +170,7 @@ class BoxController extends Controller | ||||
|         app('log')->debug(sprintf('Found %d accounts.', $allAccounts->count())); | ||||
| 
 | ||||
|         // filter list on preference of being included.
 | ||||
|         $filtered          = $allAccounts->filter( | ||||
|         $filtered = $allAccounts->filter( | ||||
|             static function (Account $account) use ($accountRepository) { | ||||
|                 $includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth'); | ||||
|                 $result          = null === $includeNetWorth ? true : '1' === $includeNetWorth; | ||||
| @@ -274,15 +182,15 @@ class BoxController extends Controller | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
|         $netWorthSet       = $netWorthHelper->byAccounts($filtered, $date); | ||||
|         $return            = []; | ||||
|         $netWorthSet = $netWorthHelper->byAccounts($filtered, $date); | ||||
|         $return      = []; | ||||
|         foreach ($netWorthSet as $key => $data) { | ||||
|             if ('native' === $key) { | ||||
|                 continue; | ||||
|             } | ||||
|             $return[$data['currency_id']] = app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false); | ||||
|         } | ||||
|         $return            = [ | ||||
|         $return = [ | ||||
|             'net_worths' => array_values($return), | ||||
|         ]; | ||||
| 
 | ||||
|   | ||||
| @@ -67,6 +67,8 @@ class Transaction extends Model | ||||
|             'transaction_journal_id', | ||||
|             'description', | ||||
|             'amount', | ||||
|             'native_amount', | ||||
|             'native_foreign_amount', | ||||
|             'identifier', | ||||
|             'transaction_currency_id', | ||||
|             'foreign_currency_id', | ||||
|   | ||||
| @@ -60,8 +60,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|             $search->whereLike('name', sprintf('%%%s', $query)); | ||||
|         } | ||||
|         $search->orderBy('name', 'ASC') | ||||
|             ->where('active', true) | ||||
|         ; | ||||
|                ->where('active', true); | ||||
| 
 | ||||
|         return $search->take($limit)->get(); | ||||
|     } | ||||
| @@ -73,8 +72,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|             $search->whereLike('name', sprintf('%s%%', $query)); | ||||
|         } | ||||
|         $search->orderBy('name', 'ASC') | ||||
|             ->where('active', true) | ||||
|         ; | ||||
|                ->where('active', true); | ||||
| 
 | ||||
|         return $search->take($limit)->get(); | ||||
|     } | ||||
| @@ -157,7 +155,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|      */ | ||||
|     public function getAttachments(Bill $bill): Collection | ||||
|     { | ||||
|         $set  = $bill->attachments()->get(); | ||||
|         $set = $bill->attachments()->get(); | ||||
| 
 | ||||
|         /** @var \Storage $disk */ | ||||
|         $disk = \Storage::disk('upload'); | ||||
| @@ -176,10 +174,9 @@ class BillRepository implements BillRepositoryInterface | ||||
|     public function getBills(): Collection | ||||
|     { | ||||
|         return $this->user->bills() | ||||
|             ->orderBy('order', 'ASC') | ||||
|             ->orderBy('active', 'DESC') | ||||
|             ->orderBy('name', 'ASC')->get() | ||||
|         ; | ||||
|                           ->orderBy('order', 'ASC') | ||||
|                           ->orderBy('active', 'DESC') | ||||
|                           ->orderBy('name', 'ASC')->get(); | ||||
|     } | ||||
| 
 | ||||
|     public function getBillsForAccounts(Collection $accounts): Collection | ||||
| @@ -203,25 +200,24 @@ class BillRepository implements BillRepositoryInterface | ||||
|         $ids    = $accounts->pluck('id')->toArray(); | ||||
| 
 | ||||
|         return $this->user->bills() | ||||
|             ->leftJoin( | ||||
|                 'transaction_journals', | ||||
|                 static function (JoinClause $join): void { | ||||
|                     $join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at'); | ||||
|                 } | ||||
|             ) | ||||
|             ->leftJoin( | ||||
|                 'transactions', | ||||
|                 static function (JoinClause $join): void { | ||||
|                     $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); | ||||
|                 } | ||||
|             ) | ||||
|             ->whereIn('transactions.account_id', $ids) | ||||
|             ->whereNull('transaction_journals.deleted_at') | ||||
|             ->orderBy('bills.active', 'DESC') | ||||
|             ->orderBy('bills.name', 'ASC') | ||||
|             ->groupBy($fields) | ||||
|             ->get($fields) | ||||
|         ; | ||||
|                           ->leftJoin( | ||||
|                               'transaction_journals', | ||||
|                               static function (JoinClause $join): void { | ||||
|                                   $join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at'); | ||||
|                               } | ||||
|                           ) | ||||
|                           ->leftJoin( | ||||
|                               'transactions', | ||||
|                               static function (JoinClause $join): void { | ||||
|                                   $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); | ||||
|                               } | ||||
|                           ) | ||||
|                           ->whereIn('transactions.account_id', $ids) | ||||
|                           ->whereNull('transaction_journals.deleted_at') | ||||
|                           ->orderBy('bills.active', 'DESC') | ||||
|                           ->orderBy('bills.name', 'ASC') | ||||
|                           ->groupBy($fields) | ||||
|                           ->get($fields); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -246,7 +242,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|     public function getOverallAverage(Bill $bill): array | ||||
|     { | ||||
|         /** @var JournalRepositoryInterface $repos */ | ||||
|         $repos    = app(JournalRepositoryInterface::class); | ||||
|         $repos = app(JournalRepositoryInterface::class); | ||||
|         $repos->setUser($this->user); | ||||
| 
 | ||||
|         // get and sort on currency
 | ||||
| @@ -259,7 +255,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|             $transaction                = $journal->transactions()->where('amount', '<', 0)->first(); | ||||
|             $currencyId                 = (int) $journal->transaction_currency_id; | ||||
|             $currency                   = $journal->transactionCurrency; | ||||
|             $result[$currencyId] ??= [ | ||||
|             $result[$currencyId]        ??= [ | ||||
|                 'sum'                     => '0', | ||||
|                 'count'                   => 0, | ||||
|                 'avg'                     => '0', | ||||
| @@ -284,7 +280,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|         return $result; | ||||
|     } | ||||
| 
 | ||||
|     public function setUser(null|Authenticatable|User $user): void | ||||
|     public function setUser(null | Authenticatable | User $user): void | ||||
|     { | ||||
|         if ($user instanceof User) { | ||||
|             $this->user = $user; | ||||
| @@ -294,9 +290,8 @@ class BillRepository implements BillRepositoryInterface | ||||
|     public function getPaginator(int $size): LengthAwarePaginator | ||||
|     { | ||||
|         return $this->user->bills() | ||||
|             ->orderBy('active', 'DESC') | ||||
|             ->orderBy('name', 'ASC')->paginate($size) | ||||
|         ; | ||||
|                           ->orderBy('active', 'DESC') | ||||
|                           ->orderBy('name', 'ASC')->paginate($size); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -309,14 +304,13 @@ class BillRepository implements BillRepositoryInterface | ||||
|         Log::debug(sprintf('Search for linked journals between %s and %s', $start->toW3cString(), $end->toW3cString())); | ||||
| 
 | ||||
|         return $bill->transactionJournals() | ||||
|             ->before($end)->after($start)->get( | ||||
|                     ->before($end)->after($start)->get( | ||||
|                 [ | ||||
|                     'transaction_journals.id', | ||||
|                     'transaction_journals.date', | ||||
|                     'transaction_journals.transaction_group_id', | ||||
|                 ] | ||||
|             ) | ||||
|         ; | ||||
|             ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -325,11 +319,10 @@ class BillRepository implements BillRepositoryInterface | ||||
|     public function getRulesForBill(Bill $bill): Collection | ||||
|     { | ||||
|         return $this->user->rules() | ||||
|             ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') | ||||
|             ->where('rule_actions.action_type', 'link_to_bill') | ||||
|             ->where('rule_actions.action_value', $bill->name) | ||||
|             ->get(['rules.*']) | ||||
|         ; | ||||
|                           ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') | ||||
|                           ->where('rule_actions.action_type', 'link_to_bill') | ||||
|                           ->where('rule_actions.action_value', $bill->name) | ||||
|                           ->get(['rules.*']); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -340,16 +333,15 @@ class BillRepository implements BillRepositoryInterface | ||||
|      */ | ||||
|     public function getRulesForBills(Collection $collection): array | ||||
|     { | ||||
|         $rules  = $this->user->rules() | ||||
|             ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') | ||||
|             ->where('rule_actions.action_type', 'link_to_bill') | ||||
|             ->get(['rules.id', 'rules.title', 'rule_actions.action_value', 'rules.active']) | ||||
|         ; | ||||
|         $array  = []; | ||||
|         $rules = $this->user->rules() | ||||
|                             ->leftJoin('rule_actions', 'rule_actions.rule_id', '=', 'rules.id') | ||||
|                             ->where('rule_actions.action_type', 'link_to_bill') | ||||
|                             ->get(['rules.id', 'rules.title', 'rule_actions.action_value', 'rules.active']); | ||||
|         $array = []; | ||||
| 
 | ||||
|         /** @var Rule $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]; | ||||
|         } | ||||
|         $return = []; | ||||
| @@ -363,28 +355,27 @@ class BillRepository implements BillRepositoryInterface | ||||
|     public function getYearAverage(Bill $bill, Carbon $date): array | ||||
|     { | ||||
|         /** @var JournalRepositoryInterface $repos */ | ||||
|         $repos    = app(JournalRepositoryInterface::class); | ||||
|         $repos = app(JournalRepositoryInterface::class); | ||||
|         $repos->setUser($this->user); | ||||
| 
 | ||||
|         // get and sort on currency
 | ||||
|         $result   = []; | ||||
|         $result = []; | ||||
| 
 | ||||
|         $journals = $bill->transactionJournals() | ||||
|             ->where('date', '>=', $date->year.'-01-01 00:00:00') | ||||
|             ->where('date', '<=', $date->year.'-12-31 23:59:59') | ||||
|             ->get() | ||||
|         ; | ||||
|                          ->where('date', '>=', $date->year . '-01-01 00:00:00') | ||||
|                          ->where('date', '<=', $date->year . '-12-31 23:59:59') | ||||
|                          ->get(); | ||||
| 
 | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             /** @var null|Transaction $transaction */ | ||||
|             $transaction                = $journal->transactions()->where('amount', '<', 0)->first(); | ||||
|             $transaction = $journal->transactions()->where('amount', '<', 0)->first(); | ||||
|             if (null === $transaction) { | ||||
|                 continue; | ||||
|             } | ||||
|             $currencyId                 = (int) $journal->transaction_currency_id; | ||||
|             $currency                   = $journal->transactionCurrency; | ||||
|             $result[$currencyId] ??= [ | ||||
|             $result[$currencyId]        ??= [ | ||||
|                 'sum'                     => '0', | ||||
|                 'count'                   => 0, | ||||
|                 'avg'                     => '0', | ||||
| @@ -428,7 +419,7 @@ class BillRepository implements BillRepositoryInterface | ||||
|      */ | ||||
|     public function nextExpectedMatch(Bill $bill, Carbon $date): Carbon | ||||
|     { | ||||
|         $cache        = new CacheProperties(); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($bill->id); | ||||
|         $cache->addProperty('nextExpectedMatch'); | ||||
|         $cache->addProperty($date); | ||||
| @@ -436,17 +427,17 @@ class BillRepository implements BillRepositoryInterface | ||||
|             return $cache->get(); | ||||
|         } | ||||
|         // 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(); | ||||
|         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) { | ||||
|             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); | ||||
|             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(); | ||||
| 
 | ||||
|         // see if the bill was paid in this period.
 | ||||
| @@ -458,8 +449,8 @@ class BillRepository implements BillRepositoryInterface | ||||
|             $start = clone $end; | ||||
|             $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: Matching end is '.$end->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')); | ||||
| 
 | ||||
|         $cache->store($start); | ||||
| 
 | ||||
| @@ -510,15 +501,18 @@ class BillRepository implements BillRepositoryInterface | ||||
| 
 | ||||
|     public function sumPaidInRange(Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         $bills  = $this->getActiveBills(); | ||||
|         $return = []; | ||||
|         $bills           = $this->getActiveBills(); | ||||
|         $return          = []; | ||||
|         $convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data; | ||||
|         $default         = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         /** @var Bill $bill */ | ||||
|         foreach ($bills as $bill) { | ||||
|             /** @var Collection $set */ | ||||
|             $set      = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); | ||||
|             $currency = $bill->transactionCurrency; | ||||
| 
 | ||||
|             $set                   = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); | ||||
|             $currency              = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; | ||||
|             $field                 = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount' : 'amount'; | ||||
|             $foreignField          = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_foreign_amount' : 'foreign_amount'; | ||||
|             $return[$currency->id] ??= [ | ||||
|                 'id'             => (string) $currency->id, | ||||
|                 'name'           => $currency->name, | ||||
| @@ -533,10 +527,10 @@ class BillRepository implements BillRepositoryInterface | ||||
|                 /** @var null|Transaction $sourceTransaction */ | ||||
|                 $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); | ||||
|                 if (null !== $sourceTransaction) { | ||||
|                     $amount                       = $sourceTransaction->amount; | ||||
|                     $amount = $sourceTransaction->$field; | ||||
|                     if ((int) $sourceTransaction->foreign_currency_id === $currency->id) { | ||||
|                         // use foreign amount instead!
 | ||||
|                         $amount = (string) $sourceTransaction->foreign_amount; | ||||
|                         $amount = (string) $sourceTransaction->$foreignField; | ||||
|                     } | ||||
|                     $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); | ||||
|                 } | ||||
| @@ -549,17 +543,19 @@ class BillRepository implements BillRepositoryInterface | ||||
|     public function getActiveBills(): Collection | ||||
|     { | ||||
|         return $this->user->bills() | ||||
|             ->where('active', true) | ||||
|             ->orderBy('bills.name', 'ASC') | ||||
|             ->get(['bills.*', \DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount')]) // @phpstan-ignore-line
 | ||||
|         ; | ||||
|                           ->where('active', true) | ||||
|                           ->orderBy('bills.name', 'ASC') | ||||
|                           ->get(['bills.*', \DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount')]) // @phpstan-ignore-line
 | ||||
|             ; | ||||
|     } | ||||
| 
 | ||||
|     public function sumUnpaidInRange(Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         app('log')->debug(sprintf('Now in sumUnpaidInRange("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); | ||||
|         $bills  = $this->getActiveBills(); | ||||
|         $return = []; | ||||
|         $bills           = $this->getActiveBills(); | ||||
|         $return          = []; | ||||
|         $convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data; | ||||
|         $default         = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         /** @var Bill $bill */ | ||||
|         foreach ($bills as $bill) { | ||||
| @@ -570,10 +566,13 @@ class BillRepository implements BillRepositoryInterface | ||||
|             // app('log')->debug(sprintf('Pay dates: %d, count: %d, left: %d', $dates->count(), $count, $total));
 | ||||
|             // app('log')->debug('dates', $dates->toArray());
 | ||||
| 
 | ||||
|             $minField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_min' : 'amount_min'; | ||||
|             $maxField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_max' : 'amount_max'; | ||||
| 
 | ||||
|             if ($total > 0) { | ||||
|                 $currency                     = $bill->transactionCurrency; | ||||
|                 $average                      = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2'); | ||||
|                 $return[$currency->id] ??= [ | ||||
|                 $currency                     = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency; | ||||
|                 $average                      = bcdiv(bcadd($bill->$maxField, $bill->$minField), '2'); | ||||
|                 $return[$currency->id]        ??= [ | ||||
|                     'id'             => (string) $currency->id, | ||||
|                     'name'           => $currency->name, | ||||
|                     'symbol'         => $currency->symbol, | ||||
| @@ -611,7 +610,7 @@ class BillRepository implements BillRepositoryInterface | ||||
| 
 | ||||
|             // 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; | ||||
|   | ||||
| @@ -47,9 +47,9 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
| 
 | ||||
|         /** @var AvailableBudget $availableBudget */ | ||||
|         foreach ($availableBudgets as $availableBudget) { | ||||
|             $start        = $availableBudget->start_date->format('Y-m-d'); | ||||
|             $end          = $availableBudget->end_date->format('Y-m-d'); | ||||
|             $key          = sprintf('%s-%s-%s', $availableBudget->transaction_currency_id, $start, $end); | ||||
|             $start = $availableBudget->start_date->format('Y-m-d'); | ||||
|             $end   = $availableBudget->end_date->format('Y-m-d'); | ||||
|             $key   = sprintf('%s-%s-%s', $availableBudget->transaction_currency_id, $start, $end); | ||||
|             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)); | ||||
|                 $availableBudget->delete(); | ||||
| @@ -101,23 +101,21 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|     public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?AvailableBudget | ||||
|     { | ||||
|         return $this->user->availableBudgets() | ||||
|             ->where('transaction_currency_id', $currency->id) | ||||
|             ->where('start_date', $start->format('Y-m-d')) | ||||
|             ->where('end_date', $end->format('Y-m-d')) | ||||
|             ->first() | ||||
|         ; | ||||
|                           ->where('transaction_currency_id', $currency->id) | ||||
|                           ->where('start_date', $start->format('Y-m-d')) | ||||
|                           ->where('end_date', $end->format('Y-m-d')) | ||||
|                           ->first(); | ||||
|     } | ||||
| 
 | ||||
|     public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string | ||||
|     { | ||||
|         $amount          = '0'; | ||||
|         $amount = '0'; | ||||
| 
 | ||||
|         /** @var null|AvailableBudget $availableBudget */ | ||||
|         $availableBudget = $this->user->availableBudgets() | ||||
|             ->where('transaction_currency_id', $currency->id) | ||||
|             ->where('start_date', $start->format('Y-m-d')) | ||||
|             ->where('end_date', $end->format('Y-m-d'))->first() | ||||
|         ; | ||||
|                                       ->where('transaction_currency_id', $currency->id) | ||||
|                                       ->where('start_date', $start->format('Y-m-d')) | ||||
|                                       ->where('end_date', $end->format('Y-m-d'))->first(); | ||||
|         if (null !== $availableBudget) { | ||||
|             $amount = $availableBudget->amount; | ||||
|         } | ||||
| @@ -129,15 +127,20 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|     { | ||||
|         $return           = []; | ||||
|         $availableBudgets = $this->user->availableBudgets() | ||||
|             ->where('start_date', $start->format('Y-m-d')) | ||||
|             ->where('end_date', $end->format('Y-m-d'))->get() | ||||
|         ; | ||||
|                                        ->where('start_date', $start->format('Y-m-d')) | ||||
|                                        ->where('end_date', $end->format('Y-m-d'))->get(); | ||||
| 
 | ||||
|         // use native amount if necessary?
 | ||||
|         $convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data; | ||||
|         $default         = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         /** @var AvailableBudget $availableBudget */ | ||||
|         foreach ($availableBudgets as $availableBudget) { | ||||
|             $return[$availableBudget->transaction_currency_id] = $availableBudget->amount; | ||||
|             $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'; | ||||
|             $return[$currencyId] = $return[$currencyId] ?? '0'; | ||||
|             $return[$currencyId] = bcadd($return[$currencyId], $availableBudget->$field); | ||||
|         } | ||||
| 
 | ||||
|         return $return; | ||||
|     } | ||||
| 
 | ||||
| @@ -172,10 +175,9 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|     public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection | ||||
|     { | ||||
|         return $this->user->availableBudgets() | ||||
|             ->where('start_date', '=', $start->format('Y-m-d')) | ||||
|             ->where('end_date', '=', $end->format('Y-m-d')) | ||||
|             ->get() | ||||
|         ; | ||||
|                           ->where('start_date', '=', $start->format('Y-m-d')) | ||||
|                           ->where('end_date', '=', $end->format('Y-m-d')) | ||||
|                           ->get(); | ||||
|     } | ||||
| 
 | ||||
|     public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget | ||||
| @@ -184,8 +186,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|             ->availableBudgets() | ||||
|             ->where('transaction_currency_id', $currency->id) | ||||
|             ->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(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -193,13 +194,12 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|      */ | ||||
|     public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget | ||||
|     { | ||||
|         $availableBudget         = $this->user->availableBudgets() | ||||
|             ->where('transaction_currency_id', $currency->id) | ||||
|             ->where('start_date', $start->format('Y-m-d')) | ||||
|             ->where('end_date', $end->format('Y-m-d'))->first() | ||||
|         ; | ||||
|         $availableBudget = $this->user->availableBudgets() | ||||
|                                       ->where('transaction_currency_id', $currency->id) | ||||
|                                       ->where('start_date', $start->format('Y-m-d')) | ||||
|                                       ->where('end_date', $end->format('Y-m-d'))->first(); | ||||
|         if (null === $availableBudget) { | ||||
|             $availableBudget                = new AvailableBudget(); | ||||
|             $availableBudget = new AvailableBudget(); | ||||
|             $availableBudget->user()->associate($this->user); | ||||
|             $availableBudget->transactionCurrency()->associate($currency); | ||||
|             $availableBudget->start_date    = $start->startOfDay()->format('Y-m-d'); // @phpstan-ignore-line
 | ||||
| @@ -213,7 +213,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|         return $availableBudget; | ||||
|     } | ||||
| 
 | ||||
|     public function setUser(null|Authenticatable|User $user): void | ||||
|     public function setUser(null | Authenticatable | User $user): void | ||||
|     { | ||||
|         if ($user instanceof User) { | ||||
|             $this->user = $user; | ||||
| @@ -226,7 +226,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface | ||||
|         if ($start instanceof Carbon) { | ||||
|             $start = $data['start']->startOfDay(); | ||||
|         } | ||||
|         $end   = $data['end']; | ||||
|         $end = $data['end']; | ||||
|         if ($end instanceof Carbon) { | ||||
|             $end = $data['end']->endOfDay(); | ||||
|         } | ||||
|   | ||||
| @@ -60,7 +60,7 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|             ++$count; | ||||
|             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) { | ||||
|             $avg = bcdiv($total, (string) $count); | ||||
|         } | ||||
| @@ -82,21 +82,21 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
| 
 | ||||
|         // get all transactions:
 | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector    = app(GroupCollectorInterface::class); | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setAccounts($accounts)->setRange($start, $end); | ||||
|         $collector->setBudgets($budgets); | ||||
|         $journals     = $collector->getExtractedJournals(); | ||||
|         $journals = $collector->getExtractedJournals(); | ||||
| 
 | ||||
|         // loop transactions:
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             // prep data array for currency:
 | ||||
|             $budgetId                     = (int) $journal['budget_id']; | ||||
|             $budgetName                   = $journal['budget_name']; | ||||
|             $currencyId                   = (int) $journal['currency_id']; | ||||
|             $key                          = sprintf('%d-%d', $budgetId, $currencyId); | ||||
|             $budgetId   = (int) $journal['budget_id']; | ||||
|             $budgetName = $journal['budget_name']; | ||||
|             $currencyId = (int) $journal['currency_id']; | ||||
|             $key        = sprintf('%d-%d', $budgetId, $currencyId); | ||||
| 
 | ||||
|             $data[$key] ??= [ | ||||
|             $data[$key]                   ??= [ | ||||
|                 'id'                      => $budgetId, | ||||
|                 'name'                    => sprintf('%s (%s)', $budgetName, $journal['currency_name']), | ||||
|                 'sum'                     => '0', | ||||
| @@ -134,13 +134,13 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|             $collector->setBudgets($this->getBudgets()); | ||||
|         } | ||||
|         $collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation(); | ||||
|         $journals  = $collector->getExtractedJournals(); | ||||
|         $array     = []; | ||||
|         $journals = $collector->getExtractedJournals(); | ||||
|         $array    = []; | ||||
| 
 | ||||
|         foreach ($journals as $journal) { | ||||
|             $currencyId                                                                   = (int) $journal['currency_id']; | ||||
|             $budgetId                                                                     = (int) $journal['budget_id']; | ||||
|             $budgetName                                                                   = (string) $journal['budget_name']; | ||||
|             $currencyId = (int) $journal['currency_id']; | ||||
|             $budgetId   = (int) $journal['budget_id']; | ||||
|             $budgetName = (string) $journal['budget_name']; | ||||
| 
 | ||||
|             // catch "no category" entries.
 | ||||
|             if (0 === $budgetId) { | ||||
| @@ -148,7 +148,7 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|             } | ||||
| 
 | ||||
|             // info about the currency:
 | ||||
|             $array[$currencyId]                       ??= [ | ||||
|             $array[$currencyId] ??= [ | ||||
|                 'budgets'                 => [], | ||||
|                 'currency_id'             => $currencyId, | ||||
|                 'currency_name'           => $journal['currency_name'], | ||||
| @@ -183,7 +183,7 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|         return $array; | ||||
|     } | ||||
| 
 | ||||
|     public function setUser(null|Authenticatable|User $user): void | ||||
|     public function setUser(null | Authenticatable | User $user): void | ||||
|     { | ||||
|         if ($user instanceof User) { | ||||
|             $this->user = $user; | ||||
| @@ -207,11 +207,8 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|         ?Collection          $accounts = null, | ||||
|         ?Collection          $budgets = null, | ||||
|         ?TransactionCurrency $currency = null | ||||
|     ): array { | ||||
|         // app('log')->debug(sprintf('Now in %s', __METHOD__));
 | ||||
|         $start->startOfDay(); | ||||
|         $end->endOfDay(); | ||||
| 
 | ||||
|     ): array | ||||
|     { | ||||
|         // this collector excludes all transfers TO
 | ||||
|         // liabilities (which are also withdrawals)
 | ||||
|         // because those expenses only become expenses
 | ||||
| @@ -219,8 +216,12 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|         // TODO this filter must be somewhere in AccountRepositoryInterface because I suspect its needed more often (A113)
 | ||||
|         $repository = app(AccountRepositoryInterface::class); | ||||
|         $repository->setUser($this->user); | ||||
|         $subset     = $repository->getAccountsByType(config('firefly.valid_liabilities')); | ||||
|         $selection  = new Collection(); | ||||
|         $subset    = $repository->getAccountsByType(config('firefly.valid_liabilities')); | ||||
|         $selection = new Collection(); | ||||
| 
 | ||||
|         // default currency information for native stuff.
 | ||||
|         $convertToNative = app('preferences')->get('convert_to_native', false)->data; | ||||
|         $default         = app('amount')->getDefaultCurrency(); | ||||
| 
 | ||||
|         /** @var Account $account */ | ||||
|         foreach ($subset as $account) { | ||||
| @@ -230,12 +231,11 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|         } | ||||
| 
 | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector  = app(GroupCollectorInterface::class); | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setUser($this->user) | ||||
|             ->setRange($start, $end) | ||||
|             ->excludeDestinationAccounts($selection) | ||||
|             ->setTypes([TransactionType::WITHDRAWAL]) | ||||
|         ; | ||||
|                   ->setRange($start, $end) | ||||
|                   ->excludeDestinationAccounts($selection) | ||||
|                   ->setTypes([TransactionType::WITHDRAWAL]); | ||||
| 
 | ||||
|         if (null !== $accounts) { | ||||
|             $collector->setAccounts($accounts); | ||||
| @@ -247,7 +247,7 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|             $collector->setCurrency($currency); | ||||
|         } | ||||
|         $collector->setBudgets($budgets); | ||||
|         $journals   = $collector->getExtractedJournals(); | ||||
|         $journals = $collector->getExtractedJournals(); | ||||
| 
 | ||||
|         // same but for foreign currencies:
 | ||||
|         if (null !== $currency) { | ||||
| @@ -255,35 +255,51 @@ class OperationsRepository implements OperationsRepositoryInterface | ||||
|             /** @var GroupCollectorInterface $collector */ | ||||
|             $collector = app(GroupCollectorInterface::class); | ||||
|             $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) | ||||
|                 ->setForeignCurrency($currency)->setBudgets($budgets) | ||||
|             ; | ||||
|                       ->setForeignCurrency($currency)->setBudgets($budgets); | ||||
| 
 | ||||
|             if (null !== $accounts) { | ||||
|                 $collector->setAccounts($accounts); | ||||
|             } | ||||
|             $result    = $collector->getExtractedJournals(); | ||||
|             $result = $collector->getExtractedJournals(); | ||||
|             // 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):
 | ||||
|             $journals  = $result + $journals; | ||||
|             $journals = $result + $journals; | ||||
|         } | ||||
|         $array      = []; | ||||
|         $array = []; | ||||
| 
 | ||||
|         foreach ($journals as $journal) { | ||||
|             $currencyId                = (int) $journal['currency_id']; | ||||
|             $array[$currencyId] ??= [ | ||||
|             $currencyId            = (int) $journal['currency_id']; | ||||
|             $currencyName          = $journal['currency_name']; | ||||
|             $currencySymbol        = $journal['currency_symbol']; | ||||
|             $currencyCode          = $journal['currency_code']; | ||||
|             $currencyDecimalPlaces = $journal['currency_decimal_places']; | ||||
|             $field                 = 'amount'; | ||||
|             $foreignField          = 'foreign_amount'; | ||||
|             // if the user wants everything in native currency, use it.
 | ||||
|             if ($convertToNative && $default->id !== (int) $journal['currency_id']) { | ||||
|                 // use default currency info
 | ||||
|                 $currencyId            = $default->id; | ||||
|                 $currencyName          = $default->name; | ||||
|                 $currencySymbol        = $default->symbol; | ||||
|                 $currencyCode          = $default->code; | ||||
|                 $currencyDecimalPlaces = $default->decimal_places; | ||||
|                 $field                 = 'native_amount'; | ||||
|                 $foreignField          = 'native_foreign_amount'; | ||||
|             } | ||||
|             $array[$currencyId]        ??= [ | ||||
|                 'sum'                     => '0', | ||||
|                 'currency_id'             => $currencyId, | ||||
|                 'currency_name'           => $journal['currency_name'], | ||||
|                 'currency_symbol'         => $journal['currency_symbol'], | ||||
|                 'currency_code'           => $journal['currency_code'], | ||||
|                 'currency_decimal_places' => $journal['currency_decimal_places'], | ||||
|                 'currency_name'           => $currencyName, | ||||
|                 'currency_symbol'         => $currencySymbol, | ||||
|                 'currency_code'           => $currencyCode, | ||||
|                 'currency_decimal_places' => $currencyDecimalPlaces, | ||||
|             ]; | ||||
|             $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount'])); | ||||
|             $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal[$field])); | ||||
| 
 | ||||
|             // also do foreign amount:
 | ||||
|             $foreignId                 = (int) $journal['foreign_currency_id']; | ||||
|             if (0 !== $foreignId) { | ||||
|                 $array[$foreignId] ??= [ | ||||
|             $foreignId = (int) $journal['foreign_currency_id']; | ||||
|             if (0 !== $foreignId && $foreignId !== $currencyId) { | ||||
|                 $array[$foreignId]        ??= [ | ||||
|                     'sum'                     => '0', | ||||
|                     'currency_id'             => $foreignId, | ||||
|                     'currency_name'           => $journal['foreign_currency_name'], | ||||
|   | ||||
		Reference in New Issue
	
	Block a user