generator = app(AccountChartGeneratorInterface::class); } /** * @param Account $account * * @return \Illuminate\Http\JsonResponse */ public function all(Account $account) { $cache = new CacheProperties(); $cache->addProperty('account-all-chart'); $cache->addProperty($account->id); if ($cache->has()) { return Response::json($cache->get()); } /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $start = $repository->oldestJournalDate($account); $end = new Carbon; $format = (string)trans('config.month_and_day'); $range = Steam::balanceInRange($account, $start, $end); $current = clone $start; $previous = array_values($range)[0]; $chartData = []; while ($end >= $current) { $theDate = $current->format('Y-m-d'); $balance = $range[$theDate] ?? $previous; $label = $current->formatLocalized($format); $chartData[$label] = $balance; $previous = $balance; $current->addDay(); } /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); $data = $generator->singleSet($account->name, $chartData); $cache->store($data); return Response::json($data); } /** * Shows the balances for all the user's expense accounts. * * @param AccountRepositoryInterface $repository * * @return \Illuminate\Http\JsonResponse */ public function expenseAccounts(AccountRepositoryInterface $repository) { $start = clone session('start', Carbon::now()->startOfMonth()); $end = clone session('end', Carbon::now()->endOfMonth()); $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('expenseAccounts'); $cache->addProperty('accounts'); if ($cache->has()) { return Response::json($cache->get()); } $accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]); $start->subDay(); $ids = $accounts->pluck('id')->toArray(); $startBalances = Steam::balancesById($ids, $start); $endBalances = Steam::balancesById($ids, $end); $chartData = []; foreach ($accounts as $account) { $id = $account->id; $startBalance = $startBalances[$id] ?? '0'; $endBalance = $endBalances[$id] ?? '0'; $diff = bcsub($endBalance, $startBalance); if (bccomp($diff, '0') !== 0) { $chartData[$account->name] = round($diff, 2); $account->difference = round($diff, 2); } } arsort($chartData); /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); $data = $generator->singleSet(trans('firefly.spent'), $chartData); $cache->store($data); return Response::json($data); } /** * @param JournalCollectorInterface $collector * @param Account $account * @param Carbon $start * @param Carbon $end * * @return \Illuminate\Http\JsonResponse */ public function expenseBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) { $cache = new CacheProperties; $cache->addProperty($account->id); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('expenseByBudget'); if ($cache->has()) { return Response::json($cache->get()); } // grab all journals: $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]); $transactions = $collector->getJournals(); $result = []; /** @var Transaction $transaction */ foreach ($transactions as $transaction) { $jrnlBudgetId = intval($transaction->transaction_journal_budget_id); $transBudgetId = intval($transaction->transaction_budget_id); $budgetId = max($jrnlBudgetId, $transBudgetId); $result[$budgetId] = $result[$budgetId] ?? '0'; $result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]); } $names = $this->getBudgetNames(array_keys($result)); $data = $this->generator->pieChart($result, $names); $cache->store($data); return Response::json($data); } /** * @param JournalCollectorInterface $collector * @param Account $account * @param Carbon $start * @param Carbon $end * * @return \Illuminate\Http\JsonResponse */ public function expenseCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) { $cache = new CacheProperties; $cache->addProperty($account->id); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('expenseByCategory'); if ($cache->has()) { return Response::json($cache->get()); } // grab all journals: $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]); $transactions = $collector->getJournals(); $result = []; /** @var Transaction $transaction */ foreach ($transactions as $transaction) { $jrnlCatId = intval($transaction->transaction_journal_category_id); $transCatId = intval($transaction->transaction_category_id); $categoryId = max($jrnlCatId, $transCatId); $result[$categoryId] = $result[$categoryId] ?? '0'; $result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]); } $names = $this->getCategoryNames(array_keys($result)); $data = $this->generator->pieChart($result, $names); $cache->store($data); return Response::json($data); } /** * Shows the balances for all the user's frontpage accounts. * * @param AccountRepositoryInterface $repository * * @return \Illuminate\Http\JsonResponse */ public function frontpage(AccountRepositoryInterface $repository) { $start = clone session('start', Carbon::now()->startOfMonth()); $end = clone session('end', Carbon::now()->endOfMonth()); $frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray()); $accounts = $repository->getAccountsById($frontPage->data); return Response::json($this->accountBalanceChart($accounts, $start, $end)); } /** * @param JournalCollectorInterface $collector * @param Account $account * @param Carbon $start * @param Carbon $end * * @return \Illuminate\Http\JsonResponse */ public function incomeCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) { $cache = new CacheProperties; $cache->addProperty($account->id); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('incomeByCategory'); if ($cache->has()) { return Response::json($cache->get()); } // grab all journals: $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]); $transactions = $collector->getJournals(); $result = []; /** @var Transaction $transaction */ foreach ($transactions as $transaction) { $jrnlCatId = intval($transaction->transaction_journal_category_id); $transCatId = intval($transaction->transaction_category_id); $categoryId = max($jrnlCatId, $transCatId); $result[$categoryId] = $result[$categoryId] ?? '0'; $result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]); } $names = $this->getCategoryNames(array_keys($result)); $data = $this->generator->pieChart($result, $names); $cache->store($data); return Response::json($data); } /** * @param Account $account * @param string $date * * @return \Illuminate\Http\JsonResponse * @throws FireflyException */ public function period(Account $account, string $date) { try { $start = new Carbon($date); } catch (Exception $e) { Log::error($e->getMessage()); throw new FireflyException('"' . e($date) . '" does not seem to be a valid date. Should be in the format YYYY-MM-DD'); } $range = Preferences::get('viewRange', '1M')->data; $end = Navigation::endOfPeriod($start, $range); // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('frontpage'); $cache->addProperty('specificPeriod'); $cache->addProperty($account->id); if ($cache->has()) { return Response::json($cache->get()); } $format = (string)trans('config.month_and_day'); $range = Steam::balanceInRange($account, $start, $end); $current = clone $start; $previous = array_values($range)[0]; $labels = []; $chartData = []; while ($end >= $current) { $theDate = $current->format('Y-m-d'); $balance = $range[$theDate] ?? $previous; $labels[] = $current->formatLocalized($format); $chartData[] = $balance; $previous = $balance; $current->addDay(); } $data = $this->generator->single($account, $labels, $chartData); $cache->store($data); return Response::json($data); } /** * Shows the balances for a given set of dates and accounts. * * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ public function report(Collection $accounts, Carbon $start, Carbon $end) { return Response::json($this->accountBalanceChart($accounts, $start, $end)); } /** * Shows the balances for all the user's revenue accounts. * * @param AccountRepositoryInterface $repository * * @return \Illuminate\Http\JsonResponse */ public function revenueAccounts(AccountRepositoryInterface $repository) { $start = clone session('start', Carbon::now()->startOfMonth()); $end = clone session('end', Carbon::now()->endOfMonth()); $chartData = []; $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('revenueAccounts'); $cache->addProperty('accounts'); if ($cache->has()) { return Response::json($cache->get()); } $accounts = $repository->getAccountsByType([AccountType::REVENUE]); $start->subDay(); $ids = $accounts->pluck('id')->toArray(); $startBalances = Steam::balancesById($ids, $start); $endBalances = Steam::balancesById($ids, $end); foreach ($accounts as $account) { $id = $account->id; $startBalance = $startBalances[$id] ?? '0'; $endBalance = $endBalances[$id] ?? '0'; $diff = bcsub($endBalance, $startBalance); $diff = bcmul($diff, '-1'); $account->difference = round($diff, 2); if (bccomp($diff, '0') !== 0) { $chartData[$account->name] = round($diff, 2); $account->difference = round($diff, 2); } } asort($chartData); /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); $data = $generator->singleSet(trans('firefly.spent'), $chartData); $cache->store($data); $data = $this->generator->revenueAccounts($accounts, $start, $end); $cache->store($data); return Response::json($data); } /** * Shows an account's balance for a single month. * * @param Account $account * * @return \Symfony\Component\HttpFoundation\Response */ public function single(Account $account) { $start = clone session('start', Carbon::now()->startOfMonth()); $end = clone session('end', Carbon::now()->endOfMonth()); // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('frontpage'); $cache->addProperty('single'); $cache->addProperty($account->id); if ($cache->has()) { return Response::json($cache->get()); } $format = (string)trans('config.month_and_day'); $range = Steam::balanceInRange($account, $start, $end); $current = clone $start; $previous = array_values($range)[0]; $labels = []; $chartData = []; while ($end >= $current) { $theDate = $current->format('Y-m-d'); $balance = $range[$theDate] ?? $previous; $labels[] = $current->formatLocalized($format); $chartData[] = $balance; $previous = $balance; $current->addDay(); } $data = $this->generator->single($account, $labels, $chartData); $cache->store($data); return Response::json($data); } /** * @param Collection $accounts * @param Carbon $start * @param Carbon $end * * @return array */ private function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array { // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('account-balance-chart'); $cache->addProperty($accounts); if ($cache->has()) { return $cache->get(); } $chartData = []; foreach ($accounts as $account) { $currentSet = [ 'label' => $account->name, 'entries' => [], ]; $currentStart = clone $start; $range = Steam::balanceInRange($account, $start, clone $end); $previous = round(array_values($range)[0], 2); while ($currentStart <= $end) { $format = $currentStart->format('Y-m-d'); $label = $currentStart->formatLocalized(strval(trans('config.month_and_day'))); $balance = isset($range[$format]) ? round($range[$format], 2) : $previous; $previous = $balance; $currentStart->addDay(); $currentSet['entries'][$label] = $balance; } $chartData[] = $currentSet; } /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); $data = $generator->multiSet($chartData); $cache->store($data); return $data; } /** * @param array $budgetIds * * @return array */ private function getBudgetNames(array $budgetIds): array { /** @var BudgetRepositoryInterface $repository */ $repository = app(BudgetRepositoryInterface::class); $budgets = $repository->getBudgets(); $grouped = $budgets->groupBy('id')->toArray(); $return = []; foreach ($budgetIds as $budgetId) { if (isset($grouped[$budgetId])) { $return[$budgetId] = $grouped[$budgetId][0]['name']; } } $return[0] = trans('firefly.no_budget'); return $return; } /** * Small helper function for some of the charts. * * @param array $categoryIds * * @return array */ private function getCategoryNames(array $categoryIds): array { /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); $categories = $repository->getCategories(); $grouped = $categories->groupBy('id')->toArray(); $return = []; foreach ($categoryIds as $categoryId) { if (isset($grouped[$categoryId])) { $return[$categoryId] = $grouped[$categoryId][0]['name']; } } $return[0] = trans('firefly.noCategory'); return $return; } }