This commit is contained in:
James Cole
2015-09-25 16:00:14 +02:00
parent a838dc163d
commit 45293fbd42
11 changed files with 228 additions and 16 deletions

View File

@@ -45,14 +45,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
foreach ($entries as $entry) { foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format); $data['labels'][] = $entry[0]->formatLocalized($format);
$amount = round($entry[1], 2); $spent = round($entry[1], 2);
if ($amount > 0) { $earned = round($entry[2], 2);
$data['datasets'][0]['data'][] = null;
$data['datasets'][1]['data'][] = $amount; $data['datasets'][0]['data'][] = $spent == 0 ? null : $spent;
} else { $data['datasets'][1]['data'][] = $earned == 0 ? null : $earned;
$data['datasets'][0]['data'][] = $amount * -1;
$data['datasets'][1]['data'][] = null;
}
} }
return $data; return $data;

View File

@@ -5,8 +5,11 @@ use Carbon\Carbon;
use FireflyIII\Http\Requests\CategoryFormRequest; use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input; use Input;
use Navigation;
use Preferences; use Preferences;
use Session; use Session;
use URL; use URL;
@@ -139,6 +142,30 @@ class CategoryController extends Controller
return view('categories.noCategory', compact('list', 'subTitle')); return view('categories.noCategory', compact('list', 'subTitle'));
} }
/**
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Illuminate\View\View
*/
public function showWithDate(CategoryRepositoryInterface $repository, Category $category, $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$set = $repository->getJournalsInRange($category, $page, $start, $end);
$count = $repository->countJournalsInRange($category, $start, $end);
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory'));
}
/** /**
* @param CategoryRepositoryInterface $repository * @param CategoryRepositoryInterface $repository
* @param Category $category * @param Category $category
@@ -156,7 +183,43 @@ class CategoryController extends Controller
$journals = new LengthAwarePaginator($set, $count, 50, $page); $journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id); $journals->setPath('categories/show/' . $category->id);
return view('categories.show', compact('category', 'journals', 'hideCategory', 'totalSum', 'periodSum')); // list of ranges for list of periods:
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-show');
$cache->addProperty($category->id);
if ($cache->has()) {
$entries = $cache->get();
} else {
while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
// here do something.
$spent = $repository->spentInPeriod($category, $end, $currentEnd);
$earned = $repository->earnedInPeriod($category, $end, $currentEnd);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
}
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'totalSum', 'periodSum'));
} }
/** /**

View File

@@ -69,7 +69,6 @@ class CategoryController extends Controller
$earned = $repository->earnedInPeriod($category, $start, $currentEnd); $earned = $repository->earnedInPeriod($category, $start, $currentEnd);
$entries->push([clone $start, $spent, $earned]); $entries->push([clone $start, $spent, $earned]);
$start = Navigation::addPeriod($start, $range, 0); $start = Navigation::addPeriod($start, $range, 0);
} }
$data = $this->generator->all($entries); $data = $this->generator->all($entries);

View File

@@ -244,6 +244,7 @@ Route::group(
Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']); Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']);
Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']); Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']);
Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']); Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
Route::get('/categories/show/{category}/{date}', ['uses' => 'CategoryController@showWithDate', 'as' => 'categories.show.date']);
Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']); Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']);
Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']); Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']);
Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']); Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']);

View File

@@ -309,7 +309,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
$cache->addProperty($category->id); $cache->addProperty($category->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('spentInPeriod'); $cache->addProperty('earnedInPeriod');
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
@@ -323,4 +323,36 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $sum; return $sum;
} }
/**
* @param Category $category
* @param int $page
*
* @return Collection
*/
public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end)
{
$offset = $page > 0 ? $page * 50 : 0;
return $category->transactionJournals()
->after($start)
->before($end)
->withRelevantData()->take(50)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(
['transaction_journals.*']
);
}
/**
* @param Category $category
*
* @return int
*/
public function countJournalsInRange(Category $category, Carbon $start, Carbon $end)
{
return $category->transactionJournals()->before($end)->after($start)->count();
}
} }

View File

@@ -20,6 +20,13 @@ interface CategoryRepositoryInterface
*/ */
public function countJournals(Category $category); public function countJournals(Category $category);
/**
* @param Category $category
*
* @return int
*/
public function countJournalsInRange(Category $category, Carbon $start, Carbon $end);
/** /**
* @param Category $category * @param Category $category
* *
@@ -57,6 +64,14 @@ interface CategoryRepositoryInterface
*/ */
public function getJournals(Category $category, $page); public function getJournals(Category $category, $page);
/**
* @param Category $category
* @param int $page
*
* @return Collection
*/
public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end);
/** /**
* This method returns the sum of the journals in the category, optionally * This method returns the sum of the journals in the category, optionally
* limited by a start or end date. * limited by a start or end date.

View File

@@ -106,13 +106,14 @@ class Navigation
* *
* @return Carbon * @return Carbon
*/ */
public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate) public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate = null)
{ {
$functionMap = [ $functionMap = [
'daily' => 'endOfDay', 'daily' => 'endOfDay',
'week' => 'endOfWeek', 'week' => 'endOfWeek',
'weekly' => 'endOfWeek', 'weekly' => 'endOfWeek',
'month' => 'endOfMonth', 'month' => 'endOfMonth',
'1M' => 'endOfMonth',
'monthly' => 'endOfMonth', 'monthly' => 'endOfMonth',
'quarter' => 'lastOfQuarter', 'quarter' => 'lastOfQuarter',
'quarterly' => 'lastOfQuarter', 'quarterly' => 'lastOfQuarter',
@@ -128,7 +129,7 @@ class Navigation
} }
if ($currentEnd > $maxDate) { if (!is_null($maxDate) && $currentEnd > $maxDate) {
return clone $maxDate; return clone $maxDate;
} }
@@ -149,6 +150,7 @@ class Navigation
'week' => 'Week %W, %Y', 'week' => 'Week %W, %Y',
'weekly' => 'Week %W, %Y', 'weekly' => 'Week %W, %Y',
'quarter' => '%B %Y', 'quarter' => '%B %Y',
'1M' => '%B %Y',
'month' => '%B %Y', 'month' => '%B %Y',
'monthly' => '%B %Y', 'monthly' => '%B %Y',
'year' => '%Y', 'year' => '%Y',
@@ -224,6 +226,7 @@ class Navigation
'week' => 'subWeeks', 'week' => 'subWeeks',
'weekly' => 'subWeeks', 'weekly' => 'subWeeks',
'month' => 'subMonths', 'month' => 'subMonths',
'1M' => 'subMonths',
'monthly' => 'subMonths', 'monthly' => 'subMonths',
'year' => 'subYears', 'year' => 'subYears',
'yearly' => 'subYears', 'yearly' => 'subYears',

View File

@@ -38,7 +38,7 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12"> <div class="col-lg-8 col-md-8 col-sm-12 col-xs-12">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
@@ -49,6 +49,33 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
{% for entry in entries %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title"><a href="{{ route('categories.show.date',[category.id,entry[0]]) }}">{{ entry[1] }}</a>
</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover">
{% if entry[2] != 0 %}
<tr>
<td colspan="33%">{{ 'spent'|_ }}</td>
<td colspan="67%">{{ entry[2]|formatAmount }}</td>
</tr>
{% endif %}
{% if entry[3] != 0 %}
<tr>
<td colspan="33%">{{ 'earned'|_ }}</td>
<td colspan="67%">{{ entry[3]|formatAmount }}</td>
</tr>
{% endif %}
</table>
</div>
</div>
{% endfor %}
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,75 @@
{% extends "./layout/default.twig" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'overview'|_ }} (month)</h3>
</div>
<div class="box-body">
{% if Config.get('firefly.chart') == 'google' %}
<div id="month"></div>
{% endif %}
{% if Config.get('firefly.chart') == 'chartjs' %}
<canvas id="month" style="width:100%;height:350px;"></canvas>
{% endif %}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'overview'|_ }} (all)</h3>
</div>
<div class="box-body">
{% if Config.get('firefly.chart') == 'google' %}
<div id="all"></div>
{% endif %}
{% if Config.get('firefly.chart') == 'chartjs' %}
<canvas id="all" style="width:100%;height:350px;"></canvas>
{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p><a href="{{ route('categories.show',[category.id]) }}">Back to all periods</a></p>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'transactions'|_ }}</h3>
</div>
<div class="box-body no-padding">
{% include 'list/journals' %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript">
var categoryID = {{ category.id }};
</script>
<!-- load the libraries and scripts necessary for Google Charts: -->
{% if Config.get('firefly.chart') == 'google' %}
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="js/gcharts.js"></script>
{% endif %}
{% if Config.get('firefly.chart') == 'chartjs' %}
<script type="text/javascript" src="js/Chart.min.js"></script>
<script type="text/javascript" src="js/charts.js"></script>
{% endif %}
<script type="text/javascript" src="js/categories.js"></script>
{% endblock %}

View File

@@ -19,7 +19,7 @@
<ul> <ul>
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;"> <li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
If you have forgotten your password already, please reset it using If you have forgotten your password already, please reset it using
<a style="color:#337ab7" href="{{ address }}password/email">the password reset tool</a>. <a style="color:#337ab7" href="{{ address }}/password/email">the password reset tool</a>.
</li> </li>
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;"> <li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
There is a help-icon in the top right corner of each page. If you need help, click it! There is a help-icon in the top right corner of each page. If you need help, click it!

View File

@@ -12,7 +12,7 @@ Firefly III:
{{ address }} {{ address }}
Password reset: Password reset:
{{ address }}password/email {{ address }}/password/email
Documentation: Documentation:
https://github.com/JC5/firefly-iii/wiki/First-use https://github.com/JC5/firefly-iii/wiki/First-use