diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 47aab7b635..677a7b4f66 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -154,7 +154,7 @@ class JournalCollector implements JournalCollectorInterface * @return LengthAwarePaginator * @throws FireflyException */ - public function getPaginatedJournals():LengthAwarePaginator + public function getPaginatedJournals(): LengthAwarePaginator { if ($this->run === true) { throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.'); @@ -244,6 +244,29 @@ class JournalCollector implements JournalCollectorInterface return $this; } + /** + * @param Collection $budgets + * + * @return JournalCollectorInterface + */ + public function setBudgets(Collection $budgets): JournalCollectorInterface + { + $budgetIds = $budgets->pluck('id')->toArray(); + if (count($budgetIds) === 0) { + return $this; + } + $this->joinBudgetTables(); + + $this->query->where( + function (EloquentBuilder $q) use ($budgetIds) { + $q->whereIn('budget_transaction.budget_id', $budgetIds); + $q->orWhereIn('budget_transaction_journal.budget_id', $budgetIds); + } + ); + + return $this; + } + /** * @param Collection $categories * @@ -259,10 +282,8 @@ class JournalCollector implements JournalCollectorInterface $this->query->where( function (EloquentBuilder $q) use ($categoryIds) { - if (count($categoryIds) > 0) { - $q->whereIn('category_transaction.category_id', $categoryIds); - $q->orWhereIn('category_transaction_journal.category_id', $categoryIds); - } + $q->whereIn('category_transaction.category_id', $categoryIds); + $q->orWhereIn('category_transaction_journal.category_id', $categoryIds); } ); diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index 1b37f7eb81..a9a70a8944 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -74,6 +74,14 @@ interface JournalCollectorInterface */ public function setBudget(Budget $budget): JournalCollectorInterface; + + /** + * @param Collection $budgets + * + * @return JournalCollectorInterface + */ + public function setBudgets(Collection $budgets): JournalCollectorInterface; + /** * @param Collection $categories * diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index c8d417d3b8..010f28f89d 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -17,11 +17,12 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use FireflyIII\Helpers\Collection\BudgetLine; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Support\Collection; -use Navigation; /** * Class BudgetReportHelper @@ -52,26 +53,66 @@ class BudgetReportHelper implements BudgetReportHelperInterface */ public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array { - $budgets = $this->repository->getBudgets(); - $queryResult = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end); - $data = []; - $periods = Navigation::listOfPeriods($start, $end); + $budgets = $this->repository->getBudgets(); + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAllAssetAccounts()->setRange($start, $end); + $collector->setBudgets($budgets); + $transactions = $collector->getJournals(); - // do budget "zero" - $emptyBudget = new Budget; - $emptyBudget->id = 0; - $emptyBudget->name = strval(trans('firefly.no_budget')); - $budgets->push($emptyBudget); - - // get all budgets and years. - foreach ($budgets as $budget) { - $data[$budget->id] = [ - 'name' => $budget->name, - 'entries' => $this->repository->filterAmounts($queryResult, $budget->id, $periods), - 'sum' => '0', - ]; + // this is the date format we need: + // define period to group on: + $carbonFormat = 'Y-m-d'; + // monthly report (for year) + if ($start->diffInMonths($end) > 1) { + $carbonFormat = 'Y-m'; } - // filter out empty ones and fill sum: + + // yearly report (for multi year) + if ($start->diffInMonths($end) > 12) { + $carbonFormat = 'Y'; + } + + // this is the set of transactions for this period + // in these budgets. Now they must be grouped (manually) + // id, period => amount + $data = []; + foreach ($transactions as $transaction) { + $budgetId = max(intval($transaction->transaction_journal_budget_id), intval($transaction->transaction_budget_id)); + $date = $transaction->date->format($carbonFormat); + + if (!isset($data[$budgetId])) { + $data[$budgetId]['name'] = $this->getBudgetName($budgetId, $budgets); + $data[$budgetId]['sum'] = '0'; + $data[$budgetId]['entries'] = []; + } + + if (!isset($data[$budgetId]['entries'][$date])) { + $data[$budgetId]['entries'][$date] = '0'; + } + $data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date], $transaction->transaction_amount); + } + // and now the same for stuff without a budget: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAllAssetAccounts()->setRange($start, $end); + $collector->setTypes([TransactionType::WITHDRAWAL]); + $collector->withoutBudget(); + $transactions = $collector->getJournals(); + + $data[0]['entries'] = []; + $data[0]['name'] = strval(trans('firefly.no_budget')); + $data[0]['sum'] = '0'; + + foreach ($transactions as $transaction) { + $date = $transaction->date->format($carbonFormat); + + if (!isset($data[0]['entries'][$date])) { + $data[0]['entries'][$date] = '0'; + } + $data[0]['entries'][$date] = bcadd($data[0]['entries'][$date], $transaction->transaction_amount); + } + $data = $this->filterBudgetPeriodReport($data); return $data; @@ -209,4 +250,25 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $data; } + /** + * @param int $budgetId + * @param Collection $budgets + * + * @return string + */ + private function getBudgetName(int $budgetId, Collection $budgets): string + { + + $first = $budgets->filter( + function (Budget $budget) use ($budgetId) { + return $budgetId === $budget->id; + } + ); + if (!is_null($first->first())) { + return $first->first()->name; + } + + return '(unknown)'; + } + } diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 09aa4a0475..8e76f2c5d5 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -36,7 +36,7 @@ class BudgetController extends Controller * @param Carbon $end * @param Collection $accounts * - * @return mixed|string + * @return string */ public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) { @@ -46,7 +46,7 @@ class BudgetController extends Controller $cache->addProperty('budget-period-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - return $cache->get(); + return $cache->get(); } $periods = Navigation::listOfPeriods($start, $end); diff --git a/resources/views/reports/partials/budget-period.twig b/resources/views/reports/partials/budget-period.twig index 53308f4606..6660019cbc 100644 --- a/resources/views/reports/partials/budget-period.twig +++ b/resources/views/reports/partials/budget-period.twig @@ -14,10 +14,17 @@ {{ info.name }} - {% for amount in info.entries %} - - {{ amount|formatAmount }} - + {% for key, period in periods %} + {% if(info.entries[key]) %} + + {{ info.entries[key]|formatAmount }} + + {% else %} + + {{ 0|formatAmount }} + + {% endif %} + {% endfor %} {{ info.sum|formatAmount }}