Moving stuff around, optimising charts.

This commit is contained in:
James Cole
2016-05-06 10:32:26 +02:00
parent 6d944ec98f
commit 27f5fe18df
12 changed files with 347 additions and 122 deletions

View File

@@ -6,6 +6,7 @@ use ExpandedForm;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Session;

View File

@@ -289,7 +289,7 @@ class BudgetController extends Controller
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$journals = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end);
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end);
$count = $journals->count();
$journals = $journals->slice($offset, $pageSize);
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
@@ -328,17 +328,15 @@ class BudgetController extends Controller
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$journals = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end);
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end);
$count = $journals->count();
$journals = $journals->slice($offset, $pageSize);
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
$subTitle = trans(
'firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]
);
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
$limits = new Collection([$repetition]);

View File

@@ -139,8 +139,8 @@ class AccountController extends Controller
{
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
// chart properties for cache:
$cache = new CacheProperties();

View File

@@ -8,9 +8,15 @@ use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Preferences;
use Response;
/**
* Class BudgetController
@@ -43,22 +49,47 @@ class BudgetController extends Controller
*/
public function budget(BudgetRepositoryInterface $repository, Budget $budget)
{
$first = $repository->firstUseDate($budget);
$range = Preferences::get('viewRange', '1M')->data;
$last = session('end', new Carbon);
$cache = new CacheProperties();
$cache->addProperty($first);
$cache->addProperty($last);
$cache->addProperty('budget');
if ($cache->has()) {
//return Response::json($cache->get());
}
$final = clone $last;
$final->addYears(2);
$budgetCollection = new Collection([$budget]);
$last = Navigation::endOfX($last, $range, $final); // not to overshoot.
$entries = new Collection;
Log::debug('---- now at chart');
while ($first < $last) {
// periodspecific dates:
$currentStart = Navigation::startOfPeriod($first, $range);
$currentEnd = Navigation::endOfPeriod($first, $range);
// sub another day because reasons.
$currentEnd->subDay();
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$entry = [$first, ($spent * -1)];
$entries->push($entry);
$first = Navigation::addPeriod($first, $range, 0);
}
$data = $this->generator->budgetLimit($entries, 'month');
$cache->store($data);
return Response::json($data);
/**
* // dates and times
* $first = $repository->getFirstBudgetLimitDate($budget);
* $range = Preferences::get('viewRange', '1M')->data;
* $last = session('end', new Carbon);
*
* // chart properties for cache:
* $cache = new CacheProperties();
* $cache->addProperty($first);
* $cache->addProperty($last);
* $cache->addProperty('budget');
* if ($cache->has()) {
*
* //return Response::json($cache->get());
* }
*
* $final = clone $last;
* $final->addYears(2);
* $last = Navigation::endOfX($last, $range, $final);
@@ -96,43 +127,34 @@ class BudgetController extends Controller
*/
public function budgetLimit(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
/**
* $start = clone $repetition->startdate;
* $end = $repetition->enddate;
*
* // chart properties for cache:
* $cache = new CacheProperties();
* $cache->addProperty($start);
* $cache->addProperty($end);
* $cache->addProperty('budget');
* $cache->addProperty('limit');
* $cache->addProperty($budget->id);
* $cache->addProperty($repetition->id);
* if ($cache->has()) {
* return Response::json($cache->get());
* }
*
* $set = $repository->spentPerDay($budget, $start, $end, new Collection);
* $entries = new Collection;
* $amount = $repetition->amount;
*
* // get sum (har har)!
* while ($start <= $end) {
* $formatted = $start->format('Y-m-d');
* $sum = $set[$formatted] ?? '0';
*
* // Sum of expenses on this day:
* $amount = round(bcadd(strval($amount), $sum), 2);
* $entries->push([clone $start, $amount]);
* $start->addDay();
* }
*
* $data = $this->generator->budgetLimit($entries, 'monthAndDay');
* $cache->store($data);
*
* return Response::json($data);
**/
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-limit');
$cache->addProperty($budget->id);
$cache->addProperty($repetition->id);
if ($cache->has()) {
// return Response::json($cache->get());
}
$entries = new Collection;
$amount = $repetition->amount;
$budgetCollection = new Collection([$budget]);
Log::debug('amount starts ' . $amount);
while ($start <= $end) {
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$amount = bcadd($amount, $spent);
$entries->push([clone $start, round($amount, 2)]);
$start->addDay();
}
$data = $this->generator->budgetLimit($entries, 'monthAndDay');
$cache->store($data);
return Response::json($data);
}
/**
@@ -146,19 +168,75 @@ class BudgetController extends Controller
*/
public function frontpage(BudgetRepositoryInterface $repository, ARI $accountRepository)
{
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget');
$cache->addProperty('all');
if ($cache->has()) {
return Response::json($cache->get());
}
$budgets = $repository->getActiveBudgets();
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$allEntries = new Collection;
$format = strval(trans('config.month_and_day'));
/** @var Budget $budget */
foreach ($budgets as $budget) {
// get relevant repetitions:
$name = $budget->name;
$reps = $repetitions->filter(
function (LimitRepetition $repetition) use ($budget, $start, $end) {
if ($repetition->startdate < $end && $repetition->enddate > $start && $repetition->budget_id === $budget->id) {
return $repetition;
}
}
);
if ($reps->count() === 0) {
$amount = '0';
$left = '0';
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
$overspent = '0';
$allEntries->push([$name, $left, $spent, $overspent, $amount, $spent]);
}
/** @var LimitRepetition $repetition */
foreach ($reps as $repetition) {
$expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
if ($reps->count() > 1) {
$name = $budget->name . ' ' . trans(
'firefly.between_dates',
['start' => $repetition->startdate->formatLocalized($format), 'end' => $repetition->enddate->formatLocalized($format)]
);
}
$amount = $repetition->amount;
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
$spent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcmul($amount, '-1') : $expenses;
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
$allEntries->push([$name, $left, $spent, $overspent, $amount, $spent]);
}
}
$list = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end);
$sum = '0';
/** @var TransactionJournal $entry */
foreach ($list as $entry) {
$sum = bcadd(TransactionJournal::amount($entry), $sum);
}
$allEntries->push([trans('firefly.no_budget'), '0', '0', $sum, '0', '0']);
$data = $this->generator->frontpage($allEntries);
$cache->store($data);
return Response::json($data);
/**
* $start = session('start', Carbon::now()->startOfMonth());
* $end = session('end', Carbon::now()->endOfMonth());
*
* // chart properties for cache:
* $cache = new CacheProperties();
* $cache->addProperty($start);
* $cache->addProperty($end);
* $cache->addProperty('budget');
* $cache->addProperty('all');
* if ($cache->has()) {
* return Response::json($cache->get());
* }
*
*
*
* $budgets = $repository->getBudgetsAndLimitsInRange($start, $end);
* $allEntries = new Collection;
@@ -379,8 +457,10 @@ class BudgetController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function year(BudgetRepositoryInterface $repository, string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
public
function year(
BudgetRepositoryInterface $repository, string $reportType, Carbon $start, Carbon $end, Collection $accounts
) {
/**
* // chart properties for cache:
* $cache = new CacheProperties();

View File

@@ -130,7 +130,7 @@ class Transaction extends Model
if (!self::isJoined($query, 'transaction_journals')) {
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
}
$query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
$query->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'));
}
/**

View File

@@ -245,7 +245,7 @@ class AccountRepository implements AccountRepositoryInterface
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id');
}
)->where('source.account_id', $account->id);
$query->take(10);
$set = $query->get(TransactionJournal::queryFields());
return $set;

View File

@@ -12,7 +12,6 @@ use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
use Log;
@@ -123,6 +122,34 @@ class BudgetRepository implements BudgetRepositoryInterface
// return new Carbon;
// }
/**
* This method returns the oldest journal or transaction date known to this budget.
* Will cache result.
*
* @param Budget $budget
*
* @return Carbon
*/
public function firstUseDate(Budget $budget): Carbon
{
$oldest = Carbon::create()->startOfYear();
$journal = $budget->transactionjournals()->orderBy('date', 'ASC')->first();
if ($journal) {
$oldest = $journal->date < $oldest ? $journal->date : $oldest;
}
$transaction = $budget
->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.id')
->orderBy('transaction_journals.date', 'ASC')->first(['transactions.*', 'transaction_journals.date']);
if ($transaction) {
$oldest = $transaction->date < $oldest ? $transaction->date : $oldest;
}
return $oldest;
}
/**
* @return Collection
*/
@@ -140,26 +167,6 @@ class BudgetRepository implements BudgetRepositoryInterface
return $set;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end): Collection
{
$query = LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'))
->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00'))
->where('budgets.user_id', $this->user->id);
$set = $query->get(['limit_repetitions.*', 'budget_limits.budget_id']);
return $set;
}
// /**
// * @param Account $account
// * @param Carbon $start
@@ -217,18 +224,21 @@ class BudgetRepository implements BudgetRepositoryInterface
// }
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgets(): Collection
public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end): Collection
{
/** @var Collection $set */
$set = $this->user->budgets()->get();
$query = LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'))
->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00'))
->where('budgets.user_id', $this->user->id);
$set = $set->sortBy(
function (Budget $budget) {
return strtolower($budget->name);
}
);
$set = $query->get(['limit_repetitions.*', 'budget_limits.budget_id']);
return $set;
}
@@ -505,10 +515,10 @@ class BudgetRepository implements BudgetRepositoryInterface
/**
* @return Collection
*/
public function getInactiveBudgets(): Collection
public function getBudgets(): Collection
{
/** @var Collection $set */
$set = $this->user->budgets()->where('active', 0)->get();
$set = $this->user->budgets()->get();
$set = $set->sortBy(
function (Budget $budget) {
@@ -660,7 +670,7 @@ class BudgetRepository implements BudgetRepositoryInterface
// }
// )
// ->whereIn('transactions.account_id', $ids)
// //->having('transaction_count', '=', 1) TODO check if this still works
// //->having('transaction_count', '=', 1) TO DO check if this still works
// ->transactionTypes([TransactionType::WITHDRAWAL])
// ->first(
// [
@@ -829,6 +839,23 @@ class BudgetRepository implements BudgetRepositoryInterface
// return $return;
// }
/**
* @return Collection
*/
public function getInactiveBudgets(): Collection
{
/** @var Collection $set */
$set = $this->user->budgets()->where('active', 0)->get();
$set = $set->sortBy(
function (Budget $budget) {
return strtolower($budget->name);
}
);
return $set;
}
/**
* @param Collection $budgets
* @param Collection $accounts
@@ -918,6 +945,7 @@ class BudgetRepository implements BudgetRepositoryInterface
}
}
);
return $set;
}
@@ -932,13 +960,15 @@ class BudgetRepository implements BudgetRepositoryInterface
public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string
{
$set = $this->journalsInPeriod($budgets, $accounts, $start, $end);
Log::debug('spentInPeriod set count is ' . $set->count());
//Log::debug('spentInPeriod set count is ' . $set->count());
$sum = '0';
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
$sum = bcadd($sum, TransactionJournal::amount($journal));
}
Log::debug('spentInPeriod between ' . $start->format('Y-m-d') . ' and ' . $end->format('Y-m-d') . ' is ' . $sum);
return $sum;
}

View File

@@ -50,6 +50,15 @@ interface BudgetRepositoryInterface
*/
public function find(int $budgetId): Budget;
/**
* This method returns the oldest journal or transaction date known to this budget.
* Will cache result.
* @param Budget $budget
*
* @return Carbon
*/
public function firstUseDate(Budget $budget): Carbon;
// /**
// * @param Budget $budget
// * @param Account $account

View File

@@ -20,6 +20,7 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
/**
* Class TransactionJournalSupport
@@ -157,6 +158,8 @@ class TransactionJournalSupport extends Model
}
/**
* @deprecated
*
* @param TransactionJournal $journal
*
* @return Account
@@ -181,6 +184,31 @@ class TransactionJournalSupport extends Model
return $account;
}
/**
* @param TransactionJournal $journal
*
* @return Collection
*/
public static function destinationAccountList(TransactionJournal $journal): Collection
{
$cache = new CacheProperties;
$cache->addProperty($journal->id);
$cache->addProperty('transaction-journal');
$cache->addProperty('destination-account-list');
if ($cache->has()) {
return $cache->get();
}
$transactions = $journal->transactions()->where('amount', '>', 0)->with('account')->get();
$list = new Collection;
/** @var Transaction $t */
foreach ($transactions as $t) {
$list->push($t->account);
}
$cache->store($list);
return $list;
}
/**
* @param TransactionJournal $journal
*
@@ -253,6 +281,8 @@ class TransactionJournalSupport extends Model
}
/**
* @deprecated
*
* @param TransactionJournal $journal
*
* @return Account
@@ -277,6 +307,32 @@ class TransactionJournalSupport extends Model
return $account;
}
/**
* @param TransactionJournal $journal
*
* @return Collection
*/
public static function sourceAccountList(TransactionJournal $journal): Collection
{
$cache = new CacheProperties;
$cache->addProperty($journal->id);
$cache->addProperty('transaction-journal');
$cache->addProperty('source-account-list');
if ($cache->has()) {
return $cache->get();
}
$transactions = $journal->transactions()->where('amount', '<', 0)->with('account')->get();
$list = new Collection;
/** @var Transaction $t */
foreach ($transactions as $t) {
$list->push($t->account);
}
$cache->store($list);
return $list;
}
/**
* @param TransactionJournal $journal
*

View File

@@ -5,6 +5,7 @@ namespace FireflyIII\Support;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use Log;
/**
* Class Navigation
@@ -107,7 +108,9 @@ class Navigation
$currentEnd->$function();
}
if (in_array($repeatFreq, $subDay)) {
Log::debug('Before subday: ' . $currentEnd->format('Y-m-d'));
$currentEnd->subDay();
Log::debug('After subday: ' . $currentEnd->format('Y-m-d'));
}
return $currentEnd;

View File

@@ -4,7 +4,9 @@ declare(strict_types = 1);
namespace FireflyIII\Support\Twig;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\CacheProperties;
use Twig_Extension;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
@@ -16,6 +18,39 @@ use Twig_SimpleFunction;
*/
class Journal extends Twig_Extension
{
/**
* @return Twig_SimpleFunction
*/
public function getDestinationAccount(): Twig_SimpleFunction
{
return new Twig_SimpleFunction(
'destinationAccount', function (TransactionJournal $journal) {
$cache = new CacheProperties;
$cache->addProperty($journal->id);
$cache->addProperty('transaction-journal');
$cache->addProperty('destination-account-string');
if ($cache->has()) {
return $cache->get();
}
$list = TransactionJournal::destinationAccountList($journal);
$array = [];
/** @var Account $entry */
foreach ($list as $entry) {
if ($entry->accountType->type == 'Cash account') {
$array[] = '<span class="text-success">(cash)</span>';
continue;
}
$array[] = '<a title="' . e($entry->name) . '" href="' . route('accounts.show', $entry->id) . '">' . e($entry->name) . '</a>';
}
$result = join(', ', $array);
$cache->store($result);
return $result;
}
);
}
/**
* @return array
@@ -33,7 +68,8 @@ class Journal extends Twig_Extension
public function getFunctions(): array
{
$functions = [
$this->invalidJournal(),
$this->getSourceAccount(),
$this->getDestinationAccount(),
];
return $functions;
@@ -52,15 +88,35 @@ class Journal extends Twig_Extension
/**
* @return Twig_SimpleFunction
*/
protected function invalidJournal(): Twig_SimpleFunction
public function getSourceAccount(): Twig_SimpleFunction
{
return new Twig_SimpleFunction(
'invalidJournal', function (TransactionJournal $journal): bool {
if (!isset($journal->transactions[1]) || !isset($journal->transactions[0])) {
return true;
'sourceAccount', function (TransactionJournal $journal): string {
$cache = new CacheProperties;
$cache->addProperty($journal->id);
$cache->addProperty('transaction-journal');
$cache->addProperty('source-account-string');
if ($cache->has()) {
return $cache->get();
}
return false;
$list = TransactionJournal::sourceAccountList($journal);
$array = [];
/** @var Account $entry */
foreach ($list as $entry) {
if ($entry->accountType->type == 'Cash account') {
$array[] = '<span class="text-success">(cash)</span>';
continue;
}
$array[] = '<a title="' . e($entry->name) . '" href="' . route('accounts.show', $entry->id) . '">' . e($entry->name) . '</a>';
}
$result = join(', ', $array);
$cache->store($result);
return $result;
}
);
}

View File

@@ -63,18 +63,10 @@
{{ journal.date.formatLocalized(monthAndDayFormat) }}
</td>
<td class="hidden-xs">
{% if journal.source_account_type == 'Cash account' %}
<span class="text-success">(cash)</span>
{% else %}
<a href="{{ route('accounts.show',journal.source_account_id) }}">{{ journal.source_account_name }}</a>
{% endif %}
{{ sourceAccount(journal)|raw }}
</td>
<td class="hidden-xs">
{% if journal.destination_account_type == 'Cash account' %}
<span class="text-success">(cash)</span>
{% else %}
<a href="{{ route('accounts.show',journal.destination_account_id) }}">{{ journal.destination_account_name }}</a>
{% endif %}
{{ destinationAccount(journal)|raw }}
</td>
<!-- Do NOT hide the budget? -->