Revamped tag report as well.

This commit is contained in:
James Cole
2019-09-03 15:37:59 +02:00
parent 4872d3e4bc
commit 5a8146aa34
22 changed files with 1917 additions and 992 deletions

View File

@@ -296,7 +296,7 @@ class CategoryReportController extends Controller
/**
* @param Collection $accounts
* @param Category $category
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
@@ -307,7 +307,7 @@ class CategoryReportController extends Controller
{
$chartData = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$category]));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category]));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category]));
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
// loop expenses.
@@ -435,6 +435,7 @@ class CategoryReportController extends Controller
/**
* TODO duplicate function
*
* @param Carbon $start
* @param Carbon $end
*

View File

@@ -27,6 +27,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Tag\OperationsRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Http\Controllers\AugumentData;
use FireflyIII\Support\Http\Controllers\TransactionCalculation;
@@ -42,8 +43,12 @@ class TagReportController extends Controller
/** @var GeneratorInterface Chart generation methods. */
protected $generator;
/** @var OperationsRepositoryInterface */
private $opsRepository;
/**
* TagReportController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
@@ -51,6 +56,14 @@ class TagReportController extends Controller
parent::__construct();
// create chart generator:
$this->generator = app(GeneratorInterface::class);
$this->middleware(
function ($request, $next) {
$this->opsRepository = app(OperationsRepositoryInterface::class);
return $next($request);
}
);
}
@@ -111,12 +124,7 @@ class TagReportController extends Controller
return response()->json($data);
}
/**
* Generate expense for tag grouped on budget.
*
* TODO this chart is not multi-currency aware.
*
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
@@ -126,25 +134,32 @@ class TagReportController extends Controller
*/
public function budgetExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setTags($tags);
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(false);
$chartData = $helper->generate('expense', 'budget');
$data = $this->generator->pieChart($chartData);
$result = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['budget_name'] ?? trans('firefly.no_budget');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* Generate expense for tag grouped on category.
*
* TODO this chart is not multi-currency aware.
*
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
@@ -154,188 +169,371 @@ class TagReportController extends Controller
*/
public function categoryExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setTags($tags);
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(false);
$chartData = $helper->generate('expense', 'category');
$data = $this->generator->pieChart($chartData);
$result = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['category_name'] ?? trans('firefly.no_category');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function categoryIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$spent = $this->opsRepository->listIncome($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['category_name'] ?? trans('firefly.no_category');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function destinationExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function destinationIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$spent = $this->opsRepository->listIncome($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* Generate main tag overview chart.
*
* TODO this chart is not multi-currency aware.
*
* @param Collection $accounts
* @param Collection $tags
* @param Tag $tag
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*
*/
public function mainChart(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
public function mainChart(Collection $accounts, Tag $tag, Carbon $start, Carbon $end): JsonResponse
{
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.main');
$cache->addProperty($accounts);
$cache->addProperty($tags);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$chartData = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$tag]));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$tag]));
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$function = app('navigation')->preferredEndOfPeriod($start, $end);
$chartData = [];
$currentStart = clone $start;
// loop expenses.
foreach ($spent as $currency) {
// add things to chart Data for each currency:
$spentKey = sprintf('%d-spent', $currency['currency_id']);
$chartData[$spentKey] = $chartData[$spentKey] ?? [
'label' => sprintf(
'%s (%s)', (string)trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']
),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_id' => $currency['currency_id'],
'entries' => $this->makeEntries($start, $end),
];
// prep chart data:
foreach ($tags as $tag) {
$chartData[$tag->id . '-in'] = [
'label' => $tag->tag . ' (' . strtolower((string)trans('firefly.income')) . ')',
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
$chartData[$tag->id . '-out'] = [
'label' => $tag->tag . ' (' . strtolower((string)trans('firefly.expenses')) . ')',
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
// total in, total out:
$chartData[$tag->id . '-total-in'] = [
'label' => $tag->tag . ' (' . strtolower((string)trans('firefly.sum_of_income')) . ')',
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
$chartData[$tag->id . '-total-out'] = [
'label' => $tag->tag . ' (' . strtolower((string)trans('firefly.sum_of_expenses')) . ')',
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
}
$sumOfIncome = [];
$sumOfExpense = [];
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
$currentEnd = $currentEnd->$function();
$expenses = $this->groupByTag($this->getExpensesForTags($accounts, $tags, $currentStart, $currentEnd));
$income = $this->groupByTag($this->getIncomeForTags($accounts, $tags, $currentStart, $currentEnd));
$label = $currentStart->formatLocalized($format);
/** @var Tag $tag */
foreach ($tags as $tag) {
$labelIn = $tag->id . '-in';
$labelOut = $tag->id . '-out';
$labelSumIn = $tag->id . '-total-in';
$labelSumOut = $tag->id . '-total-out';
$currentIncome = bcmul($income[$tag->id] ?? '0','-1');
$currentExpense = $expenses[$tag->id] ?? '0';
// add to sum:
$sumOfIncome[$tag->id] = $sumOfIncome[$tag->id] ?? '0';
$sumOfExpense[$tag->id] = $sumOfExpense[$tag->id] ?? '0';
$sumOfIncome[$tag->id] = bcadd($sumOfIncome[$tag->id], $currentIncome);
$sumOfExpense[$tag->id] = bcadd($sumOfExpense[$tag->id], $currentExpense);
// add to chart:
$chartData[$labelIn]['entries'][$label] = $currentIncome;
$chartData[$labelOut]['entries'][$label] = $currentExpense;
$chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$tag->id];
$chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$tag->id];
foreach ($currency['tags'] as $currentTag) {
foreach ($currentTag['transaction_journals'] as $journal) {
$key = $journal['date']->formatLocalized($format);
$amount = app('steam')->positive($journal['amount']);
$chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0';
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
}
}
/** @var Carbon $currentStart */
}
// loop income.
foreach ($earned as $currency) {
// add things to chart Data for each currency:
$spentKey = sprintf('%d-earned', $currency['currency_id']);
$chartData[$spentKey] = $chartData[$spentKey] ?? [
'label' => sprintf(
'%s (%s)', (string)trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']
),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_id' => $currency['currency_id'],
'entries' => $this->makeEntries($start, $end),
];
foreach ($currency['tags'] as $currentTag) {
foreach ($currentTag['transaction_journals'] as $journal) {
$key = $journal['date']->formatLocalized($format);
$amount = app('steam')->positive($journal['amount']);
$chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0';
$chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
}
}
}
$data = $this->generator->multiSet($chartData);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function sourceExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['source_account_name'] ?? trans('firefly.empty');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function sourceIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags);
// loop expenses.
foreach ($earned as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
foreach ($tag['transaction_journals'] as $journal) {
$objectName = $journal['source_account_name'] ?? trans('firefly.empty');
$title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function tagExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, $tags);
// loop expenses.
foreach ($spent as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
$title = sprintf('%s (%s)', $tag['name'], $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
foreach ($tag['transaction_journals'] as $journal) {
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
}
/**
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
*/
public function tagIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
{
$result = [];
$earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags);
// loop expenses.
foreach ($earned as $currency) {
/** @var array $tag */
foreach ($currency['tags'] as $tag) {
$title = sprintf('%s (%s)', $tag['name'], $currency['currency_name']);
$result[$title] = $result[$title] ?? [
'amount' => '0',
'currency_symbol' => $currency['currency_symbol'],
];
foreach ($tag['transaction_journals'] as $journal) {
$amount = app('steam')->positive($journal['amount']);
$result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
}
}
}
$data = $this->generator->multiCurrencyPieChart($result);
return response()->json($data);
return response()->json($data);
}
/**
* TODO duplicate function
*
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function makeEntries(Carbon $start, Carbon $end): array
{
$return = [];
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$preferredRange = app('navigation')->preferredRangeFormat($start, $end);
$currentStart = clone $start;
while ($currentStart <= $end) {
$currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
$key = $currentStart->formatLocalized($format);
$return[$key] = '0';
$currentStart = clone $currentEnd;
$currentStart->addDay();
$currentStart->startOfDay();
$currentStart->addDay()->startOfDay();
}
}
// remove all empty entries to prevent cluttering:
$newSet = [];
foreach ($chartData as $key => $entry) {
if (0 === !array_sum($entry['entries'])) {
$newSet[$key] = $chartData[$key]; // @codeCoverageIgnore
}
}
if (0 === count($newSet)) {
$newSet = $chartData; // @codeCoverageIgnore
}
$data = $this->generator->multiSet($newSet);
$cache->store($data);
return response()->json($data);
return $return;
}
/**
* Show expense grouped by expense account.
*
* TODO this chart is not multi-currency aware.
*
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return JsonResponse
*/
public function tagExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others): JsonResponse
{
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setTags($tags);
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(1 === (int)$others);
$chartData = $helper->generate('expense', 'tag');
$data = $this->generator->pieChart($chartData);
return response()->json($data);
}
/**
* Show income grouped by tag.
*
* TODO this chart is not multi-currency aware.
*
* @param Collection $accounts
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return JsonResponse
*/
public function tagIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others): JsonResponse
{
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setTags($tags);
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(1 === (int)$others);
$chartData = $helper->generate('income', 'tag');
$data = $this->generator->pieChart($chartData);
return response()->json($data);
}
}