diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index 8a8e96d4b5..a2512f3a72 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -58,7 +58,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface public function generate(): string { $accountIds = join(',', $this->accounts->pluck('id')->toArray()); - $categoryIds = join(',', $this->budgets->pluck('id')->toArray()); + $budgetIds = join(',', $this->budgets->pluck('id')->toArray()); $expenses = $this->getExpenses(); $accountSummary = $this->summarizeByAccount($expenses); $budgetSummary = $this->summarizeByBudget($expenses); @@ -66,7 +66,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $topExpenses = $this->getTopExpenses(); // render! - return view('reports.budget.month', compact('accountIds', 'categoryIds', 'accountSummary', 'budgetSummary', 'averageExpenses', 'topExpenses')) + return view('reports.budget.month', compact('accountIds', 'budgetIds', 'accountSummary', 'budgetSummary', 'averageExpenses', 'topExpenses')) ->with('start', $this->start)->with('end', $this->end) ->with('budgets', $this->budgets) ->with('accounts', $this->accounts) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 6378a341e6..06faf92d07 100755 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -30,16 +30,6 @@ use Lang; */ class LoginController extends Controller { - /* - |-------------------------------------------------------------------------- - | Login Controller - |-------------------------------------------------------------------------- - | - | This controller handles authenticating users for the application and - | redirecting them to your home screen. The controller uses a trait - | to conveniently provide its functionality to your applications. - | - */ use AuthenticatesUsers; diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php index 95a5c82132..02a4757bc2 100644 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -31,16 +31,6 @@ use Illuminate\Support\Facades\Password; */ class PasswordController extends Controller { - /* - |-------------------------------------------------------------------------- - | Password Reset Controller - |-------------------------------------------------------------------------- - | - | This controller is responsible for handling password reset requests - | and uses a simple trait to include this behavior. You're free to - | explore this trait and override any methods you wish to tweak. - | - */ use ResetsPasswords; diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index dd2b9ec905..b56978153a 100755 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -36,16 +36,6 @@ use Validator; */ class RegisterController extends Controller { - /* - |-------------------------------------------------------------------------- - | Register Controller - |-------------------------------------------------------------------------- - | - | This controller handles the registration of new users as well as their - | validation and creation. By default this controller uses a trait to - | provide this functionality without requiring any additional code. - | - */ use RegistersUsers; diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 3e8c162572..1d99a0c179 100755 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -22,16 +22,6 @@ use Illuminate\Foundation\Auth\ResetsPasswords; */ class ResetPasswordController extends Controller { - /* - |-------------------------------------------------------------------------- - | Password Reset Controller - |-------------------------------------------------------------------------- - | - | This controller is responsible for handling password reset requests - | and uses a simple trait to include this behavior. You're free to - | explore this trait and override any methods you wish to tweak. - | - */ use ResetsPasswords; diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index f1e20b369a..8bf0047e6f 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -65,8 +65,11 @@ class AccountController extends Controller $cache->addProperty('chart.account.all'); $cache->addProperty($account->id); if ($cache->has()) { + Log::debug('Return chart.account.all from cache.'); + return Response::json($cache->get()); } + Log::debug('Regenerate chart.account.all from scratch.'); /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); @@ -458,8 +461,11 @@ class AccountController extends Controller $cache->addProperty('chart.account.account-balance-chart'); $cache->addProperty($accounts); if ($cache->has()) { + Log::debug('Return chart.account.account-balance-chart from cache.'); + return $cache->get(); } + Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); $chartData = []; foreach ($accounts as $account) { diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php new file mode 100644 index 0000000000..e053189066 --- /dev/null +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -0,0 +1,292 @@ +middleware( + function ($request, $next) { + $this->generator = app(GeneratorInterface::class); + $this->budgetRepository = app(BudgetRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Collection $accounts + * @param Collection $budgets + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others) + { + /** @var bool $others */ + $others = intval($others) === 1; + $cache = new CacheProperties; + $cache->addProperty('chart.budget.report.account-expense'); + $cache->addProperty($accounts); + $cache->addProperty($budgets); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } + + $names = []; + $set = $this->getExpenses($accounts, $budgets, $start, $end); + $grouped = $this->groupByOpposingAccount($set); + $chartData = []; + $total = '0'; + + foreach ($grouped as $accountId => $amount) { + if (!isset($names[$accountId])) { + $account = $this->accountRepository->find(intval($accountId)); + $names[$accountId] = $account->name; + } + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $chartData[$names[$accountId]] = $amount; + } + + // also collect all transactions NOT in these budgets. + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcmul($sum, '-1'); + $sum = bcsub($sum, $total); + $chartData[strval(trans('firefly.everything_else'))] = $sum; + } + + $data = $this->generator->pieChart($chartData); + $cache->store($data); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * @param Collection $budgets + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others) + { + /** @var bool $others */ + $others = intval($others) === 1; + $cache = new CacheProperties; + $cache->addProperty('chart.budget.report.budget-expense'); + $cache->addProperty($accounts); + $cache->addProperty($budgets); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } + + $names = []; + $set = $this->getExpenses($accounts, $budgets, $start, $end); + $grouped = $this->groupByBudget($set); + $total = '0'; + $chartData = []; + + foreach ($grouped as $budgetId => $amount) { + if (!isset($names[$budgetId])) { + $budget = $this->budgetRepository->find(intval($budgetId)); + $names[$budgetId] = $budget->name; + } + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $chartData[$names[$budgetId]] = $amount; + } + + // also collect all transactions NOT in these budgets. + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcmul($sum, '-1'); + $sum = bcsub($sum, $total); + $chartData[strval(trans('firefly.everything_else'))] = $sum; + } + + $data = $this->generator->pieChart($chartData); + $cache->store($data); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * @param Collection $budgets + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function mainChart(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) + { + $cache = new CacheProperties; + $cache->addProperty('chart.budget.report.main'); + $cache->addProperty($accounts); + $cache->addProperty($budgets); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } + + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); + $function = Navigation::preferredEndOfPeriod($start, $end); + $chartData = []; + $currentStart = clone $start; + + // prep chart data: + foreach ($budgets as $budget) { + $chartData[$budget->id] = [ + 'label' => $budget->name, + 'type' => 'bar', + 'entries' => [], + ]; + } + + while ($currentStart < $end) { + $currentEnd = clone $currentStart; + $currentEnd = $currentEnd->$function(); + $expenses = $this->groupByBudget($this->getExpenses($accounts, $budgets, $currentStart, $currentEnd)); + $label = $currentStart->formatLocalized($format); + + /** @var Budget $budget */ + foreach ($budgets as $budget) { + $chartData[$budget->id]['entries'][$label] = $expenses[$budget->id] ?? '0'; + } + $currentStart = clone $currentEnd; + $currentStart->addDay(); + } + + $data = $this->generator->multiSet($chartData); + $cache->store($data); + + return Response::json($data); + } + + + /** + * @param Collection $accounts + * @param Collection $budgets + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection + { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setBudgets($budgets)->withOpposingAccount()->disableFilter(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterExpenses($transactions, $accountIds); + + return $set; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByBudget(Collection $set): array + { + // group by category ID: + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $jrnlBudId = intval($transaction->transaction_journal_budget_id); + $transBudId = intval($transaction->transaction_budget_id); + $budgetId = max($jrnlBudId, $transBudId); + $grouped[$budgetId] = $grouped[$budgetId] ?? '0'; + $grouped[$budgetId] = bcadd($transaction->transaction_amount, $grouped[$budgetId]); + } + + return $grouped; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByOpposingAccount(Collection $set): array + { + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $accountId = $transaction->opposing_account_id; + $grouped[$accountId] = $grouped[$accountId] ?? '0'; + $grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]); + } + + return $grouped; + } +} \ No newline at end of file diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php index b5c465e455..92fa2f3246 100644 --- a/app/Jobs/Job.php +++ b/app/Jobs/Job.php @@ -22,16 +22,6 @@ use Illuminate\Bus\Queueable; */ abstract class Job { - /* - |-------------------------------------------------------------------------- - | Queueable Jobs - |-------------------------------------------------------------------------- - | - | This job base class provides a central location to place any logic that - | is shared across all of your jobs. The trait included with the class - | provides access to the "onQueue" and "delay" queue helper methods. - | - */ use Queueable; } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 179e0aa12b..d3bfa01572 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -211,7 +211,6 @@ class BillRepository implements BillRepositoryInterface $sum = bcadd($sum, $amount); Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $amount, $sum)); } - Log::debug('---'); } return $sum; @@ -245,7 +244,6 @@ class BillRepository implements BillRepositoryInterface $sum = bcadd($sum, $multi); Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $multi, $sum)); } - Log::debug('---'); } return $sum; diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index bfe3b857d0..105115f959 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -18,6 +18,7 @@ use Cache; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Support\Collection; +use Log; use Preferences as Prefs; /** @@ -75,6 +76,8 @@ class CacheProperties public function has(): bool { if (getenv('APP_ENV') == 'testing') { + Log::debug('APP_ENV is testing, cache disabled.'); + return false; } $this->md5(); @@ -118,7 +121,8 @@ class CacheProperties $this->md5 .= json_encode($property); } - + Log::debug(sprintf('Cache string is %s', $this->md5)); $this->md5 = md5($this->md5); + Log::debug(sprintf('Cache MD5 is %s', $this->md5)); } } diff --git a/public/js/ff/reports/budget/month.js b/public/js/ff/reports/budget/month.js index 90a4f3a5a2..0eeb33fa93 100644 --- a/public/js/ff/reports/budget/month.js +++ b/public/js/ff/reports/budget/month.js @@ -13,16 +13,8 @@ $(function () { "use strict"; drawChart(); - $('#budgets-in-pie-chart-checked').on('change', function () { - redrawPieChart('budgets-in-pie-chart', categoryIncomeUri); - }); - $('#budgets-out-pie-chart-checked').on('change', function () { - redrawPieChart('budgets-out-pie-chart', categoryExpenseUri); - }); - - $('#accounts-in-pie-chart-checked').on('change', function () { - redrawPieChart('accounts-in-pie-chart', accountIncomeUri); + redrawPieChart('budgets-out-pie-chart', budgetExpenseUri); }); $('#accounts-out-pie-chart-checked').on('change', function () { @@ -39,9 +31,7 @@ function drawChart() { stackedColumnChart(mainUri, 'in-out-chart'); // draw pie chart of income, depending on "show other transactions too": - redrawPieChart('budgets-in-pie-chart', categoryIncomeUri); - redrawPieChart('budgets-out-pie-chart', categoryExpenseUri); - redrawPieChart('accounts-in-pie-chart', accountIncomeUri); + redrawPieChart('budgets-out-pie-chart', budgetExpenseUri); redrawPieChart('accounts-out-pie-chart', accountExpenseUri); diff --git a/resources/views/reports/budget/month.twig b/resources/views/reports/budget/month.twig index aeccc2dbde..d8623ebf32 100644 --- a/resources/views/reports/budget/month.twig +++ b/resources/views/reports/budget/month.twig @@ -84,22 +84,20 @@ {% endif %} - {% if accounts.count > 1 %} -