diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 0b58a4a137..86e0373509 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -408,10 +408,11 @@ class BudgetController extends Controller $cache->addProperty($currency->id); $cache->addProperty('chart.budget.period'); if ($cache->has()) { - // return response()->json($cache->get()); // @codeCoverageIgnore + return response()->json($cache->get()); // @codeCoverageIgnore } - $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $chartData = [ + $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $chartData = [ [ 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency->name]), 'type' => 'bar', @@ -430,7 +431,7 @@ class BudgetController extends Controller $currentStart = clone $start; while ($currentStart <= $end) { $title = $currentStart->formatLocalized($titleFormat); - $currentEnd = app('navigation')->endOfPeriod($currentStart, '1M'); + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); // default limit is no limit: $chartData[0]['entries'][$title] = 0; @@ -448,7 +449,7 @@ class BudgetController extends Controller $amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0'); $chartData[0]['entries'][$title] = round($amount, $currency->decimal_places); - $currentStart = app('navigation')->addPeriod($currentStart, '1M', 0); + $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); } $data = $this->generator->multiSet($chartData); @@ -478,20 +479,21 @@ class BudgetController extends Controller $cache->addProperty($currency->id); $cache->addProperty('chart.budget.no-budget'); if ($cache->has()) { - return response()->json($cache->get()); // @codeCoverageIgnore + return response()->json($cache->get()); // @codeCoverageIgnore } // the expenses: - $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $chartData = []; - $currentStart = clone $start; + $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $chartData = []; + $currentStart = clone $start; + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, '1M'); + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); $title = $currentStart->formatLocalized($titleFormat); $sum = $this->nbRepository->sumExpenses($currentStart, $currentEnd, $accounts, $currency); $amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0'); $chartData[$title] = round($amount, $currency->decimal_places); - $currentStart = app('navigation')->addPeriod($currentStart, '1M', 0); + $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); } $data = $this->generator->singleSet((string)trans('firefly.spent'), $chartData); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 49ef05285d..a9a5c92ff0 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -315,7 +315,7 @@ class CategoryController extends Controller $cache->addProperty('chart.category.period.no-cat'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - //return response()->json($cache->get()); // @codeCoverageIgnore + return response()->json($cache->get()); // @codeCoverageIgnore } /** @var NoCategoryRepositoryInterface $noCatRepository */ diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 25d09ff330..b6499ac84c 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -154,6 +154,7 @@ class ReportController extends Controller Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); $format = app('navigation')->preferredCarbonFormat($start, $end); $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $preferredRange = app('navigation')->preferredRangeFormat($start, $end); $ids = $accounts->pluck('id')->toArray(); // get journals for entire period: @@ -161,7 +162,8 @@ class ReportController extends Controller $chartData = []; /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setRange($start, $end)->setAccounts($accounts)->withAccountInformation(); + $collector->setRange($start, $end)->setAccounts($accounts) + ->withAccountInformation(); $journals = $collector->getExtractedJournals(); // loop. group by currency and by period. @@ -187,7 +189,7 @@ class ReportController extends Controller if (TransactionType::DEPOSIT === $journal['transaction_type_type'] || (TransactionType::TRANSFER === $journal['transaction_type_type'] && in_array( - $journal['destination_id'], $ids, true + $journal['destination_account_id'], $ids, true ))) { $key = 'earned'; } @@ -217,13 +219,12 @@ class ReportController extends Controller // loop all possible periods between $start and $end $currentStart = clone $start; while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, '1M'); + $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); $key = $currentStart->format($format); $title = $currentStart->formatLocalized($titleFormat); $income['entries'][$title] = round($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); $expense['entries'][$title] = round($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); - - $currentStart = app('navigation')->addPeriod($currentStart, '1M', 0); + $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); } $chartData[] = $income; diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 13bb1f6aab..0d6b2b7c83 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -94,7 +94,7 @@ class BudgetController extends Controller $cache->addProperty('budget-period-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore + // return $cache->get(); // @codeCoverageIgnore } // generate budget report right here. @@ -102,30 +102,41 @@ class BudgetController extends Controller $repository = app(BudgetRepositoryInterface::class); /** @var OperationsRepositoryInterface $opsRepository */ - $opsRepository= app(OperationsRepositoryInterface::class); + $opsRepository = app(OperationsRepositoryInterface::class); /** @var NoBudgetRepositoryInterface $nbRepository */ $nbRepository = app(NoBudgetRepositoryInterface::class); - $budgets = $repository->getBudgets(); - $data = $opsRepository->getBudgetPeriodReport($budgets, $accounts, $start, $end); - $noBudget = $nbRepository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget" - $data = array_merge($data, $noBudget); - $report = $this->filterPeriodReport($data); + $budgets = $repository->getBudgets(); + $periods = app('navigation')->listOfPeriods($start, $end); + $keyFormat = app('navigation')->preferredCarbonFormat($start, $end); - // depending on the carbon format (a reliable way to determine the general date difference) - // change the "listOfPeriods" call so the entire period gets included correctly. - $range = app('navigation')->preferredCarbonFormat($start, $end); - - if ('Y' === $range) { - $start->startOfYear(); + // list expenses for budgets in account(s) + $expenses = $opsRepository->listExpenses($start, $end, $accounts); + $report = []; + foreach ($expenses as $currency) { + foreach ($currency['budgets'] as $budget) { + foreach ($budget['transaction_journals'] as $journal) { + $key = sprintf('%d-%d', $budget['id'], $currency['currency_id']); + $dateKey = $journal['date']->format($keyFormat); + $report[$key] = $report[$key] ?? [ + 'id' => $budget['id'], + 'name' => sprintf('%s (%s)', $budget['name'], $currency['currency_name']), + 'sum' => '0', + 'currency_id' => $currency['currency_id'], + 'currency_name' => $currency['currency_name'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_code' => $currency['currency_code'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'entries' => [], + ]; + $report[$key] ['entries'][$dateKey] = $report[$key] ['entries'][$dateKey] ?? '0'; + $report[$key] ['entries'][$dateKey] = bcadd($journal['amount'], $report[$key] ['entries'][$dateKey]); + $report[$key] ['sum'] = bcadd($report[$key] ['sum'], $journal['amount']); + } + } } - if ('Y-m' === $range) { - $start->startOfMonth(); - } - - $periods = app('navigation')->listOfPeriods($start, $end); try { $result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); // @codeCoverageIgnoreStart diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index ddb2023415..0822e0b768 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -58,7 +58,7 @@ class CategoryController extends Controller $cache->addProperty('category-period-expenses-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - //return $cache->get(); // @codeCoverageIgnore + return $cache->get(); // @codeCoverageIgnore } /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); @@ -165,7 +165,7 @@ class CategoryController extends Controller $cache->addProperty('category-period-income-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - // return $cache->get(); // @codeCoverageIgnore + return $cache->get(); // @codeCoverageIgnore } /** @var OperationsRepositoryInterface $opsRepository */ @@ -271,7 +271,7 @@ class CategoryController extends Controller $cache->addProperty('category-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - // return $cache->get(); // @codeCoverageIgnore + return $cache->get(); // @codeCoverageIgnore } /** @var CategoryRepositoryInterface $repository */ diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 57b4afa01a..e7de2fe04e 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -185,6 +185,78 @@ class OperationsRepository implements OperationsRepositoryInterface return $data; } + /** + * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period + * which have the specified budget set to them. It's grouped per currency, with as few details in the array + * as possible. Amounts are always negative. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $budgets + * + * @return array + */ + public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + if (null !== $accounts && $accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if (null !== $budgets && $budgets->count() > 0) { + $collector->setBudgets($budgets); + } + if (null === $budgets || (null !== $budgets && 0 === $budgets->count())) { + $collector->setBudgets($this->getBudgets()); + } + $collector->withBudgetInformation(); + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $budgetId = (int)$journal['budget_id']; + $budgetName = (string)$journal['budget_name']; + + // catch "no category" entries. + if (0 === $budgetId) { + $budgetName = (string)trans('firefly.no_budget'); + } + + // info about the currency: + $array[$currencyId] = $array[$currencyId] ?? [ + 'budgets' => [], + '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'], + ]; + + // info about the categories: + $array[$currencyId]['budgets'][$budgetId] = $array[$currencyId]['budgets'][$budgetId] ?? [ + 'id' => $budgetId, + 'name' => $budgetName, + 'transaction_journals' => [], + ]; + + // add journal to array: + // only a subset of the fields. + $journalId = (int)$journal['transaction_journal_id']; + + + $array[$currencyId]['budgets'][$budgetId]['transaction_journals'][$journalId] = [ + 'amount' => app('steam')->negative($journal['amount']), + 'date' => $journal['date'], + ]; + + } + + return $array; + } + /** * @param User $user */ @@ -217,6 +289,8 @@ class OperationsRepository implements OperationsRepositoryInterface return $collector->getSum(); } + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @param Collection $budgets * @param Collection $accounts @@ -278,7 +352,6 @@ class OperationsRepository implements OperationsRepositoryInterface return $return; } - /** @noinspection MoreThanThreeArgumentsInspection */ /** * @param Carbon $start * @param Carbon $end diff --git a/app/Repositories/Budget/OperationsRepositoryInterface.php b/app/Repositories/Budget/OperationsRepositoryInterface.php index 0e24ab7e2b..b6c8ca3d68 100644 --- a/app/Repositories/Budget/OperationsRepositoryInterface.php +++ b/app/Repositories/Budget/OperationsRepositoryInterface.php @@ -109,4 +109,18 @@ interface OperationsRepositoryInterface public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null ): array; + /** + * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period + * which have the specified budget set to them. It's grouped per currency, with as few details in the array + * as possible. Amounts are always negative. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $budgets + * + * @return array + */ + public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array; + } \ No newline at end of file