diff --git a/.coveralls.yml b/.coveralls.yml
new file mode 100644
index 0000000000..8951ac7b73
--- /dev/null
+++ b/.coveralls.yml
@@ -0,0 +1,3 @@
+src_dir: .
+coverage_clover: tests/_output/coverage.xml
+json_path: tests/_output/coveralls-upload.json
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000..102eb830b2
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,24 @@
+language: php
+
+php:
+ - 5.5
+ - 5.6
+
+addons:
+ code_climate:
+ repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
+
+install:
+ - rm composer.lock
+ - composer install
+
+script:
+ - ./tests/_data/db.sh
+ - php vendor/bin/codecept build
+ - php vendor/bin/codecept run --coverage --coverage-xml
+
+after_script:
+ - cp -v tests/_output/coverage.xml build/logs/clover.xml
+ - php vendor/bin/coveralls
+ - vendor/bin/test-reporter --stdout > codeclimate.json
+ - "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"
\ No newline at end of file
diff --git a/app/Http/Controllers/GoogleChartController.php b/app/Http/Controllers/GoogleChartController.php
new file mode 100644
index 0000000000..9fe515eeef
--- /dev/null
+++ b/app/Http/Controllers/GoogleChartController.php
@@ -0,0 +1,135 @@
+addColumn('Day of the month', 'date');
+
+ $frontPage = Preferences::get('frontPageAccounts', []);
+ $start = Session::get('start', Carbon::now()->startOfMonth());
+ $end = Session::get('end', Carbon::now()->endOfMonth());
+
+ if ($frontPage->data == []) {
+ $accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
+ } else {
+ $accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->get(['accounts.*']);
+ }
+ $index = 1;
+ /** @var Account $account */
+ foreach ($accounts as $account) {
+ $chart->addColumn('Balance for ' . $account->name, 'number');
+ $chart->addCertainty($index);
+ $index++;
+ }
+ $current = clone $start;
+ $current->subDay();
+ $today = Carbon::now();
+ while ($end >= $current) {
+ $row = [clone $current];
+ $certain = $current < $today;
+ foreach ($accounts as $account) {
+ $row[] = Steam::balance($account, $current);
+ $row[] = $certain;
+ }
+ $chart->addRowArray($row);
+ $current->addDay();
+ }
+ $chart->generate();
+
+ return Response::json($chart->getData());
+
+ }
+
+
+ /**
+ * @param GChart $chart
+ *
+ * @return \Symfony\Component\HttpFoundation\Response
+ */
+ public function allBudgetsHomeChart(GChart $chart)
+ {
+ $chart->addColumn('Budget', 'string');
+ $chart->addColumn('Budgeted', 'number');
+ $chart->addColumn('Spent', 'number');
+
+ $budgets = Auth::user()->budgets()->orderBy('name', 'DESC')->get();
+ $start = Session::get('start', Carbon::now()->startOfMonth());
+ $end = Session::get('end', Carbon::now()->endOfMonth());
+
+ /** @var Budget $budget */
+ foreach ($budgets as $budget) {
+
+ /** @var \LimitRepetition $repetition */
+ $repetition = LimitRepetition::
+ leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
+ ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
+ ->where('budget_limits.budget_id', $budget->id)
+ ->first(['limit_repetitions.*']);
+ if (is_null($repetition)) { // use the session start and end for our search query
+ $searchStart = $start;
+ $searchEnd = $end;
+ $limit = 0; // the limit is zero:
+ } else {
+ // use the limit's start and end for our search query
+ $searchStart = $repetition->startdate;
+ $searchEnd = $repetition->enddate;
+ $limit = floatval($repetition->amount); // the limit is the repetitions limit:
+ }
+
+ $expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
+ if ($expenses > 0) {
+ $chart->addRow($budget->name, $limit, $expenses);
+ }
+ }
+
+ $noBudgetSet = Auth::user()
+ ->transactionjournals()
+ ->whereNotIn(
+ 'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
+ $query
+ ->select('transaction_journals.id')
+ ->from('transaction_journals')
+ ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
+ ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
+ ->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'));
+ }
+ )
+ ->before($end)
+ ->after($start)
+ ->lessThan(0)
+ ->transactionTypes(['Withdrawal'])
+ ->get();
+ $sum = $noBudgetSet->sum('amount') * -1;
+ $chart->addRow('No budget', 0, $sum);
+ $chart->generate();
+
+ return Response::json($chart->getData());
+ }
+
+
+}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index 7eaa83087c..41fb237c2b 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -1,6 +1,6 @@
'HomeController@index', 'as' => 'index']);
@@ -8,7 +8,7 @@ Route::get('/prev', ['uses' => 'HomeController@sessionPrev', 'as' => 'sessionPre
Route::get('/next', ['uses' => 'HomeController@sessionNext', 'as' => 'sessionNext']);
Route::get('/jump/{range}', ['uses' => 'HomeController@rangeJump', 'as' => 'rangeJump']);
-/*
+/**
* Account Controller
*/
Route::get('/accounts/{what}', ['uses' => 'AccountController@index', 'as' => 'accounts.index'])->where('what', 'revenue|asset|expense');
@@ -17,10 +17,9 @@ Route::get('/accounts/create/{what}', ['uses' => 'AccountController@create', 'as
//Route::get('/accounts/delete/{account}', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']);
Route::get('/accounts/show/{account}/{view?}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']);
-/*
+/**
* Bills Controller
*/
-// bills controller
Route::get('/bills', ['uses' => 'BillController@index', 'as' => 'bills.index']);
//Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
@@ -28,7 +27,7 @@ Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.c
//Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
//Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);
-/*
+/**
* Budget Controller
*/
Route::get('/budgets', ['uses' => 'BudgetController@index', 'as' => 'budgets.index']);
@@ -39,7 +38,7 @@ Route::get('/budgets', ['uses' => 'BudgetController@index', 'as' => 'budgets.ind
//Route::get('/budgets/show/{budget}/{limitrepetition?}', ['uses' => 'BudgetController@show', 'as' => 'budgets.show']);
//Route::get('/budgets/list/noBudget', ['uses' => 'BudgetController@noBudget', 'as' => 'budgets.noBudget']);
-/*
+/**
* Category Controller
*/
Route::get('/categories', ['uses' => 'CategoryController@index', 'as' => 'categories.index']);
@@ -49,7 +48,7 @@ Route::get('/categories', ['uses' => 'CategoryController@index', 'as' => 'catego
//Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
//Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']);
-/*
+/**
* Currency Controller
*/
Route::get('/currency', ['uses' => 'CurrencyController@index', 'as' => 'currency.index']);
@@ -58,7 +57,20 @@ Route::get('/currency', ['uses' => 'CurrencyController@index', 'as' => 'currency
//Route::get('/currency/delete/{currency}', ['uses' => 'CurrencyController@delete', 'as' => 'currency.delete']);
//Route::get('/currency/default/{currency}', ['uses' => 'CurrencyController@defaultCurrency', 'as' => 'currency.default']);
-/*
+/**
+ * Google Chart Controller
+ */
+Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']);
+Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']);
+//Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']);
+//Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']);
+//Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']);
+//Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
+//Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
+//Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']);
+
+
+/**
* Piggy Bank Controller
*/
// piggy bank controller
@@ -70,28 +82,27 @@ Route::get('/piggy_banks', ['uses' => 'PiggyBankController@index', 'as' => 'pigg
//Route::get('/piggy_banks/delete/{piggyBank}', ['uses' => 'PiggyBankController@delete', 'as' => 'piggy_banks.delete']);
//Route::get('/piggy_banks/show/{piggyBank}', ['uses' => 'PiggyBankController@show', 'as' => 'piggy_banks.show']);
-/*
+/**
* Preferences Controller
*/
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
-/*
+/**
* Profile Controller
*/
Route::get('/profile', ['uses' => 'ProfileController@index', 'as' => 'profile']);
//Route::get('/profile/change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
-/*
+/**
* Repeated Expenses Controller
*/
-// repeated expenses controller:
Route::get('/repeatedexpenses', ['uses' => 'RepeatedExpenseController@index', 'as' => 'repeated.index']);
//Route::get('/repeatedexpenses/create', ['uses' => 'RepeatedExpenseController@create', 'as' => 'repeated.create']);
//Route::get('/repeatedexpenses/edit/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@edit', 'as' => 'repeated.edit']);
//Route::get('/repeatedexpenses/delete/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@delete', 'as' => 'repeated.delete']);
//Route::get('/repeatedexpenses/show/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@show', 'as' => 'repeated.show']);
-/*
+/**
* Report Controller
*/
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
@@ -99,15 +110,14 @@ Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.ind
//Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']);
//Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']);
-/*
+/**
* Search Controller
*/
Route::get('/search', ['uses' => 'SearchController@index', 'as' => 'search']);
-/*
+/**
* Transaction Controller
*/
-// transaction controller:
Route::get('/transactions/{what}', ['uses' => 'TransactionController@index', 'as' => 'transactions.index'])->where(
['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']
);
@@ -123,7 +133,7 @@ Route::get('/transaction/show/{tj}', ['uses' => 'TransactionController@show', 'a
//Route::post('/transactions/doRelate', ['uses' => 'TransactionController@doRelate', 'as' => 'transactions.doRelate']);
//Route::any('/transactions/unrelate/{tj}', ['uses' => 'TransactionController@unrelate', 'as' => 'transactions.unrelate']);
-/*
+/**
* User Controller
* TODO move to AuthController
*/
diff --git a/app/Models/Account.php b/app/Models/Account.php
index ec0b931223..52895ec134 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -26,6 +26,11 @@ class Account extends Model
return $this->belongsTo('FireflyIII\User');
}
+ public function transactions()
+ {
+ return $this->hasMany('FireflyIII\Models\Transaction');
+ }
+
public function scopeAccountTypeIn(EloquentBuilder $query, array $types)
{
if (is_null($this->joinedAccountTypes)) {
diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php
index ada65f6e16..15f6a1e2db 100644
--- a/app/Models/TransactionJournal.php
+++ b/app/Models/TransactionJournal.php
@@ -1,7 +1,10 @@
belongsToMany('FireflyIII\Models\Category');
}
+ public function getDates()
+ {
+ return ['created_at', 'updated_at', 'date'];
+ }
+
public function getDescriptionAttribute($value)
{
if ($this->encrypted) {
@@ -36,6 +44,59 @@ class TransactionJournal extends Model
return $this->hasMany('FireflyIII\Models\PiggyBankEvent');
}
+ /**
+ * @param EloquentBuilder $query
+ * @param Carbon $date
+ *
+ * @return mixed
+ */
+ public function scopeAfter(EloquentBuilder $query, Carbon $date)
+ {
+ return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00'));
+ }
+
+ /**
+ * @param EloquentBuilder $query
+ * @param Carbon $date
+ *
+ * @return mixed
+ */
+ public function scopeBefore(EloquentBuilder $query, Carbon $date)
+ {
+ return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
+ }
+
+ /**
+ * @param EloquentBuilder $query
+ * @param $amount
+ */
+ public function scopeLessThan(EloquentBuilder $query, $amount)
+ {
+ if (is_null($this->joinedTransactions)) {
+ $query->leftJoin(
+ 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
+ );
+ $this->joinedTransactions = true;
+ }
+
+ $query->where('transactions.amount', '<=', $amount);
+ }
+
+ /**
+ * @param EloquentBuilder $query
+ * @param array $types
+ */
+ public function scopeTransactionTypes(EloquentBuilder $query, array $types)
+ {
+ if (is_null($this->joinedTransactionTypes)) {
+ $query->leftJoin(
+ 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
+ );
+ $this->joinedTransactionTypes = true;
+ }
+ $query->whereIn('transaction_types.type', $types);
+ }
+
public function setDescriptionAttribute($value)
{
$this->attributes['description'] = \Crypt::encrypt($value);
@@ -67,10 +128,4 @@ class TransactionJournal extends Model
return $this->belongsTo('FireflyIII\User');
}
- public function getDates()
- {
- return ['created_at', 'updated_at','date'];
- }
-
-
}
diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php
index 3c0b53e882..88d9837a6b 100644
--- a/app/Providers/FireflyServiceProvider.php
+++ b/app/Providers/FireflyServiceProvider.php
@@ -23,6 +23,12 @@ class FireflyServiceProvider extends ServiceProvider
return new \FireflyIII\Support\Amount;
}
);
+
+ $this->app->bind(
+ 'steam', function () {
+ return new \FireflyIII\Support\Steam;
+ }
+ );
}
}
\ No newline at end of file
diff --git a/app/Support/Amount.php b/app/Support/Amount.php
index af02b5b8e8..019f6cc0c1 100644
--- a/app/Support/Amount.php
+++ b/app/Support/Amount.php
@@ -3,6 +3,9 @@
namespace FireflyIII\Support;
use FireflyIII\Models\Transaction;
+use FireflyIII\Models\TransactionCurrency;
+use Preferences as Prefs;
+use Cache;
/**
* Class Amount
*
@@ -54,4 +57,29 @@ class Amount
// €
return $symbol . ' ' . $string;
}
+
+ /**
+ * @return string
+ */
+ public function getCurrencyCode()
+ {
+ if (defined('FFCURRENCYCODE')) {
+ return FFCURRENCYCODE;
+ }
+ if (Cache::has('FFCURRENCYCODE')) {
+ define('FFCURRENCYCODE', Cache::get('FFCURRENCYCODE'));
+
+ return FFCURRENCYCODE;
+ }
+
+
+ $currencyPreference = Prefs::get('currencyPreference', 'EUR');
+ $currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
+
+ \Cache::forever('FFCURRENCYCODE', $currency->code);
+
+ define('FFCURRENCYCODE', $currency->code);
+
+ return $currency->code;
+ }
}
\ No newline at end of file
diff --git a/app/Support/Facades/Steam.php b/app/Support/Facades/Steam.php
new file mode 100644
index 0000000000..dc8e3fd3eb
--- /dev/null
+++ b/app/Support/Facades/Steam.php
@@ -0,0 +1,23 @@
+transactions()->leftJoin(
+ 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
+ )->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
+ );
+
+ return $balance;
+ }
+
+}
\ No newline at end of file
diff --git a/codeception.yml b/codeception.yml
new file mode 100644
index 0000000000..1c64b071f9
--- /dev/null
+++ b/codeception.yml
@@ -0,0 +1,22 @@
+actor: Tester
+paths:
+ tests: tests
+ log: tests/_output
+ data: tests/_data
+ helpers: tests/_support
+settings:
+ bootstrap: _bootstrap.php
+ colors: true
+ memory_limit: 1024M
+modules:
+ config:
+coverage:
+ enabled: true
+ remote: false
+ whitelist:
+ include:
+ - app/controllers/*
+ - app/models/*
+ - app/lib/FireflyIII/*
+ exclude:
+ - app/controllers/BaseController.php
diff --git a/config/app.php b/config/app.php
index 66080a7d2a..377fc5eedf 100644
--- a/config/app.php
+++ b/config/app.php
@@ -201,6 +201,7 @@ return [
'Preferences' => 'FireflyIII\Support\Facades\Preferences',
'Navigation' => 'FireflyIII\Support\Facades\Navigation',
'Amount' => 'FireflyIII\Support\Facades\Amount',
+ 'Steam' => 'FireflyIII\Support\Facades\Steam',
],
diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php
index 6dceb03a91..3a212eb533 100644
--- a/resources/views/index.blade.php
+++ b/resources/views/index.blade.php
@@ -114,7 +114,7 @@
@stop
@section('scripts')