Various report updates and code cleanup.

This commit is contained in:
James Cole 2016-12-04 18:02:19 +01:00
parent 905a2432c6
commit f4b9b7ae84
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
32 changed files with 592 additions and 450 deletions

View File

@ -44,4 +44,11 @@ interface BudgetChartGeneratorInterface
* @return array * @return array
*/ */
public function period(array $entries): array; public function period(array $entries): array;
/**
* @param array $entries
*
* @return array
*/
public function periodNoBudget(array $entries): array;
} }

View File

@ -104,7 +104,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function period(array $entries) : array public function period(array $entries): array
{ {
$data = [ $data = [
@ -133,4 +133,32 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
return $data; return $data;
} }
/**
* @param array $entries
*
* @return array
*/
public function periodNoBudget(array $entries): array
{
$data = [
'labels' => array_keys($entries),
'datasets' => [
0 => [
'label' => trans('firefly.spent'),
'data' => [],
],
],
'count' => 1,
];
foreach ($entries as $label => $entry) {
// data set 0 is budgeted
// data set 1 is spent:
$data['datasets'][0]['data'][] = round(($entry['spent'] * -1), 2);
}
return $data;
}
} }

View File

@ -78,6 +78,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance; $auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
} }
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
$reportType = 'audit'; $reportType = 'audit';
$accountIds = join(',', $this->accounts->pluck('id')->toArray()); $accountIds = join(',', $this->accounts->pluck('id')->toArray());
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', $hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
@ -87,10 +88,9 @@ class MonthReportGenerator implements ReportGeneratorInterface
'from', 'to', 'budget', 'category', 'bill', 'from', 'to', 'budget', 'category', 'bill',
// more new optional fields // more new optional fields
'internal_reference', 'notes', 'internal_reference', 'notes',
'create_date', 'update_date', 'create_date', 'update_date',
]; ];
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow')) return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow'))
->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts) ->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts)

View File

@ -42,22 +42,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface
$this->repository = $repository; $this->repository = $repository;
} }
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array
{
$budgets = $this->repository->getBudgets();
$report = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end, true);
$data = $this->filterBudgetPeriodReport($report);
return $data;
}
/** /**
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@ -162,31 +146,4 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $array; return $array;
} }
/**
* Filters empty results from getBudgetPeriodReport
*
* @param array $data
*
* @return array
*/
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
* @var array $set
*/
foreach ($data as $budgetId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$budgetId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$budgetId]);
}
}
return $data;
}
} }

View File

@ -26,15 +26,6 @@ use Illuminate\Support\Collection;
interface BudgetReportHelperInterface interface BudgetReportHelperInterface
{ {
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array;
/** /**
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end

View File

@ -252,6 +252,48 @@ class BudgetController extends Controller
return Response::json($data); return Response::json($data);
} }
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function periodNoBudget(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty('no-budget');
$cache->addProperty('period');
if ($cache->has()) {
return Response::json($cache->get());
}
// the expenses:
$periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end);
// join them:
$result = [];
foreach (array_keys($periods) as $period) {
$nice = $periods[$period];
$result[$nice] = [
'spent' => isset($entries['entries'][$period]) ? $entries['entries'][$period] : '0',
];
}
$data = $this->generator->periodNoBudget($result);
$cache->store($data);
return Response::json($data);
}
/** /**
* @param Collection $repetitions * @param Collection $repetitions
* @param Budget $budget * @param Budget $budget

View File

@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface; use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
@ -31,14 +32,13 @@ class BudgetController extends Controller
/** /**
* *
* @param BudgetReportHelperInterface $helper * @param Carbon $start
* @param Carbon $start * @param Carbon $end
* @param Carbon $end * @param Collection $accounts
* @param Collection $accounts
* *
* @return string * @return string
*/ */
public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) public function budgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts)
{ {
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($start); $cache->addProperty($start);
@ -46,12 +46,19 @@ class BudgetController extends Controller
$cache->addProperty('budget-period-report'); $cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); return $cache->get();
} }
$periods = Navigation::listOfPeriods($start, $end); // generate budget report right here.
$budgets = $helper->getBudgetPeriodReport($start, $end, $accounts); /** @var BudgetRepositoryInterface $repository */
$result = view('reports.partials.budget-period', compact('budgets', 'periods'))->render(); $repository = app(BudgetRepositoryInterface::class);
$budgets = $repository->getBudgets();
$data = $repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data[0] = $repository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget"
$report = $this->filterBudgetPeriodReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.budget-period', compact('report', 'periods'))->render();
$cache->store($result); $cache->store($result);
return $result; return $result;
@ -86,4 +93,31 @@ class BudgetController extends Controller
return $result; return $result;
} }
/**
* Filters empty results from getBudgetPeriodReport
*
* @param array $data
*
* @return array
*/
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
* @var array $set
*/
foreach ($data as $budgetId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$budgetId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$budgetId]);
}
}
return $data;
}
} }

View File

@ -31,40 +31,6 @@ use Navigation;
*/ */
class CategoryController extends Controller class CategoryController extends Controller
{ {
/**
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function categoryPeriodReport(Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$report = $repository->getCategoryPeriodReport($categories, $accounts, $start, $end, true);
$report = $this->filterCategoryPeriodReport($report);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('categories', 'periods', 'report'))->render();
$cache->store($result);
return $result;
}
/** /**
* @param ReportHelperInterface $helper * @param ReportHelperInterface $helper
* @param Carbon $start * @param Carbon $start
@ -93,6 +59,73 @@ class CategoryController extends Controller
return $result; return $result;
} }
/**
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function expenseReport(Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-expenses-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$data = $repository->periodExpenses($categories, $accounts, $start, $end);
$data[0] = $repository->periodExpensesNoCategory($accounts, $start, $end);
$report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/**
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function incomeReport(Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$data = $repository->periodIncome($categories, $accounts, $start, $end);
$data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end);
$report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/** /**
* Filters empty results from category period report * Filters empty results from category period report
* *
@ -100,22 +133,16 @@ class CategoryController extends Controller
* *
* @return array * @return array
*/ */
private function filterCategoryPeriodReport(array $data): array private function filterReport(array $data): array
{ {
/** foreach ($data as $categoryId => $set) {
* @var string $type $sum = '0';
* @var array $report foreach ($set['entries'] as $amount) {
*/ $sum = bcadd($amount, $sum);
foreach ($data as $type => $report) { }
foreach ($report as $categoryId => $set) { $data[$categoryId]['sum'] = $sum;
$sum = '0'; if (bccomp('0', $sum) === 0) {
foreach ($set['entries'] as $amount) { unset($data[$categoryId]);
$sum = bcadd($amount, $sum);
}
$data[$type][$categoryId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$type][$categoryId]);
}
} }
} }
@ -123,25 +150,5 @@ class CategoryController extends Controller
return $data; return $data;
} }
/**
* @param int $categoryId
* @param Collection $categories
*
* @return string
*/
private function getCategoryName(int $categoryId, Collection $categories): string
{
$first = $categories->filter(
function (Category $category) use ($categoryId) {
return $categoryId === $category->id;
}
);
if (!is_null($first->first())) {
return $first->first()->name;
}
return '(unknown)';
}
} }

View File

@ -368,10 +368,10 @@ class AccountRepository implements AccountRepositoryInterface
protected function openingBalanceTransaction(Account $account): TransactionJournal protected function openingBalanceTransaction(Account $account): TransactionJournal
{ {
$journal = TransactionJournal::sortCorrectly() $journal = TransactionJournal::sortCorrectly()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id) ->where('transactions.account_id', $account->id)
->transactionTypes([TransactionType::OPENING_BALANCE]) ->transactionTypes([TransactionType::OPENING_BALANCE])
->first(['transaction_journals.*']); ->first(['transaction_journals.*']);
if (is_null($journal)) { if (is_null($journal)) {
Log::debug('Could not find a opening balance journal, return empty one.'); Log::debug('Could not find a opening balance journal, return empty one.');

View File

@ -250,19 +250,19 @@ class AccountTasker implements AccountTaskerInterface
$selection = $incoming ? '>' : '<'; $selection = $incoming ? '>' : '<';
$query = Transaction::distinct() $query = Transaction::distinct()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin( ->leftJoin(
'transactions as other_side', function (JoinClause $join) use ($joinModifier) { 'transactions as other_side', function (JoinClause $join) use ($joinModifier) {
$join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0); $join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0);
} }
) )
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_journals.user_id', $this->user->id) ->where('transaction_journals.user_id', $this->user->id)
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->whereIn('transactions.account_id', $accounts['accounts']) ->whereIn('transactions.account_id', $accounts['accounts'])
->where('transactions.amount', $selection, 0); ->where('transactions.amount', $selection, 0);
if (count($accounts['exclude']) > 0) { if (count($accounts['exclude']) > 0) {
$query->whereNotIn('other_side.account_id', $accounts['exclude']); $query->whereNotIn('other_side.account_id', $accounts['exclude']);
} }
@ -302,25 +302,27 @@ class AccountTasker implements AccountTaskerInterface
$joinModifier = $incoming ? '<' : '>'; $joinModifier = $incoming ? '<' : '>';
$selection = $incoming ? '>' : '<'; $selection = $incoming ? '>' : '<';
$query = Transaction::distinct() $query = Transaction::distinct()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id') ->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->leftJoin( ->leftJoin(
'transactions as other_side', function (JoinClause $join) use ($joinModifier) { 'transactions as other_side', function (JoinClause $join) use ($joinModifier) {
$join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0); $join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where(
} 'other_side.amount', $joinModifier, 0
) );
->leftJoin('accounts as other_account', 'other_account.id', '=', 'other_side.account_id') }
->where('transaction_types.type', '!=', TransactionType::OPENING_BALANCE) )
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->leftJoin('accounts as other_account', 'other_account.id', '=', 'other_side.account_id')
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_types.type', '!=', TransactionType::OPENING_BALANCE)
->where('transaction_journals.user_id', $this->user->id) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->whereNull('transactions.deleted_at') ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->whereNull('other_side.deleted_at') ->where('transaction_journals.user_id', $this->user->id)
->whereNull('transaction_journals.deleted_at') ->whereNull('transactions.deleted_at')
->whereIn('transactions.account_id', $accounts['accounts']) ->whereNull('other_side.deleted_at')
->where('other_side.amount', '=', DB::raw('transactions.amount * -1')) ->whereNull('transaction_journals.deleted_at')
->where('transactions.amount', $selection, 0) ->whereIn('transactions.account_id', $accounts['accounts'])
->orderBy('transactions.amount'); ->where('other_side.amount', '=', DB::raw('transactions.amount * -1'))
->where('transactions.amount', $selection, 0)
->orderBy('transactions.amount');
if (count($accounts['exclude']) > 0) { if (count($accounts['exclude']) > 0) {
$query->whereNotIn('other_side.account_id', $accounts['exclude']); $query->whereNotIn('other_side.account_id', $accounts['exclude']);

View File

@ -66,7 +66,7 @@ class BillRepository implements BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function find(int $billId) : Bill public function find(int $billId): Bill
{ {
$bill = $this->user->bills()->find($billId); $bill = $this->user->bills()->find($billId);
if (is_null($bill)) { if (is_null($bill)) {
@ -83,7 +83,7 @@ class BillRepository implements BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function findByName(string $name) : Bill public function findByName(string $name): Bill
{ {
$bills = $this->user->bills()->get(['bills.*']); $bills = $this->user->bills()->get(['bills.*']);

View File

@ -40,7 +40,7 @@ interface BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function find(int $billId) : Bill; public function find(int $billId): Bill;
/** /**
* Find a bill by name. * Find a bill by name.
@ -49,7 +49,7 @@ interface BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function findByName(string $name) : Bill; public function findByName(string $name): Bill;
/** /**
* @return Collection * @return Collection

View File

@ -219,11 +219,10 @@ class BudgetRepository implements BudgetRepositoryInterface
* @param Collection $accounts * @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $noBudget
* *
* @return array * @return array
*/ */
public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end, bool $noBudget): array public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array
{ {
$carbonFormat = Navigation::preferredCarbonFormat($start, $end); $carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = []; $data = [];
@ -252,11 +251,6 @@ class BudgetRepository implements BudgetRepositoryInterface
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $transaction->transaction_amount); $data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $transaction->transaction_amount);
} }
if ($noBudget) {
// and now the same for stuff without a budget:
$data[0] = $this->getNoBudgetPeriodReport($start, $end);
}
return $data; return $data;
} }
@ -295,6 +289,40 @@ class BudgetRepository implements BudgetRepositoryInterface
return $set; return $set;
} }
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL]);
$collector->withoutBudget();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_budget')),
'sum' => '0',
];
foreach ($transactions as $transaction) {
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
return $result;
}
/** /**
* @param Collection $budgets * @param Collection $budgets
* @param Collection $accounts * @param Collection $accounts
@ -533,58 +561,4 @@ class BudgetRepository implements BudgetRepositoryInterface
return $limit; return $limit;
} }
/**
* @param int $budgetId
* @param Collection $budgets
*
* @return string
*/
private function getBudgetName(int $budgetId, Collection $budgets): string
{
$first = $budgets->filter(
function (Budget $budget) use ($budgetId) {
return $budgetId === $budget->id;
}
);
if (!is_null($first->first())) {
return $first->first()->name;
}
return '(unknown)';
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getNoBudgetPeriodReport(Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL]);
$collector->withoutBudget();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_budget')),
'sum' => '0',
];
foreach ($transactions as $transaction) {
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
return $result;
}
} }

View File

@ -96,11 +96,10 @@ interface BudgetRepositoryInterface
* @param Collection $accounts * @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $noBudget
* *
* @return array * @return array
*/ */
public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end, bool $noBudget): array; public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
* @return Collection * @return Collection
@ -112,6 +111,14 @@ interface BudgetRepositoryInterface
*/ */
public function getInactiveBudgets(): Collection; public function getInactiveBudgets(): Collection;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
* @param Collection $budgets * @param Collection $budgets
* @param Collection $accounts * @param Collection $accounts

View File

@ -175,32 +175,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $set; return $set;
} }
/**
* This method is being used to generate the category overview in the year/multi-year report. Its used
* in both the year/multi-year budget overview AND in the accompanying chart.
*
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param @bool $noCategory
*
* @return array
*/
public function getCategoryPeriodReport(Collection $categories, Collection $accounts, Carbon $start, Carbon $end, bool $noCategory): array
{
$data = [
'income' => $this->getCategoryReportData($categories, $accounts, $start, $end, $noCategory, [TransactionType::DEPOSIT, TransactionType::TRANSFER]),
'expense' => $this->getCategoryReportData(
$categories, $accounts, $start, $end, $noCategory, [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]
),
];
return $data;
}
/** /**
* @param Category $category * @param Category $category
* @param Collection $accounts * @param Collection $accounts
@ -250,6 +224,158 @@ class CategoryRepository implements CategoryRepositoryInterface
return $last; return $last;
} }
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Category $category */
foreach ($categories as $category) {
$data[$category->id] = [
'name' => $category->name,
'sum' => '0',
'entries' => [],
];
}
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setCategories($categories)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
// loop transactions:
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$categoryId = max(intval($transaction->transaction_journal_category_id), intval($transaction->transaction_category_id));
$date = $transaction->date->format($carbonFormat);
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])->enableInternalFilter();
$collector->withoutCategory();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_category')),
'sum' => '0',
];
foreach ($transactions as $transaction) {
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
return $result;
}
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Category $category */
foreach ($categories as $category) {
$data[$category->id] = [
'name' => $category->name,
'sum' => '0',
'entries' => [],
];
}
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setCategories($categories)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
// loop transactions:
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$categoryId = max(intval($transaction->transaction_journal_category_id), intval($transaction->transaction_category_id));
$date = $transaction->date->format($carbonFormat);
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->enableInternalFilter();
$collector->withoutCategory();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_category')),
'sum' => '0',
];
foreach ($transactions as $transaction) {
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
return $result;
}
/** /**
* @param Collection $categories * @param Collection $categories
* @param Collection $accounts * @param Collection $accounts
@ -314,55 +440,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $category; return $category;
} }
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param bool $noCategory
* @param array $types
*
* @return array
*/
private function getCategoryReportData(Collection $categories, Collection $accounts, Carbon $start, Carbon $end, bool $noCategory, array $types): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Category $category */
foreach ($categories as $category) {
$data[$category->id] = [
'name' => $category->name,
'sum' => '0',
'entries' => [],
];
}
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setCategories($categories)->setTypes($types)
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
// loop transactions:
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$categoryId = max(intval($transaction->transaction_journal_category_id), intval($transaction->transaction_category_id));
$date = $transaction->date->format($carbonFormat);
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
}
if ($noCategory) {
// and now the same for stuff without a budget:
//$data[0] = $this->getNoBudgetPeriodReport($start, $end);
}
return $data;
}
/** /**
* @param Collection $categories * @param Collection $categories
* @param Collection $accounts * @param Collection $accounts

View File

@ -24,7 +24,6 @@ use Illuminate\Support\Collection;
*/ */
interface CategoryRepositoryInterface interface CategoryRepositoryInterface
{ {
/** /**
* @param Category $category * @param Category $category
* *
@ -83,20 +82,6 @@ interface CategoryRepositoryInterface
*/ */
public function getCategories(): Collection; public function getCategories(): Collection;
/**
* This method is being used to generate the category overview in the year/multi-year report. Its used
* in both the year/multi-year budget overview AND in the accompanying chart.
*
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param bool $noCategory
*
* @return array
*/
public function getCategoryPeriodReport(Collection $categories, Collection $accounts, Carbon $start, Carbon $end, bool $noCategory): array;
/** /**
* Return most recent transaction(journal) date. * Return most recent transaction(journal) date.
* *
@ -107,6 +92,44 @@ interface CategoryRepositoryInterface
*/ */
public function lastUseDate(Category $category, Collection $accounts): Carbon; public function lastUseDate(Category $category, Collection $accounts): Carbon;
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
* @param Collection $categories * @param Collection $categories
* @param Collection $accounts * @param Collection $accounts

View File

@ -43,7 +43,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function find(int $currencyId) : TransactionCurrency public function find(int $currencyId): TransactionCurrency
{ {
$currency = TransactionCurrency::find($currencyId); $currency = TransactionCurrency::find($currencyId);
if (is_null($currency)) { if (is_null($currency)) {
@ -61,7 +61,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByCode(string $currencyCode) : TransactionCurrency public function findByCode(string $currencyCode): TransactionCurrency
{ {
$currency = TransactionCurrency::whereCode($currencyCode)->first(); $currency = TransactionCurrency::whereCode($currencyCode)->first();
if (is_null($currency)) { if (is_null($currency)) {
@ -78,7 +78,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByName(string $currencyName) : TransactionCurrency public function findByName(string $currencyName): TransactionCurrency
{ {
$preferred = TransactionCurrency::whereName($currencyName)->first(); $preferred = TransactionCurrency::whereName($currencyName)->first();
if (is_null($preferred)) { if (is_null($preferred)) {
@ -95,7 +95,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findBySymbol(string $currencySymbol) : TransactionCurrency public function findBySymbol(string $currencySymbol): TransactionCurrency
{ {
$currency = TransactionCurrency::whereSymbol($currencySymbol)->first(); $currency = TransactionCurrency::whereSymbol($currencySymbol)->first();
if (is_null($currency)) { if (is_null($currency)) {

View File

@ -39,7 +39,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function find(int $currencyId) : TransactionCurrency; public function find(int $currencyId): TransactionCurrency;
/** /**
* Find by currency code * Find by currency code
@ -48,7 +48,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByCode(string $currencyCode) : TransactionCurrency; public function findByCode(string $currencyCode): TransactionCurrency;
/** /**
* Find by currency name * Find by currency name
@ -57,7 +57,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByName(string $currencyName) : TransactionCurrency; public function findByName(string $currencyName): TransactionCurrency;
/** /**
* Find by currency symbol * Find by currency symbol
@ -66,7 +66,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findBySymbol(string $currencySymbol) : TransactionCurrency; public function findBySymbol(string $currencySymbol): TransactionCurrency;
/** /**
* @return Collection * @return Collection

View File

@ -105,7 +105,7 @@ class JournalRepository implements JournalRepositoryInterface
* *
* @return TransactionJournal * @return TransactionJournal
*/ */
public function find(int $journalId) : TransactionJournal public function find(int $journalId): TransactionJournal
{ {
$journal = $this->user->transactionJournals()->where('id', $journalId)->first(); $journal = $this->user->transactionJournals()->where('id', $journalId)->first();
if (is_null($journal)) { if (is_null($journal)) {

View File

@ -52,7 +52,7 @@ interface JournalRepositoryInterface
* *
* @return TransactionJournal * @return TransactionJournal
*/ */
public function find(int $journalId) : TransactionJournal; public function find(int $journalId): TransactionJournal;
/** /**
* Get users very first transaction journal * Get users very first transaction journal

View File

@ -172,36 +172,36 @@ class JournalTasker implements JournalTaskerInterface
// go! // go!
$sum = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') $sum = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('account_id', $transaction->account_id) ->where('account_id', $transaction->account_id)
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->where('transactions.id', '!=', $transactionId) ->where('transactions.id', '!=', $transactionId)
->where( ->where(
function (Builder $q1) use ($date, $order, $journalId, $identifier) { function (Builder $q1) use ($date, $order, $journalId, $identifier) {
$q1->where('transaction_journals.date', '<', $date); // date $q1->where('transaction_journals.date', '<', $date); // date
$q1->orWhere( $q1->orWhere(
function (Builder $q2) use ($date, $order) { // function 1 function (Builder $q2) use ($date, $order) { // function 1
$q2->where('transaction_journals.date', $date); $q2->where('transaction_journals.date', $date);
$q2->where('transaction_journals.order', '>', $order); $q2->where('transaction_journals.order', '>', $order);
} }
); );
$q1->orWhere( $q1->orWhere(
function (Builder $q3) use ($date, $order, $journalId) { // function 2 function (Builder $q3) use ($date, $order, $journalId) { // function 2
$q3->where('transaction_journals.date', $date); $q3->where('transaction_journals.date', $date);
$q3->where('transaction_journals.order', $order); $q3->where('transaction_journals.order', $order);
$q3->where('transaction_journals.id', '<', $journalId); $q3->where('transaction_journals.id', '<', $journalId);
} }
); );
$q1->orWhere( $q1->orWhere(
function (Builder $q4) use ($date, $order, $journalId, $identifier) { // function 3 function (Builder $q4) use ($date, $order, $journalId, $identifier) { // function 3
$q4->where('transaction_journals.date', $date); $q4->where('transaction_journals.date', $date);
$q4->where('transaction_journals.order', $order); $q4->where('transaction_journals.order', $order);
$q4->where('transaction_journals.id', $journalId); $q4->where('transaction_journals.id', $journalId);
$q4->where('transactions.identifier', '>', $identifier); $q4->where('transactions.identifier', '>', $identifier);
} }
); );
} }
)->sum('transactions.amount'); )->sum('transactions.amount');
return strval($sum); return strval($sum);
} }

View File

@ -117,7 +117,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
* *
* @return Collection * @return Collection
*/ */
public function getPiggyBanksWithAmount() : Collection public function getPiggyBanksWithAmount(): Collection
{ {
$set = $this->getPiggyBanks(); $set = $this->getPiggyBanks();
foreach ($set as $piggy) { foreach ($set as $piggy) {

View File

@ -58,7 +58,7 @@ interface PiggyBankRepositoryInterface
* *
* @return Collection * @return Collection
*/ */
public function getEvents(PiggyBank $piggyBank) : Collection; public function getEvents(PiggyBank $piggyBank): Collection;
/** /**
* Highest order of all piggy banks. * Highest order of all piggy banks.
@ -72,14 +72,14 @@ interface PiggyBankRepositoryInterface
* *
* @return Collection * @return Collection
*/ */
public function getPiggyBanks() : Collection; public function getPiggyBanks(): Collection;
/** /**
* Also add amount in name. * Also add amount in name.
* *
* @return Collection * @return Collection
*/ */
public function getPiggyBanksWithAmount() : Collection; public function getPiggyBanksWithAmount(): Collection;
/** /**
* Set all piggy banks to order 0. * Set all piggy banks to order 0.

View File

@ -93,7 +93,7 @@ class TagRepository implements TagRepositoryInterface
* *
* @return Tag * @return Tag
*/ */
public function find(int $tagId) : Tag public function find(int $tagId): Tag
{ {
$tag = $this->user->tags()->find($tagId); $tag = $this->user->tags()->find($tagId);
if (is_null($tag)) { if (is_null($tag)) {
@ -108,7 +108,7 @@ class TagRepository implements TagRepositoryInterface
* *
* @return Tag * @return Tag
*/ */
public function findByTag(string $tag) : Tag public function findByTag(string $tag): Tag
{ {
$tags = $this->user->tags()->get(); $tags = $this->user->tags()->get();
/** @var Tag $tag */ /** @var Tag $tag */

View File

@ -49,14 +49,14 @@ interface TagRepositoryInterface
* *
* @return Tag * @return Tag
*/ */
public function find(int $tagId) : Tag; public function find(int $tagId): Tag;
/** /**
* @param string $tag * @param string $tag
* *
* @return Tag * @return Tag
*/ */
public function findByTag(string $tag) : Tag; public function findByTag(string $tag): Tag;
/** /**
* This method returns all the user's tags. * This method returns all the user's tags.

View File

@ -114,10 +114,10 @@ class UserRepository implements UserRepositoryInterface
$return['categories'] = $user->categories()->count(); $return['categories'] = $user->categories()->count();
$return['budgets'] = $user->budgets()->count(); $return['budgets'] = $user->budgets()->count();
$return['budgets_with_limits'] = BudgetLimit::distinct() $return['budgets_with_limits'] = BudgetLimit::distinct()
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('amount', '>', 0) ->where('amount', '>', 0)
->whereNull('budgets.deleted_at') ->whereNull('budgets.deleted_at')
->where('budgets.user_id', $user->id)->get(['budget_limits.budget_id'])->count(); ->where('budgets.user_id', $user->id)->get(['budget_limits.budget_id'])->count();
$return['export_jobs'] = $user->exportJobs()->count(); $return['export_jobs'] = $user->exportJobs()->count();
$return['export_jobs_success'] = $user->exportJobs()->where('status', 'export_downloaded')->count(); $return['export_jobs_success'] = $user->exportJobs()->where('status', 'export_downloaded')->count();
$return['import_jobs'] = $user->exportJobs()->count(); $return['import_jobs'] = $user->exportJobs()->count();

View File

@ -5,7 +5,9 @@ $(function () {
drawChart(); drawChart();
loadAjaxPartial('budgetPeriodReport', budgetPeriodReportUri); loadAjaxPartial('budgetPeriodReport', budgetPeriodReportUri);
loadAjaxPartial('categoryPeriodReport', categoryPeriodReportUri);
loadAjaxPartial('categoryExpense', categoryExpenseUri);
loadAjaxPartial('categoryIncome', categoryIncomeUri);
}); });
function drawChart() { function drawChart() {

View File

@ -85,4 +85,5 @@ return [
'file' => 'The :attribute must be a file.', 'file' => 'The :attribute must be a file.',
'in_array' => 'The :attribute field does not exist in :other.', 'in_array' => 'The :attribute field does not exist in :other.',
'present' => 'The :attribute field must be present.', 'present' => 'The :attribute field must be present.',
'amount_zero' => 'The total amount cannot be zero',
]; ];

View File

@ -113,14 +113,27 @@
</div> </div>
</div> </div>
{# same thing but for categories #} {# same thing but for categories (expenses) #}
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'categories'|_ }}</h3> <h3 class="box-title">{{ 'categories'|_ }} ({{ 'expenses'|_ }})</h3>
</div> </div>
<div class="box-body no-padding table-responsive loading" id="categoryPeriodReport"> <div class="box-body no-padding table-responsive loading" id="categoryExpense">
</div>
</div>
</div>
</div>
{# same thing but for categories (income) #}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'categories'|_ }} ({{ 'income'|_ }})</h3>
</div>
<div class="box-body no-padding table-responsive loading" id="categoryIncome">
</div> </div>
</div> </div>
</div> </div>
@ -164,7 +177,9 @@
var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}';
var budgetPeriodReportUri = '{{ route('reports.data.budgetPeriodReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var budgetPeriodReportUri = '{{ route('reports.data.budgetPeriodReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}';
var categoryPeriodReportUri = '{{ route('reports.data.categoryPeriodReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var categoryExpenseUri = '{{ route('reports.data.categoryExpense', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}';
var categoryIncomeUri = '{{ route('reports.data.categoryIncome', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}';
</script> </script>
<script type="text/javascript" src="js/ff/reports/default/all.js"></script> <script type="text/javascript" src="js/ff/reports/default/all.js"></script>

View File

@ -9,7 +9,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for id, info in budgets %} {% for id, info in report %}
<tr> <tr>
<td data-value="{{ info.name }}"> <td data-value="{{ info.name }}">
<a title="{{ info.name }}" href="#" data-budget="{{ id }}" class="budget-chart-activate">{{ info.name }}</a> <a title="{{ info.name }}" href="#" data-budget="{{ id }}" class="budget-chart-activate">{{ info.name }}</a>

View File

@ -1,75 +1,44 @@
<table class="table table-hover sortable table-condensed"> <table class="table table-hover sortable table-condensed">
<thead> <thead>
<tr> <tr>
<th rowspan="2" data-defaultsort="disabled">{{ 'category'|_ }}</th> <th data-defaultsort="disabled">{{ 'category'|_ }}</th>
{% for period in periods %} {% for period in periods %}
<th colspan="2" data-defaultsort="disabled">{{ period }}</th> <th data-defaultsort="disabled">{{ period }}</th>
{% endfor %} {% endfor %}
<th colspan="2" data-defaultsort="disabled">{{ 'sum'|_ }}</th> <th data-defaultsort="disabled">{{ 'sum'|_ }}</th>
</tr>
<tr>
{% for period in periods %}
<th data-defaultsign="_19">In</th>
<th data-defaultsign="_19">Out</th>
{% endfor %}
<th data-defaultsign="_19">In</th>
<th data-defaultsign="_19">Out</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for category in categories %} {% for id, info in report %}
{% if report.income[category.id] or report.expense[category.id] %} <tr>
<tr> <td data-value="{{ info.name }}">
<td data-value="{{ category.name }}"> <a title="{{ info.name }}" href="#" data-category="{{ id }}" class="category-chart-activate">{{ info.name }}</a>
<a title="{{ category.name }}" href="#" data-category="{{ category.id }}" class="category-chart-activate">{{ category.name }}</a> </td>
{% for key, period in periods %}
{# income first #}
{% if(info.entries[key]) %}
<td data-value="{{ info.entries[key] }}">
{{ info.entries[key]|formatAmount }}
</td>
{% else %}
<td data-value="0">
{{ 0|formatAmount }}
</td>
{% endif %}
{% endfor %}
{# if sum of income, display: #}
{% if info.sum %}
<td data-value="{{ info.sum }}">
{{ info.sum|formatAmount }}
</td> </td>
{% else %}
{% for key, period in periods %} <td data-value="0">
{# income first #} {{ 0|formatAmount }}
{% if(report.income[category.id].entries[key]) %} </td>
<td data-value="{{ report.income[category.id].entries[key] }}"> {% endif %}
{{ report.income[category.id].entries[key]|formatAmount }} </tr>
</td>
{% else %}
<td data-value="0">
{{ 0|formatAmount }}
</td>
{% endif %}
{# expenses #}
{% if(report.expense[category.id].entries[key]) %}
<td data-value="{{ report.expense[category.id].entries[key] }}">
{{ report.expense[category.id].entries[key]|formatAmount }}
</td>
{% else %}
<td data-value="0">
{{ 0|formatAmount }}
</td>
{% endif %}
{% endfor %}
{# if sum of income, display: #}
{% if report.income[category.id].sum %}
<td data-value="{{ report.income[category.id].sum }}">
{{ report.income[category.id].sum|formatAmount }}
</td>
{% else %}
<td data-value="0">
{{ 0|formatAmount }}
</td>
{% endif %}
{# if sum of expense, display: #}
{% if report.expense[category.id].sum %}
<td data-value="{{ report.expense[category.id].sum }}">
{{ report.expense[category.id].sum|formatAmount }}
</td>
{% else %}
<td data-value="0">
{{ 0|formatAmount }}
</td>
{% endif %}
</tr>
{% endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -210,6 +210,7 @@ Route::group(
// budgets: // budgets:
Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']); Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']);
Route::get('/chart/budget/period/0/default/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@periodNoBudget']);
Route::get('/chart/budget/period/{budget}/default/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@period']); Route::get('/chart/budget/period/{budget}/default/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@period']);
// this chart is used in reports: // this chart is used in reports:
@ -410,11 +411,16 @@ Route::group(
['uses' => 'Report\BudgetController@budgetPeriodReport', 'as' => 'reports.data.budgetPeriodReport'] ['uses' => 'Report\BudgetController@budgetPeriodReport', 'as' => 'reports.data.budgetPeriodReport']
); );
// category period overview // category period overview (expense and income)
Route::get( Route::get(
'/reports/data/category-period/{start_date}/{end_date}/{accountList}', '/reports/data/category-expense/{start_date}/{end_date}/{accountList}',
['uses' => 'Report\CategoryController@categoryPeriodReport', 'as' => 'reports.data.categoryPeriodReport'] ['uses' => 'Report\CategoryController@expenseReport', 'as' => 'reports.data.categoryExpense']
); );
Route::get(
'/reports/data/category-income/{start_date}/{end_date}/{accountList}',
['uses' => 'Report\CategoryController@incomeReport', 'as' => 'reports.data.categoryIncome']
);
/** /**
* Rules Controller * Rules Controller