diff --git a/app/Helpers/Report/BalanceReportHelper.php b/app/Helpers/Report/BalanceReportHelper.php index 76e41779fe..355db28dc0 100644 --- a/app/Helpers/Report/BalanceReportHelper.php +++ b/app/Helpers/Report/BalanceReportHelper.php @@ -84,10 +84,13 @@ class BalanceReportHelper implements BalanceReportHelperInterface Log::debug('Create and add balance line for budget #' . $budget->id); $balance->addBalanceLine($this->createBalanceLine($budget, $repetition, $accounts)); } + $noBudgetLine = $this->createNoBudgetLine($accounts, $start, $end); + $coveredByTagLine = $this->createTagsBalanceLine($accounts, $start, $end); + $leftUnbalancedLine = $this->createLeftUnbalancedLine($noBudgetLine, $coveredByTagLine); - $balance->addBalanceLine($this->createEmptyBalanceLine($accounts, $spentData)); - $balance->addBalanceLine($this->createTagsBalanceLine($accounts, $start, $end)); - $balance->addBalanceLine($this->createDifferenceBalanceLine($accounts, $spentData, $start, $end)); + $balance->addBalanceLine($noBudgetLine); + $balance->addBalanceLine($coveredByTagLine); + $balance->addBalanceLine($leftUnbalancedLine); $balance->setBalanceHeader($header); // remove budgets without expenses from balance lines: @@ -155,7 +158,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface private function createBalanceLine(BudgetModel $budget, LimitRepetition $repetition, Collection $accounts): BalanceLine { Log::debug('Create line for budget #' . $budget->id . ' and repetition #' . $repetition->id); - $line = new BalanceLine; + $line = new BalanceLine; $budget->amount = $repetition->amount; $line->setBudget($budget); @@ -178,90 +181,55 @@ class BalanceReportHelper implements BalanceReportHelperInterface } /** - * @param Account $account - * @param Collection $spentData - * @param Collection $tagsLeft + * @param BalanceLine $noBudgetLine + * @param BalanceLine $coveredByTagLine * - * @return BalanceEntry + * @return BalanceLine */ - private function createDifferenceBalanceEntry(Account $account, Collection $spentData, Collection $tagsLeft): BalanceEntry + private function createLeftUnbalancedLine(BalanceLine $noBudgetLine, BalanceLine $coveredByTagLine): BalanceLine { - $entry = $spentData->filter( - function (TransactionJournal $model) use ($account) { - return $model->account_id == $account->id && is_null($model->budget_id); - } - ); - $spent = '0'; - if (!is_null($entry->first())) { - $spent = $entry->first()->spent; - } - $leftEntry = $tagsLeft->filter( - function (Tag $tag) use ($account) { - return $tag->account_id == $account->id; - } - ); - $left = '0'; - if (!is_null($leftEntry->first())) { - $left = $leftEntry->first()->sum; - } - $diffValue = bcadd($spent, $left); + $line = new BalanceLine; + $line->setRole(BalanceLine::ROLE_DIFFROLE); + $noBudgetEntries = $noBudgetLine->getBalanceEntries(); + $tagEntries = $coveredByTagLine->getBalanceEntries(); + + /** @var BalanceEntry $entry */ + foreach ($noBudgetEntries as $entry) { + $account = $entry->getAccount(); + $tagEntry = $tagEntries->filter( + function (BalanceEntry $current) use ($account) { + return $current->getAccount()->id === $account->id; + } + ); + if ($tagEntry->first()) { + // found corresponding entry. As we should: + $newEntry = new BalanceEntry; + $newEntry->setAccount($account); + $spent = bcadd($tagEntry->first()->getLeft(), $entry->getSpent()); + $newEntry->setSpent($spent); + $line->addBalanceEntry($newEntry); + } + } + + return $line; - // difference: - $diffEntry = new BalanceEntry; - $diffEntry->setAccount($account); - $diffEntry->setSpent($diffValue); - return $diffEntry; } /** * @param Collection $accounts - * @param Collection $spentData * @param Carbon $start * @param Carbon $end * - * - * * @return BalanceLine */ - private function createDifferenceBalanceLine(Collection $accounts, Collection $spentData, Carbon $start, Carbon $end): BalanceLine - { - $diff = new BalanceLine; - $tagsLeft = $this->allCoveredByBalancingActs($accounts, $start, $end); - - $diff->setRole(BalanceLine::ROLE_DIFFROLE); - - /** @var Account $account */ - foreach ($accounts as $account) { - $diffEntry = $this->createDifferenceBalanceEntry($account, $spentData, $tagsLeft); - $diff->addBalanceEntry($diffEntry); - - } - - return $diff; - } - - /** - * @param Collection $accounts - * @param Collection $spentData - * - * @return BalanceLine - */ - private function createEmptyBalanceLine(Collection $accounts, Collection $spentData): BalanceLine + private function createNoBudgetLine(Collection $accounts, Carbon $start, Carbon $end): BalanceLine { $empty = new BalanceLine; foreach ($accounts as $account) { - $entry = $spentData->filter( - function (TransactionJournal $model) use ($account) { - return $model->account_id == $account->id && is_null($model->budget_id); - } - ); - $spent = '0'; - if (!is_null($entry->first())) { - $spent = $entry->first()->spent; - } - + $spent = $this->budgetRepository->spentInPeriodWithoutBudget(new Collection([$account]), $start, $end); + //$spent ='0'; // budget $budgetEntry = new BalanceEntry; $budgetEntry->setAccount($account); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 8881ecdf6d..0e6c80f587 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -13,6 +13,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Log; @@ -881,7 +882,12 @@ class BudgetRepository implements BudgetRepositoryInterface ->before($end) ->sortCorrectly() ->after($start) - ->leftJoin('transactions as source', 'source.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin( + 'transactions as source', + function (JoinClause $join) { + $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); + } + ) ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->whereIn('budget_transaction_journal.budget_id', $budgets->pluck('id')->toArray()); // add account id's, if relevant: @@ -900,7 +906,12 @@ class BudgetRepository implements BudgetRepositoryInterface ->after($start) ->leftJoin('transactions as related', 'related.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'related.id') - ->leftJoin('transactions as source', 'source.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin( + 'transactions as source', + function (JoinClause $join) { + $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); + } + ) ->whereIn('budget_transaction.budget_id', $budgets->pluck('id')->toArray()); if (count($accountIds) > 0) { @@ -925,14 +936,25 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function journalsInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): Collection { + $accountIds = []; + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + } + /** @var Collection $set */ - $set = $this->user + $query = $this->user ->transactionjournals() ->expanded() ->sortCorrectly() ->transactionTypes([TransactionType::WITHDRAWAL]) ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->whereNull('budget_transaction_journal.id') + ->leftJoin( + 'transactions as source', + function (JoinClause $join) { + $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); + } + ) ->before($end) ->after($start)->with( [ @@ -941,8 +963,14 @@ class BudgetRepository implements BudgetRepositoryInterface }, 'transactions.budgets', ] - )->get(TransactionJournal::queryFields()); + ); + // add account id's, if relevant: + if (count($accountIds) > 0) { + $query->whereIn('source.account_id', $accountIds); + } + + $set = $query->get(TransactionJournal::queryFields()); $set = $set->filter( function (TransactionJournal $journal) { foreach ($journal->transactions as $t) { @@ -1065,4 +1093,26 @@ class BudgetRepository implements BudgetRepositoryInterface return $limit; } + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string + { + $set = $this->journalsInPeriodWithoutBudget($accounts, $start, $end); + //Log::debug('spentInPeriod set count is ' . $set->count()); + $sum = '0'; + /** @var TransactionJournal $journal */ + foreach ($set as $journal) { + $sum = bcadd($sum, TransactionJournal::amount($journal)); + } + + Log::debug('spentInPeriodWithoutBudget between ' . $start->format('Y-m-d') . ' and ' . $end->format('Y-m-d') . ' is ' . $sum); + + return $sum; + } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 780a3dbf2a..5c34a58bd7 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -53,6 +53,7 @@ interface BudgetRepositoryInterface /** * This method returns the oldest journal or transaction date known to this budget. * Will cache result. + * * @param Budget $budget * * @return Carbon @@ -218,6 +219,15 @@ interface BudgetRepositoryInterface */ public function journalsInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): Collection; + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param Collection $budgets * @param Collection $accounts