diff --git a/app/Generator/Chart/Basic/ChartJsGenerator.php b/app/Generator/Chart/Basic/ChartJsGenerator.php index ffc2b14f38..a4a4caa8c1 100644 --- a/app/Generator/Chart/Basic/ChartJsGenerator.php +++ b/app/Generator/Chart/Basic/ChartJsGenerator.php @@ -52,11 +52,16 @@ class ChartJsGenerator implements GeneratorInterface */ public function multiSet(array $data): array { + reset($data); + $first = current($data); + $labels = array_keys($first['entries']); + $chartData = [ 'count' => count($data), - 'labels' => array_keys($data[0]['entries']), // take ALL labels from the first set. + 'labels' => $labels, // take ALL labels from the first set. 'datasets' => [], ]; + unset($first, $labels); foreach ($data as $set) { $chartData['datasets'][] = [ diff --git a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php deleted file mode 100644 index c311d70298..0000000000 --- a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php +++ /dev/null @@ -1,67 +0,0 @@ - 2, - 'labels' => [], - 'datasets' => [ - [ - 'label' => trans('firefly.spent'), - 'data' => [], - ], - [ - 'label' => trans('firefly.earned'), - 'data' => [], - ], - ], - ]; - - foreach ($entries as $entry) { - $data['labels'][] = $entry[1]; - $spent = $entry[2]; - $earned = $entry[3]; - - $data['datasets'][0]['data'][] = bccomp($spent, '0') === 0 ? null : round(bcmul($spent, '-1'), 4); - $data['datasets'][1]['data'][] = bccomp($earned, '0') === 0 ? null : round($earned, 4); - } - - return $data; - } - - /** - * @param Collection $entries - * - * @return array - */ - public function frontpage(Collection $entries): array - { - $data = [ - 'count' => 1, - 'labels' => [], - 'datasets' => [ - [ - 'label' => trans('firefly.spent'), - 'data' => [], - ], - ], - ]; - foreach ($entries as $entry) { - if ($entry->spent != 0) { - $data['labels'][] = $entry->name; - $data['datasets'][0]['data'][] = round(bcmul($entry->spent, '-1'), 2); - } - } - - return $data; - } - - /** - * @param array $entries - * - * @return array - */ - public function mainReportChart(array $entries): array - { - - $data = [ - 'count' => 0, - 'labels' => array_keys($entries), - 'datasets' => [], - ]; - - - foreach ($entries as $row) { - foreach ($row['in'] as $categoryId => $amount) { - // get in: - $data['datasets'][$categoryId . 'in']['data'][] = round($amount, 2); - - // get out: - $opposite = $row['out'][$categoryId]; - $data['datasets'][$categoryId . 'out']['data'][] = round($opposite, 2); - - // set name: - $data['datasets'][$categoryId . 'out']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')'; - $data['datasets'][$categoryId . 'in']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.income'))) . ')'; - - } - } - - // remove empty rows: - foreach ($data['datasets'] as $key => $content) { - if (array_sum($content['data']) === 0.0) { - unset($data['datasets'][$key]); - } - } - - // re-key the datasets array: - $data['datasets'] = array_values($data['datasets']); - $data['count'] = count($data['datasets']); - - return $data; - } - - /** - * - * @param Collection $entries - * - * @return array - */ - public function period(Collection $entries): array - { - return $this->all($entries); - - } - - /** - * @param array $entries - * - * @return array - */ - public function pieChart(array $entries): array - { - $data = [ - 'datasets' => [ - 0 => [], - ], - 'labels' => [], - ]; - $index = 0; - foreach ($entries as $entry) { - - if (bccomp($entry['amount'], '0') === -1) { - $entry['amount'] = bcmul($entry['amount'], '-1'); - } - - $data['datasets'][0]['data'][] = round($entry['amount'], 2); - $data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index); - $data['labels'][] = $entry['name']; - $index++; - } - - return $data; - } - - /** - * @param array $entries - * - * @return array - */ - public function reportPeriod(array $entries): array - { - - $data = [ - 'labels' => array_keys($entries), - 'datasets' => [ - 0 => [ - 'label' => trans('firefly.earned'), - 'data' => [], - ], - 1 => [ - 'label' => trans('firefly.spent'), - 'data' => [], - ], - ], - 'count' => 2, - ]; - - foreach ($entries as $label => $entry) { - // data set 0 is budgeted - // data set 1 is spent: - $data['datasets'][0]['data'][] = round($entry['earned'], 2); - $data['datasets'][1]['data'][] = round(bcmul($entry['spent'], '-1'), 2); - - } - - return $data; - - } - -} diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index a2ff4338c7..c0ff85e2e3 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; -use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; +use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; @@ -26,7 +26,6 @@ use Illuminate\Support\Collection; use Navigation; use Preferences; use Response; -use stdClass; /** * Class CategoryController @@ -35,7 +34,7 @@ use stdClass; */ class CategoryController extends Controller { - /** @var CategoryChartGeneratorInterface */ + /** @var GeneratorInterface */ protected $generator; /** @@ -45,7 +44,7 @@ class CategoryController extends Controller { parent::__construct(); // create chart generator: - $this->generator = app(CategoryChartGeneratorInterface::class); + $this->generator = app(GeneratorInterface::class); } /** @@ -59,34 +58,42 @@ class CategoryController extends Controller */ public function all(CRI $repository, AccountRepositoryInterface $accountRepository, Category $category) { - $start = $repository->firstUseDate($category); - $range = Preferences::get('viewRange', '1M')->data; - $start = Navigation::startOfPeriod($start, $range); - $categoryCollection = new Collection([$category]); - $end = new Carbon; - $entries = new Collection; - $cache = new CacheProperties; - $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('all'); - $cache->addProperty('categories'); + $cache = new CacheProperties; + $cache->addProperty('chart.category.all'); + $cache->addProperty($category->id); if ($cache->has()) { return Response::json($cache->get()); } + $start = $repository->firstUseDate($category); + $range = Preferences::get('viewRange', '1M')->data; + $start = Navigation::startOfPeriod($start, $range); + $end = new Carbon; + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $chartData = [ + [ + 'label' => strval(trans('firefly.spent')), + 'entries' => [], + 'type' => 'bar', + ], + [ + 'label' => strval(trans('firefly.earned')), + 'entries' => [], + 'type' => 'bar', + ], + ]; + while ($start <= $end) { - $currentEnd = Navigation::endOfPeriod($start, $range); - $spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $currentEnd); - $earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $currentEnd); - $date = Navigation::periodShow($start, $range); - $entries->push([clone $start, $date, $spent, $earned]); - $start = Navigation::addPeriod($start, $range, 0); + $currentEnd = Navigation::endOfPeriod($start, $range); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); + $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); + $label = Navigation::periodShow($start, $range); + $chartData[0]['entries'][$label] = bcmul($spent, '-1'); + $chartData[1]['entries'][$label] = $earned; + $start = Navigation::addPeriod($start, $range, 0); } - $entries = $entries->reverse(); - $entries = $entries->slice(0, 48); - $entries = $entries->reverse(); - $data = $this->generator->all($entries); + + $data = $this->generator->multiSet($chartData); $cache->store($data); return Response::json($data); @@ -122,34 +129,29 @@ class CategoryController extends Controller $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty('category'); - $cache->addProperty('frontpage'); + $cache->addProperty('chart.category.frontpage'); if ($cache->has()) { return Response::json($cache->get()); } + $chartData = []; $categories = $repository->getCategories(); $accounts = $accountRepository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); - $set = new Collection; /** @var Category $category */ foreach ($categories as $category) { $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); if (bccomp($spent, '0') === -1) { - $category->spent = $spent; - $set->push($category); + $chartData[$category->name] = bcmul($spent, '-1'); } } - // this is a "fake" entry for the "no category" entry. - $entry = new stdClass; - $entry->name = trans('firefly.no_category'); - $entry->spent = $repository->spentInPeriodWithoutCategory(new Collection, $start, $end); - $set->push($entry); + $chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1'); - $set = $set->sortBy('spent'); - $data = $this->generator->frontpage($set); + // sort + arsort($chartData); + + $data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData); $cache->store($data); return Response::json($data); - } /** @@ -166,35 +168,43 @@ class CategoryController extends Controller $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty('category-period-chart'); + $cache->addProperty('chart.category.period'); $cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($category); if ($cache->has()) { - return $cache->get(); } - $expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end); - $income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end); - $periods = Navigation::listOfPeriods($start, $end); + $expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end); + $income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end); + $periods = Navigation::listOfPeriods($start, $end); + $chartData = [ + [ + 'label' => strval(trans('firefly.spent')), + 'entries' => [], + 'type' => 'bar', + ], + [ + 'label' => strval(trans('firefly.earned')), + 'entries' => [], + 'type' => 'bar', + ], + ]; - - // join them: - $result = []; foreach (array_keys($periods) as $period) { - $nice = $periods[$period]; - $result[$nice] = [ - 'earned' => $income[$category->id]['entries'][$period] ?? '0', - 'spent' => $expenses[$category->id]['entries'][$period] ?? '0', - ]; + $label = $periods[$period]; + $spent = $expenses[$category->id]['entries'][$period] ?? '0'; + $chartData[0]['entries'][$label] = bcmul($spent, '-1'); + $chartData[1]['entries'][$label] = $income[$category->id]['entries'][$period] ?? '0'; } - $data = $this->generator->reportPeriod($result); + + $data = $this->generator->multiSet($chartData); + $cache->store($data); return Response::json($data); } /** * @param CRI $repository - * @param Category $category * @param Collection $accounts * @param Carbon $start * @param Carbon $end @@ -206,26 +216,36 @@ class CategoryController extends Controller $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty('no-category-period-chart'); + $cache->addProperty('chart.category.period.no-cat'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - return $cache->get(); } - $expenses = $repository->periodExpensesNoCategory($accounts, $start, $end); - $income = $repository->periodIncomeNoCategory($accounts, $start, $end); - $periods = Navigation::listOfPeriods($start, $end); + $expenses = $repository->periodExpensesNoCategory($accounts, $start, $end); + $income = $repository->periodIncomeNoCategory($accounts, $start, $end); + $periods = Navigation::listOfPeriods($start, $end); + $chartData = [ + [ + 'label' => strval(trans('firefly.spent')), + 'entries' => [], + 'type' => 'bar', + ], + [ + 'label' => strval(trans('firefly.earned')), + 'entries' => [], + 'type' => 'bar', + ], + ]; - // join them: - $result = []; foreach (array_keys($periods) as $period) { - $nice = $periods[$period]; - $result[$nice] = [ - 'earned' => $income['entries'][$period] ?? '0', - 'spent' => $expenses['entries'][$period] ?? '0', - ]; + $label = $periods[$period]; + $spent = $expenses['entries'][$period] ?? '0'; + $chartData[0]['entries'][$label] = bcmul($spent, '-1'); + $chartData[1]['entries'][$label] = $income['entries'][$period] ?? '0'; + } - $data = $this->generator->reportPeriod($result); + $data = $this->generator->multiSet($chartData); + $cache->store($data); return Response::json($data); } @@ -260,33 +280,47 @@ class CategoryController extends Controller */ private function makePeriodChart(CRI $repository, Category $category, Carbon $start, Carbon $end) { - $categoryCollection = new Collection([$category]); - $cache = new CacheProperties; + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($category->id); + $cache->addProperty('chart.category.period-chart'); /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($accounts); - $cache->addProperty($category->id); - $cache->addProperty('specific-period'); - - if ($cache->has()) { return $cache->get(); } - $entries = new Collection; + + // chart data + $chartData = [ + [ + 'label' => strval(trans('firefly.spent')), + 'entries' => [], + 'type' => 'bar', + ], + [ + 'label' => strval(trans('firefly.earned')), + 'entries' => [], + 'type' => 'bar', + ], + ]; + while ($start <= $end) { - $spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $start); - $earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $start); - $date = Navigation::periodShow($start, '1D'); - $entries->push([clone $start, $date, $spent, $earned]); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start); + $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); + $label = Navigation::periodShow($start, '1D'); + + $chartData[0]['entries'][$label] = bcmul($spent, '-1'); + $chartData[1]['entries'][$label] = $earned; + + $start->addDay(); } - $data = $this->generator->period($entries); + $data = $this->generator->multiSet($chartData); $cache->store($data); return $data; diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 0cabf7ad52..003648218c 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; -use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; +use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Generator\Report\Category\MonthReportGenerator; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; @@ -24,8 +24,9 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Log; +use Navigation; use Response; @@ -43,7 +44,7 @@ class CategoryReportController extends Controller private $accountRepository; /** @var CategoryRepositoryInterface */ private $categoryRepository; - /** @var CategoryChartGeneratorInterface */ + /** @var GeneratorInterface */ private $generator; /** @@ -54,7 +55,7 @@ class CategoryReportController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - $this->generator = app(CategoryChartGeneratorInterface::class); + $this->generator = app(GeneratorInterface::class); $this->categoryRepository = app(CategoryRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class); @@ -76,38 +77,45 @@ class CategoryReportController extends Controller { /** @var bool $others */ $others = intval($others) === 1; - $names = []; + $cache = new CacheProperties; + $cache->addProperty('chart.category.report.account-expense'); + $cache->addProperty($accounts); + $cache->addProperty($categories); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } - // collect journals (just like the category report does): - $set = $this->getExpenses($accounts, $categories, $start, $end); - $grouped = $this->groupByOpposingAccount($set); + $names = []; + $set = $this->getExpenses($accounts, $categories, $start, $end); + $grouped = $this->groupByOpposingAccount($set); + $chartData = []; + $total = '0'; - // show the grouped results: - $result = []; - $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); - $result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount]; + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $chartData[$names[$accountId]] = $amount; } // also collect all transactions NOT in these categories. 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'); - Log::debug(sprintf('Sum of others in accountExpense is %f', $sum)); - $sum = bcsub($sum, $total); - $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + $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($result); + $data = $this->generator->pieChart($chartData); + $cache->store($data); return Response::json($data); } @@ -125,36 +133,44 @@ class CategoryReportController extends Controller { /** @var bool $others */ $others = intval($others) === 1; - $names = []; + $cache = new CacheProperties; + $cache->addProperty('chart.category.report.account-income'); + $cache->addProperty($accounts); + $cache->addProperty($categories); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } - // collect journals (just like the category report does): - $set = $this->getIncome($accounts, $categories, $start, $end); - $grouped = $this->groupByOpposingAccount($set); - // loop and show the grouped results: - $result = []; - $total = '0'; + $names = []; + $set = $this->getIncome($accounts, $categories, $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; } - $total = bcadd($total, $amount); - $result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount]; + $total = bcadd($total, $amount); + $chartData[$names[$accountId]] = $amount; } // also collect others? if ($others) { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - Log::debug(sprintf('Sum of others in accountIncome is %f', $sum)); - $sum = bcsub($sum, $total); - $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcsub($sum, $total); + $chartData[strval(trans('firefly.everything_else'))] = $sum; } - $data = $this->generator->pieChart($result); + $data = $this->generator->pieChart($chartData); + $cache->store($data); return Response::json($data); } @@ -172,38 +188,45 @@ class CategoryReportController extends Controller { /** @var bool $others */ $others = intval($others) === 1; - $names = []; + $cache = new CacheProperties; + $cache->addProperty('chart.category.report.category-expense'); + $cache->addProperty($accounts); + $cache->addProperty($categories); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } - // collect journals (just like the category report does): - $set = $this->getExpenses($accounts, $categories, $start, $end); - $grouped = $this->groupByCategory($set); + $names = []; + $set = $this->getExpenses($accounts, $categories, $start, $end); + $grouped = $this->groupByCategory($set); + $total = '0'; + $chartData = []; - // show the grouped results: - $result = []; - $total = '0'; foreach ($grouped as $categoryId => $amount) { if (!isset($names[$categoryId])) { $category = $this->categoryRepository->find(intval($categoryId)); $names[$categoryId] = $category->name; } - $amount = bcmul($amount, '-1'); - $total = bcadd($total, $amount); - $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $chartData[$names[$categoryId]] = $amount; } // also collect all transactions NOT in these categories. 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'); - Log::debug(sprintf('Sum of others in categoryExpense is %f', $sum)); - $sum = bcsub($sum, $total); - $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + $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($result); + $data = $this->generator->pieChart($chartData); + $cache->store($data); return Response::json($data); } @@ -221,36 +244,42 @@ class CategoryReportController extends Controller { /** @var bool $others */ $others = intval($others) === 1; - $names = []; + $cache = new CacheProperties; + $cache->addProperty('chart.category.report.category-income'); + $cache->addProperty($accounts); + $cache->addProperty($categories); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } - // collect journals (just like the category report does): - $set = $this->getIncome($accounts, $categories, $start, $end); - $grouped = $this->groupByCategory($set); + $names = []; + $set = $this->getIncome($accounts, $categories, $start, $end); + $grouped = $this->groupByCategory($set); + $total = '0'; + $chartData = []; - // loop and show the grouped results: - $result = []; - $total = '0'; foreach ($grouped as $categoryId => $amount) { if (!isset($names[$categoryId])) { $category = $this->categoryRepository->find(intval($categoryId)); $names[$categoryId] = $category->name; } - $total = bcadd($total, $amount); - $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; + $total = bcadd($total, $amount); + $chartData[$names[$categoryId]] = $amount; } - // also collect others? if ($others) { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - Log::debug(sprintf('Sum of others in categoryIncome is %f', $sum)); - $sum = bcsub($sum, $total); - $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcsub($sum, $total); + $chartData[strval(trans('firefly.everything_else'))] = $sum; } - $data = $this->generator->pieChart($result); + $data = $this->generator->pieChart($chartData); + $cache->store($data); return Response::json($data); } @@ -265,52 +294,56 @@ class CategoryReportController extends Controller */ public function mainChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) { - // determin optimal period: - $period = '1D'; - $format = 'month_and_day'; - $function = 'endOfDay'; - if ($start->diffInMonths($end) > 1) { - $period = '1M'; - $format = 'month'; - $function = 'endOfMonth'; + $cache = new CacheProperties; + $cache->addProperty('chart.category.report.main'); + $cache->addProperty($accounts); + $cache->addProperty($categories); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); } - if ($start->diffInMonths($end) > 13) { - $period = '1Y'; - $format = 'year'; - $function = 'endOfYear'; - } - Log::debug(sprintf('Period is %s', $period)); - $data = []; + + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); + $function = Navigation::preferredEndOfPeriod($start, $end); + $chartData = []; $currentStart = clone $start; + + // prep chart data: + foreach ($categories as $category) { + $chartData[$category->id . '-in'] = [ + 'label' => $category->name . ' (' . strtolower(strval(trans('firefly.income'))) . ')', + 'type' => 'bar', + 'entries' => [], + ]; + $chartData[$category->id . '-out'] = [ + 'label' => $category->name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')', + 'type' => 'bar', + 'entries' => [], + ]; + } + while ($currentStart < $end) { $currentEnd = clone $currentStart; - Log::debug(sprintf('Function is %s', $function)); $currentEnd = $currentEnd->$function(); $expenses = $this->groupByCategory($this->getExpenses($accounts, $categories, $currentStart, $currentEnd)); $income = $this->groupByCategory($this->getIncome($accounts, $categories, $currentStart, $currentEnd)); - $label = $currentStart->formatLocalized(strval(trans('config.' . $format))); - - Log::debug(sprintf('Now grabbing CMC expenses between %s and %s', $currentStart->format('Y-m-d'), $currentEnd->format('Y-m-d'))); - - $data[$label] = [ - 'in' => [], - 'out' => [], - ]; + $label = $currentStart->formatLocalized($format); /** @var Category $category */ foreach ($categories as $category) { + $labelIn = $category->id . '-in'; + $labelOut = $category->id . '-out'; // get sum, and get label: - $categoryId = $category->id; - $data[$label]['name'][$categoryId] = $category->name; - $data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0'; - $data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0'; + $chartData[$labelIn]['entries'][$label] = $income[$category->id] ?? '0'; + $chartData[$labelOut]['entries'][$label] = $expenses[$category->id] ?? '0'; } - $currentStart = clone $currentEnd; $currentStart->addDay(); } - $data = $this->generator->mainReportChart($data); + $data = $this->generator->multiSet($chartData); + $cache->store($data); return Response::json($data); } diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index 7ed1c1fdfb..bfe3b857d0 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -95,6 +95,7 @@ class CacheProperties */ private function md5() { + $this->md5 = ''; foreach ($this->properties as $property) { if ($property instanceof Collection || $property instanceof EloquentCollection) { @@ -108,6 +109,12 @@ class CacheProperties if (is_object($property)) { $this->md5 .= $property->__toString(); } + if (is_bool($property) && $property === false) { + $this->md5 .= 'false'; + } + if (is_bool($property) && $property === true) { + $this->md5 .= 'true'; + } $this->md5 .= json_encode($property); } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index f36d54e054..6a6e360e78 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -243,6 +243,28 @@ class Navigation throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); } + /** + * If the date difference between start and end is less than a month, method returns "endOfDay". If the difference is less than a year, + * method returns "endOfMonth". If the date difference is larger, method returns "endOfYear". + * + * @param \Carbon\Carbon $start + * @param \Carbon\Carbon $end + * + * @return string + */ + public function preferredEndOfPeriod(Carbon $start, Carbon $end): string + { + $format = 'endOfDay'; + if ($start->diffInMonths($end) > 1) { + $format = 'endOfMonth'; + } + + if ($start->diffInMonths($end) > 12) { + $format = 'endOfYear'; + } + return $format; + } + /** * If the date difference between start and end is less than a month, method returns "Y-m-d". If the difference is less than a year, * method returns "Y-m". If the date difference is larger, method returns "Y". @@ -262,6 +284,28 @@ class Navigation if ($start->diffInMonths($end) > 12) { $format = 'Y'; } + return $format; + } + + /** + * If the date difference between start and end is less than a month, method returns trans(config.month_and_day). If the difference is less than a year, + * method returns "config.month". If the date difference is larger, method returns "config.year". + * + * @param \Carbon\Carbon $start + * @param \Carbon\Carbon $end + * + * @return string + */ + public function preferredCarbonLocalizedFormat(Carbon $start, Carbon $end): string + { + $format = strval(trans('config.month_and_day')); + if ($start->diffInMonths($end) > 1) { + $format = strval(trans('config.month')); + } + + if ($start->diffInMonths($end) > 12) { + $format = strval(trans('config.year')); + } return $format;