Compare commits

...

89 Commits
3.3.8 ... 3.4

Author SHA1 Message Date
James Cole
ae90815708 Merge branch 'release/3.4' 2015-05-02 23:51:44 +02:00
James Cole
1c460343b7 New version! 2015-05-02 23:51:34 +02:00
James Cole
26079622f9 Fixed JSON controller tests. 2015-05-02 23:39:39 +02:00
James Cole
06c6d6096f Fixed coverage. 2015-05-02 23:28:04 +02:00
James Cole
c8183aea51 Format amount [skip ci] 2015-05-02 22:50:40 +02:00
James Cole
7531134ad2 Some tag related fine tuning. 2015-05-02 22:41:32 +02:00
James Cole
cd0c6439c2 Delete tags now possible. 2015-05-02 22:30:59 +02:00
James Cole
c918c93f51 Forget me own head next. [skip ci] 2015-05-02 22:13:37 +02:00
James Cole
97d3bd68ed Show balance act tags. 2015-05-02 22:12:26 +02:00
James Cole
75f1e034ae Fixed menu. [skip ci] 2015-05-02 22:09:21 +02:00
James Cole
8e72f218f1 Show HTML 2015-05-02 22:05:57 +02:00
James Cole
d643e05c5a Basic amount format fix. 2015-05-02 22:05:18 +02:00
James Cole
fea44834d0 Fixed test to match fixed controller. 2015-05-02 21:22:27 +02:00
James Cole
195a2d7523 Removed all the old views. 2015-05-02 21:18:04 +02:00
James Cole
b2fd346ef8 Merge branch 'feature/twig' into develop 2015-05-02 21:16:21 +02:00
James Cole
1ff8b62cb7 And now with actual coverage. 2015-05-02 19:44:31 +02:00
James Cole
c7ae15a41a This should fix all twig tests. Let's find out! 2015-05-02 19:44:12 +02:00
James Cole
efe6f59f79 Last templates. Should cover everything. [skip ci] 2015-05-02 19:19:47 +02:00
James Cole
8e31d0491d Fixed search [skip ci] 2015-05-02 18:30:59 +02:00
James Cole
73d4a10351 Fixed reports [skip ci] 2015-05-02 18:26:55 +02:00
James Cole
81d9d4dbc7 Various files updated to twig [skip ci] 2015-05-02 13:20:49 +02:00
James Cole
184ec13f99 Fixed bills [skip ci] 2015-05-02 12:51:02 +02:00
James Cole
dee0422eff Fix preferences [skip ci] 2015-05-02 12:22:19 +02:00
James Cole
81a2975f1a Show piggy banks [skip ci] 2015-05-02 11:32:45 +02:00
James Cole
5cd2ef4a5e Some generic templates and the first piggy bank ones [skip ci] 2015-05-02 10:53:54 +02:00
James Cole
466c2a68c2 Add delete form [skip ci] 2015-05-02 10:42:41 +02:00
James Cole
0fb9fba531 New currency routes 2015-05-02 10:40:20 +02:00
James Cole
356dd2c6cd Creat / edit / index [skip ci] 2015-05-02 10:37:16 +02:00
James Cole
32c4661233 Fixed no budget / no category bread crumb [skip ci] 2015-05-02 09:49:37 +02:00
James Cole
a245b504ec Fixed bread crumbs. 2015-05-02 09:31:57 +02:00
James Cole
a2f5fbdfd3 Working on two more views [skip ci] 2015-05-02 09:29:34 +02:00
James Cole
04be26adc5 Fixed title [skip ci] 2015-05-02 09:26:20 +02:00
James Cole
be0adb7cf2 Create / edit category 2015-05-02 09:25:45 +02:00
James Cole
84fd92bf5a Categories [skip ci] 2015-05-02 09:21:42 +02:00
James Cole
ee4e061739 Removed code from budget index [skip ci] 2015-05-02 09:21:32 +02:00
James Cole
4a2b01cd9a Menu fixes [skip ci] 2015-05-02 09:16:17 +02:00
James Cole
efdd6460fb Fixed injection. 2015-05-02 09:08:41 +02:00
James Cole
803827e05c Fixed more views. [skip ci] 2015-05-02 09:06:07 +02:00
James Cole
a41d5e9ab3 New budget [skip ci] 2015-05-02 08:32:20 +02:00
James Cole
e94333f877 Can view budgets [skip ci] 2015-05-02 08:28:24 +02:00
James Cole
6dbb80d687 Optimisations [skip ci] 2015-05-02 08:09:54 +02:00
James Cole
31a1031624 And delete is fixed. 2015-05-01 23:39:45 +02:00
James Cole
f8e9ce0d52 Can edit as well. 2015-05-01 23:35:35 +02:00
James Cole
11baa968cd Can create accounts. 2015-05-01 23:17:17 +02:00
James Cole
3e5e5b376f Made it almost to the accounts. 2015-05-01 22:44:35 +02:00
James Cole
bda18f296d Optimised Twig extensions. 2015-05-01 20:17:06 +02:00
James Cole
287d110c84 Small bug fix in budget chart. 2015-05-01 18:45:42 +02:00
James Cole
c98275e73a More templates converted to twig. 2015-05-01 18:44:49 +02:00
James Cole
aa573cc951 Fix redirect 2015-05-01 14:32:31 +02:00
James Cole
883d3d86e3 Update a test. 2015-05-01 14:32:26 +02:00
James Cole
fb047bd5f4 Fix migration. 2015-05-01 08:34:53 +02:00
James Cole
66a04d8365 Slightly different chart on index. 2015-05-01 08:34:44 +02:00
James Cole
a0e501f9fd First attempt at implementing Twig. 2015-05-01 08:29:41 +02:00
James Cole
8b40d3346d An attempt at showing tag info. 2015-05-01 06:47:24 +02:00
James Cole
eddf5cd250 Fix button, close #74 2015-05-01 06:38:48 +02:00
James Cole
ee5c534ca3 Took the time to fix the tests. 2015-04-30 17:32:00 +02:00
James Cole
fd4e77ae0f Catch empty tags. 2015-04-28 21:01:54 +02:00
James Cole
b747c50aa3 Fix method to return object, not key. 2015-04-28 21:00:26 +02:00
James Cole
deebdd86a6 And another small JS fix [skip ci] 2015-04-28 20:20:16 +02:00
James Cole
b2db79cc10 Fixed some small CSS / JS problems. 2015-04-28 20:17:31 +02:00
James Cole
bbab370b1e Some new code. 2015-04-28 17:10:52 +02:00
James Cole
db4adf399d Cleaning up. 2015-04-28 15:26:30 +02:00
James Cole
0683c87e52 Small check in tags. 2015-04-28 14:08:58 +02:00
James Cole
3f62b647fc Removed code related to "related transactions". 2015-04-28 13:50:53 +02:00
James Cole
2562a5b30d Don't save all tags unconditionally. 2015-04-28 13:47:13 +02:00
James Cole
e232f2e223 Delete tags 2015-04-28 11:04:46 +02:00
James Cole
21644ff4dd Routes will throw 404's 2015-04-28 11:04:38 +02:00
James Cole
be96a4fce5 Show tags in transaction. 2015-04-28 10:51:56 +02:00
James Cole
a1cabcbed3 Show tag icons. 2015-04-28 10:50:43 +02:00
James Cole
ce933b1f06 Edit and update tags. 2015-04-28 10:36:13 +02:00
James Cole
51ae130922 Fixed the build by adding new migrations for each step. 2015-04-28 09:54:22 +02:00
James Cole
da1bc18a47 Better list for piggy banks. 2015-04-28 09:15:31 +02:00
James Cole
e7165a526b First attempt at basic tag functionality. 2015-04-28 08:58:01 +02:00
James Cole
6081cc399f Small bug fixes and cleaning up. 2015-04-28 08:12:12 +02:00
James Cole
7c5c24e15d Moved edit buttons. 2015-04-26 07:42:55 +02:00
James Cole
e3e55b4347 Moved all create-buttons lower. 2015-04-26 07:39:49 +02:00
James Cole
40d8e7d1ad Fixed a bug where transfers away from a piggy bank's account would not properly reflect the piggy bank. 2015-04-24 12:06:13 +02:00
James Cole
59e23b89f2 Removed unnecessary sort. 2015-04-24 11:54:23 +02:00
James Cole
cc33af8193 Could not set order. 2015-04-24 10:00:56 +02:00
James Cole
0e1e7eb2a9 Merge branch 'release/3.3.9' 2015-04-22 08:08:14 +02:00
James Cole
52a28a7758 Merge branch 'release/3.3.9' into develop 2015-04-22 08:08:14 +02:00
James Cole
3f752d6832 New read me. 2015-04-22 08:08:05 +02:00
James Cole
fe714e9989 Built a routine that will allow you to completely delete an account. 2015-04-22 07:54:56 +02:00
James Cole
0d3213a379 Covered the preferences controller. 2015-04-21 17:19:14 +02:00
James Cole
44056629e8 Use local db instead of constant migrating. 2015-04-20 22:08:24 +02:00
James Cole
54125c05d3 New tests and prep for future runs. 2015-04-20 21:57:20 +02:00
James Cole
7d32e50f25 Mentioned wrong amount. 2015-04-20 20:56:28 +02:00
James Cole
4af9ff49a0 Fixed a bug in the report controller that grouped transactions wrong. 2015-04-20 20:54:55 +02:00
James Cole
41dad4091a Merge branch 'release/3.3.8' into develop 2015-04-20 18:07:02 +02:00
218 changed files with 6831 additions and 4176 deletions

18
.env.backup Executable file
View File

@@ -0,0 +1,18 @@
APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
EMAIL_SMTP=
EMAIL_DRIVER=smtp
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=

18
.env.local Executable file
View File

@@ -0,0 +1,18 @@
APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
EMAIL_SMTP=
EMAIL_DRIVER=smtp
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ tests/_output/*
clover.xml clover.xml
node_modules/ node_modules/
addNewLines.php addNewLines.php
.phpstorm.meta.php

View File

@@ -1,4 +1,4 @@
Firefly III (v3.3.8) Firefly III (v3.4)
=========== ===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii) [![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
@@ -22,7 +22,9 @@ laptop and [Firefly II](https://github.com/JC5/Firefly) is live.
If you're not sure if this tool is for you, please read the [full description](https://github.com/JC5/firefly-iii/wiki/full-description). If you're not sure if this tool is for you, please read the [full description](https://github.com/JC5/firefly-iii/wiki/full-description).
To install and use Firefly III, please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation), To install and use Firefly III, please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable) and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)** [the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable) and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/). This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current features ## Current features

View File

@@ -65,6 +65,22 @@ class ConnectJournalToPiggyBank
} }
Log::debug('Found rep! ' . $repetition->id); Log::debug('Found rep! ' . $repetition->id);
/*
* Add amount when
*/
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id == $piggyBank->account_id) {
if ($transaction->amount < 0) {
$amount = $amount * -1;
Log::debug('Transaction is away from piggy, so amount becomes ' . $amount);
} else {
Log::debug('Transaction is to from piggy, so amount stays ' . $amount);
}
}
}
$repetition->currentamount += $amount; $repetition->currentamount += $amount;
$repetition->save(); $repetition->save();

View File

@@ -230,7 +230,7 @@ class ReportQuery implements ReportQueryInterface
// any deposit is fine. // any deposit is fine.
$query->where('transaction_types.type', 'Deposit'); $query->where('transaction_types.type', 'Deposit');
} }
$query->groupBy('t_from.account_id')->orderBy('transaction_journals.date'); $query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date');
// get everything, decrypt and return // get everything, decrypt and return
$data = $query->get( $data = $query->get(

View File

@@ -11,6 +11,7 @@ use Input;
use Redirect; use Redirect;
use Session; use Session;
use Steam; use Steam;
use URL;
use View; use View;
/** /**
@@ -25,6 +26,7 @@ class AccountController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card'); View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts'); View::share('title', 'Accounts');
} }
@@ -39,6 +41,12 @@ class AccountController extends Controller
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$subTitle = 'Create a new ' . e($what) . ' account'; $subTitle = 'Create a new ' . e($what) . ' account';
// put previous url in session if not redirect from store (not "create another").
if (Session::get('accounts.create.fromStore') !== true) {
Session::put('accounts.create.url', URL::previous());
}
Session::forget('accounts.create.fromStore');
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle')); return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
} }
@@ -52,6 +60,9 @@ class AccountController extends Controller
{ {
$subTitle = 'Delete ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $subTitle = 'Delete ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
// put previous url in session
Session::put('accounts.delete.url', URL::previous());
return view('accounts.delete', compact('account', 'subTitle')); return view('accounts.delete', compact('account', 'subTitle'));
} }
@@ -71,7 +82,7 @@ class AccountController extends Controller
Session::flash('success', 'The ' . e($typeName) . ' account "' . e($name) . '" was deleted.'); Session::flash('success', 'The ' . e($typeName) . ' account "' . e($name) . '" was deleted.');
return Redirect::route('accounts.index', $typeName); return Redirect::to(Session::get('accounts.delete.url'));
} }
/** /**
@@ -88,6 +99,12 @@ class AccountController extends Controller
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$openingBalance = $repository->openingBalanceTransaction($account); $openingBalance = $repository->openingBalanceTransaction($account);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('accounts.edit.fromUpdate') !== true) {
Session::put('accounts.edit.url', URL::previous());
}
Session::forget('accounts.edit.fromUpdate');
// pre fill some useful values. // pre fill some useful values.
// the opening balance is tricky: // the opening balance is tricky:
@@ -184,10 +201,15 @@ class AccountController extends Controller
Session::flash('success', 'New account "' . $account->name . '" stored!'); Session::flash('success', 'New account "' . $account->name . '" stored!');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
return Redirect::route('accounts.create', $request->input('what'))->withInput(); // set value so create routine will not overwrite URL:
Session::put('accounts.create.fromStore', true);
return Redirect::route('accounts.create')->withInput();
} }
return Redirect::route('accounts.index', $request->input('what')); // redirect to previous URL.
return Redirect::to(Session::get('accounts.create.url'));
} }
@@ -200,7 +222,7 @@ class AccountController extends Controller
*/ */
public function update(Account $account, AccountFormRequest $request, AccountRepositoryInterface $repository) public function update(Account $account, AccountFormRequest $request, AccountRepositoryInterface $repository)
{ {
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$accountData = [ $accountData = [
'name' => $request->input('name'), 'name' => $request->input('name'),
'active' => $request->input('active'), 'active' => $request->input('active'),
@@ -219,10 +241,14 @@ class AccountController extends Controller
Session::flash('success', 'Account "' . $account->name . '" updated.'); Session::flash('success', 'Account "' . $account->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('accounts.edit', $account->id); // set value so edit routine will not overwrite URL:
Session::put('accounts.edit.fromUpdate', true);
return Redirect::route('accounts.edit', $account->id)->withInput(['return_to_edit' => 1]);
} }
return Redirect::route('accounts.index', $what); // redirect to previous URL.
return Redirect::to(Session::get('accounts.edit.url'));
} }

View File

@@ -8,6 +8,7 @@ use Illuminate\Http\Request;
use Illuminate\Mail\Message; use Illuminate\Mail\Message;
use Mail; use Mail;
use Session; use Session;
use Twig;
/** /**
* Class AuthController * Class AuthController
@@ -47,6 +48,16 @@ class AuthController extends Controller
$this->middleware('guest', ['except' => 'getLogout']); $this->middleware('guest', ['except' => 'getLogout']);
} }
/**
* Show the application login form.
*
* @return \Illuminate\Http\Response
*/
public function getLogin()
{
return Twig::render('auth.login');
}
/** /**
* Handle a registration request for the application. * Handle a registration request for the application.
* *

View File

@@ -29,6 +29,7 @@ class BillController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Bills'); View::share('title', 'Bills');
View::share('mainTitleIcon', 'fa-calendar-o'); View::share('mainTitleIcon', 'fa-calendar-o');
} }
@@ -83,6 +84,12 @@ class BillController extends Controller
{ {
$periods = Config::get('firefly.periods_to_text'); $periods = Config::get('firefly.periods_to_text');
// put previous url in session if not redirect from store (not "create another").
if (Session::get('bills.create.fromStore') !== true) {
Session::put('bills.create.url', URL::previous());
}
Session::forget('bills.create.fromStore');
return view('bills.create')->with('periods', $periods)->with('subTitle', 'Create new'); return view('bills.create')->with('periods', $periods)->with('subTitle', 'Create new');
} }
@@ -93,6 +100,9 @@ class BillController extends Controller
*/ */
public function delete(Bill $bill) public function delete(Bill $bill)
{ {
// put previous url in session
Session::put('bills.delete.url', URL::previous());
return view('bills.delete')->with('bill', $bill)->with('subTitle', 'Delete "' . e($bill->name) . '"'); return view('bills.delete')->with('bill', $bill)->with('subTitle', 'Delete "' . e($bill->name) . '"');
} }
@@ -107,7 +117,7 @@ class BillController extends Controller
Session::flash('success', 'The bill was deleted.'); Session::flash('success', 'The bill was deleted.');
return Redirect::route('bills.index'); return Redirect::to(Session::get('bills.delete.url'));
} }
@@ -120,6 +130,12 @@ class BillController extends Controller
{ {
$periods = Config::get('firefly.periods_to_text'); $periods = Config::get('firefly.periods_to_text');
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('bills.edit.fromUpdate') !== true) {
Session::put('bills.edit.url', URL::previous());
}
Session::forget('bills.edit.fromUpdate');
return view('bills.edit')->with('periods', $periods)->with('bill', $bill)->with('subTitle', 'Edit "' . e($bill->name) . '"'); return view('bills.edit')->with('periods', $periods)->with('bill', $bill)->with('subTitle', 'Edit "' . e($bill->name) . '"');
} }
@@ -190,10 +206,14 @@ class BillController extends Controller
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.'); Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('bills.create.fromStore', true);
return Redirect::route('bills.create')->withInput(); return Redirect::route('bills.create')->withInput();
} }
return Redirect::route('bills.index'); // redirect to previous URL.
return Redirect::to(Session::get('bills.create.url'));
} }
@@ -207,13 +227,17 @@ class BillController extends Controller
$billData = $request->getBillData(); $billData = $request->getBillData();
$bill = $repository->update($bill, $billData); $bill = $repository->update($bill, $billData);
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('bills.edit', $bill->id);
}
Session::flash('success', 'Bill "' . e($bill->name) . '" updated.'); Session::flash('success', 'Bill "' . e($bill->name) . '" updated.');
return Redirect::route('bills.index'); if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('bills.edit.fromUpdate', true);
return Redirect::route('bills.edit', $bill->id)->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return Redirect::to(Session::get('bills.edit.url'));
} }

View File

@@ -29,8 +29,10 @@ class BudgetController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Budgets'); View::share('title', 'Budgets');
View::share('mainTitleIcon', 'fa-tasks'); View::share('mainTitleIcon', 'fa-tasks');
View::share('hideBudgets', true);
} }
/** /**
@@ -185,12 +187,12 @@ class BudgetController extends Controller
return view('error')->with('message', 'Invalid selection.'); return view('error')->with('message', 'Invalid selection.');
} }
$hideBudget = true; // used in transaction list. $journals = $repository->getJournals($budget, $repetition);
$journals = $repository->getJournals($budget, $repetition); $limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $repository->getBudgetLimits($budget);
$limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $repository->getBudgetLimits($budget); $subTitle = !is_null($repetition->id) ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name);
$subTitle = !is_null($repetition->id) ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name); $journals->setPath('/budgets/show/' . $budget->id);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget')); return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
} }
/** /**

View File

@@ -26,6 +26,7 @@ class CategoryController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Categories'); View::share('title', 'Categories');
View::share('mainTitleIcon', 'fa-bar-chart'); View::share('mainTitleIcon', 'fa-bar-chart');
} }
@@ -51,7 +52,7 @@ class CategoryController extends Controller
*/ */
public function delete(Category $category) public function delete(Category $category)
{ {
$subTitle = 'Delete category' . e($category->name) . '"'; $subTitle = 'Delete category "' . e($category->name) . '"';
// put previous url in session // put previous url in session
Session::put('categories.delete.url', URL::previous()); Session::put('categories.delete.url', URL::previous());

View File

@@ -3,6 +3,7 @@
use Illuminate\Foundation\Bus\DispatchesCommands; use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
use View;
/** /**
* Class Controller * Class Controller
@@ -14,4 +15,14 @@ abstract class Controller extends BaseController
use DispatchesCommands, ValidatesRequests; use DispatchesCommands, ValidatesRequests;
/**
*
*/
public function __construct()
{
View::share('hideBudgets', false);
View::share('hideCategories', false);
View::share('hideBills', false);
View::share('hideTags', false);
}
} }

View File

@@ -27,7 +27,7 @@ class CurrencyController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Currencies'); View::share('title', 'Currencies');
View::share('mainTitleIcon', 'fa-usd'); View::share('mainTitleIcon', 'fa-usd');
} }

View File

@@ -144,8 +144,8 @@ class GoogleChartController extends Controller
public function allBudgetsHomeChart(GChart $chart, BudgetRepositoryInterface $repository) public function allBudgetsHomeChart(GChart $chart, BudgetRepositoryInterface $repository)
{ {
$chart->addColumn('Budget', 'string'); $chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number'); $chart->addColumn('Left', 'number');
$chart->addColumn('Spent', 'number'); //$chart->addColumn('Spent', 'number');
$budgets = $repository->getBudgets(); $budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
@@ -171,7 +171,8 @@ class GoogleChartController extends Controller
foreach ($allEntries as $entry) { foreach ($allEntries as $entry) {
if ($entry[2] > 0) { if ($entry[2] > 0) {
$chart->addRow($entry[0], $entry[1], $entry[2]); $left = $entry[1] - $entry[2];
$chart->addRow($entry[0], $left);
} }
} }
@@ -358,7 +359,7 @@ class GoogleChartController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function budgetsAndSpending(Budget $budget, $year = 0, GChart $chart, BudgetRepositoryInterface $repository) public function budgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, $year = 0)
{ {
$chart->addColumn('Month', 'date'); $chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number'); $chart->addColumn('Budgeted', 'number');

View File

@@ -1,12 +1,8 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Cache;
use ErrorException;
use FireflyIII\Helpers\Help\HelpInterface; use FireflyIII\Helpers\Help\HelpInterface;
use League\CommonMark\CommonMarkConverter;
use Log; use Log;
use Response; use Response;
use Route;
/** /**
* Class HelpController * Class HelpController

View File

@@ -86,7 +86,7 @@ class HomeController extends Controller
} }
} }
return view('index', compact('count', 'title', 'savings', 'subTitle', 'mainTitleIcon', 'transactions', 'savingsTotal','piggyBankAccounts')); return view('index', compact('count', 'title', 'savings', 'subTitle', 'mainTitleIcon', 'transactions', 'savingsTotal', 'piggyBankAccounts'));
} }

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Amount; use Amount;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportQueryInterface; use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -12,6 +11,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Preferences; use Preferences;
use Response; use Response;
@@ -180,6 +180,23 @@ class JsonController extends Controller
} }
/**
* Returns a JSON list of all beneficiaries.
*
* @return \Illuminate\Http\JsonResponse
*/
public function tags(TagRepositoryInterface $tagRepository)
{
$list = $tagRepository->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->tag;
}
return Response::json($return);
}
/** /**
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */

View File

@@ -1,12 +1,12 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Amount; use Amount;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config; use Config;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\PiggyBankFormRequest; use FireflyIII\Http\Requests\PiggyBankFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -32,6 +32,7 @@ class PiggyBankController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Piggy banks'); View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc'); View::share('mainTitleIcon', 'fa-sort-amount-asc');
} }
@@ -56,13 +57,13 @@ class PiggyBankController extends Controller
/** /**
* @return mixed * @return mixed
*/ */
public function create() public function create(AccountRepositoryInterface $repository)
{ {
$periods = Config::get('firefly.piggy_bank_periods'); $periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList( $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']) //Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
); // );
$subTitle = 'Create new piggy bank'; $subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus'; $subTitleIcon = 'fa-plus';
@@ -95,11 +96,12 @@ class PiggyBankController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(PiggyBank $piggyBank) public function destroy(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository)
{ {
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.'); Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$piggyBank->delete(); $repository->destroy($piggyBank);
return Redirect::to(Session::get('piggy-banks.delete.url')); return Redirect::to(Session::get('piggy-banks.delete.url'));
} }
@@ -111,13 +113,11 @@ class PiggyBankController extends Controller
* *
* @return $this * @return $this
*/ */
public function edit(PiggyBank $piggyBank) public function edit(PiggyBank $piggyBank, AccountRepositoryInterface $repository)
{ {
$periods = Config::get('firefly.piggy_bank_periods'); $periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList( $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
$subTitle = 'Edit piggy bank "' . e($piggyBank->name) . '"'; $subTitle = 'Edit piggy bank "' . e($piggyBank->name) . '"';
$subTitleIcon = 'fa-pencil'; $subTitleIcon = 'fa-pencil';
@@ -151,16 +151,16 @@ class PiggyBankController extends Controller
/** /**
* @return $this * @return $this
*/ */
public function index(AccountRepositoryInterface $repository) public function index(AccountRepositoryInterface $repository, PiggyBankRepositoryInterface $piggyRepository)
{ {
/** @var Collection $piggyBanks */ /** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get(); $piggyBanks = $piggyRepository->getPiggyBanks();
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) { foreach ($piggyBanks as $piggyBank) {
$piggyBank->savedSoFar = floatval($piggyBank->currentRelevantRep()->currentamount); $piggyBank->savedSoFar = floatval($piggyBank->currentRelevantRep()->currentamount);
$piggyBank->percentage = intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100); $piggyBank->percentage = $piggyBank->savedSoFar != 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
$piggyBank->leftToSave = $piggyBank->targetamount - $piggyBank->savedSoFar; $piggyBank->leftToSave = $piggyBank->targetamount - $piggyBank->savedSoFar;
/* /*
@@ -223,8 +223,8 @@ class PiggyBankController extends Controller
$repetition->currentamount += $amount; $repetition->currentamount += $amount;
$repetition->save(); $repetition->save();
// create event. // create event
PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount, 'piggy_bank_id' => $piggyBank->id]); $repository->createEvent($piggyBank, $amount);
/* /*
* Create event! * Create event!
@@ -244,7 +244,7 @@ class PiggyBankController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function postRemove(PiggyBank $piggyBank) public function postRemove(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository)
{ {
$amount = floatval(Input::get('amount')); $amount = floatval(Input::get('amount'));
@@ -255,12 +255,8 @@ class PiggyBankController extends Controller
$repetition->currentamount -= $amount; $repetition->currentamount -= $amount;
$repetition->save(); $repetition->save();
PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount * -1, 'piggy_bank_id' => $piggyBank->id]); // create event
$repository->createEvent($piggyBank, $amount * -1);
/*
* Create event!
*/
//Event::fire('piggy_bank.removeMoney', [$piggyBank, $amount]); // new and used.
Session::flash('success', 'Removed ' . Amount::format($amount, false) . ' from "' . e($piggyBank->name) . '".'); Session::flash('success', 'Removed ' . Amount::format($amount, false) . ' from "' . e($piggyBank->name) . '".');
} else { } else {
@@ -287,10 +283,9 @@ class PiggyBankController extends Controller
* *
* @return $this * @return $this
*/ */
public function show(PiggyBank $piggyBank) public function show(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository)
{ {
$events = $repository->getEvents($piggyBank);
$events = $piggyBank->piggyBankEvents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get();
/* /*
* Number of reminders: * Number of reminders:
@@ -310,6 +305,7 @@ class PiggyBankController extends Controller
*/ */
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository) public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{ {
$piggyBankData = [ $piggyBankData = [
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => new Carbon, 'startdate' => new Carbon,
@@ -354,7 +350,6 @@ class PiggyBankController extends Controller
'remind_me' => $request->get('remind_me') 'remind_me' => $request->get('remind_me')
]; ];
$piggyBank = $repository->update($piggyBank, $piggyBankData); $piggyBank = $repository->update($piggyBank, $piggyBankData);
Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".'); Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".');

View File

@@ -1,6 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
use Redirect; use Redirect;
@@ -20,7 +20,7 @@ class PreferencesController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Preferences'); View::share('title', 'Preferences');
View::share('mainTitleIcon', 'fa-gear'); View::share('mainTitleIcon', 'fa-gear');
} }
@@ -28,9 +28,9 @@ class PreferencesController extends Controller
/** /**
* @return $this|\Illuminate\View\View * @return $this|\Illuminate\View\View
*/ */
public function index() public function index(AccountRepositoryInterface $repository)
{ {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']); $accounts = $repository->getAccounts(['Default account', 'Asset account']);
$viewRange = Preferences::get('viewRange', '1M'); $viewRange = Preferences::get('viewRange', '1M');
$viewRangeValue = $viewRange->data; $viewRangeValue = $viewRange->data;
$frontPage = Preferences::get('frontPageAccounts', []); $frontPage = Preferences::get('frontPageAccounts', []);

View File

@@ -2,6 +2,7 @@
use Auth; use Auth;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest;
use Hash; use Hash;
use Redirect; use Redirect;
@@ -34,6 +35,37 @@ class ProfileController extends Controller
return view('profile.index')->with('title', 'Profile')->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user'); return view('profile.index')->with('title', 'Profile')->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user');
} }
/**
* @return \Illuminate\View\View
*/
public function deleteAccount()
{
return view('profile.delete-account')->with('title', Auth::user()->email)->with('subTitle', 'Delete account')->with(
'mainTitleIcon', 'fa-user'
);
}
/**
*
*/
public function postDeleteAccount(DeleteAccountFormRequest $request)
{
// old, new1, new2
if (!Hash::check($request->get('password'), Auth::user()->password)) {
Session::flash('error', 'Invalid password!');
return Redirect::route('delete-account');
}
// DELETE!
Auth::user()->delete();
Session::flush();
return Redirect::route('index');
}
/** /**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/ */

View File

@@ -1,164 +0,0 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Redirect;
use Response;
use URL;
/**
* Class RelatedController
*
* @package FireflyIII\Http\Controllers
*/
class RelatedController extends Controller
{
/**
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
*/
public function alreadyRelated(TransactionJournal $journal)
{
$ids = [];
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$ids[] = $loopJournal->id;
}
}
}
$unique = array_unique($ids);
$journals = new Collection;
if (count($unique) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $unique)->get();
}
$parent = $journal;
return view('related.alreadyRelated', compact('parent', 'journals'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function getRemoveRelation(TransactionJournal $parentJournal, TransactionJournal $childJournal)
{
$groups = $parentJournal->transactiongroups()->get();
/** @var TransactionGroup $group */
foreach ($groups as $group) {
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id == $childJournal->id) {
// remove from group:
$group->transactionjournals()->detach($childJournal);
}
}
if ($group->transactionjournals()->count() == 1) {
$group->delete();
}
}
return Redirect::to(URL::previous());
}
/**
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*
* @return \Illuminate\Http\JsonResponse
*/
public function relate(TransactionJournal $parentJournal, TransactionJournal $childJournal)
{
$group = new TransactionGroup;
$group->relation = 'balance';
$group->user_id = Auth::user()->id;
$group->save();
$group->transactionjournals()->save($parentJournal);
$group->transactionjournals()->save($childJournal);
return Response::json(true);
}
/**
* @param TransactionJournal $journal
*
* @return \Illuminate\View\View
*/
public function related(TransactionJournal $journal)
{
$groups = $journal->transactiongroups()->get();
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($groups as $group) {
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$members->push($loopJournal);
}
}
}
return view('related.relate', compact('journal', 'members'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function removeRelation(TransactionJournal $parentJournal, TransactionJournal $childJournal)
{
$groups = $parentJournal->transactiongroups()->get();
/** @var TransactionGroup $group */
foreach ($groups as $group) {
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id == $childJournal->id) {
// remove from group:
$group->transactionjournals()->detach($childJournal);
}
}
if ($group->transactionjournals()->count() == 1) {
$group->delete();
}
}
return Response::json(true);
}
/**
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
*/
public function search(TransactionJournal $journal, JournalRepositoryInterface $repository)
{
$search = e(trim(Input::get('searchValue')));
$parent = $journal;
$journals = $repository->searchRelated($search, $journal);
return view('related.searchResult', compact('journals', 'search', 'parent'));
}
}

View File

@@ -47,50 +47,51 @@ class ReportController extends Controller
*/ */
public function budget($year = '2014', $month = '1') public function budget($year = '2014', $month = '1')
{ {
try { $date = new Carbon($year . '-' . $month . '-01');
new Carbon($year . '-' . $month . '-01'); $subTitle = 'Budget report for ' . $date->format('F Y');
} catch (Exception $e) { $subTitleIcon = 'fa-calendar';
return view('error')->with('message', 'Invalid date'); $start = clone $date;
}
$date = new Carbon($year . '-' . $month . '-01');
$start = clone $date;
$start->startOfMonth(); $start->startOfMonth();
$end = clone $date; $end = clone $date;
$end->endOfMonth(); $end->endOfMonth();
$start->subDay();
// should show shared reports?
/** @var Preference $pref */ /** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false); $pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data; $showSharedReports = $pref->data;
$accountAmounts = []; // array with sums of spent amounts on each account.
$accounts = $this->query->getAllAccounts($start, $end, $showSharedReports); // all accounts and some data.
foreach ($accounts as $account) {
$dayEarly = clone $date; $budgets = $this->query->getBudgetSummary($account, $start, $end);// get budget summary for this account:
$subTitle = 'Budget report for ' . $date->format('F Y'); $balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$subTitleIcon = 'fa-calendar'; $accountAmounts[$account->id] = $balancedAmount;
$dayEarly = $dayEarly->subDay(); // balance out the transactions (see transaction groups & tags) ^^
$accounts = $this->query->getAllAccounts($start, $end, $showSharedReports);
$start->addDay();
$accounts->each( // array with budget information for each account:
function (Account $account) use ($start, $end) { $array = [];
$budgets = $this->query->getBudgetSummary($account, $start, $end); // should always hide account
$balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end); $hide = true;
$array = []; // loop all budgets
$hide = true; foreach ($budgets as $budget) {
foreach ($budgets as $budget) { $id = intval($budget->id);
$id = intval($budget->id); $data = $budget->toArray();
$data = $budget->toArray(); $array[$id] = $data;
$array[$id] = $data;
if (floatval($data['queryAmount']) != 0) { // no longer hide account if any budget has money in it.
$hide = false; if (floatval($data['queryAmount']) != 0) {
} $hide = false;
} }
$account->hide = $hide; $accountAmounts[$account->id] += $data['queryAmount'];
$account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount;
} }
); $account->hide = $hide;
$account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount;
}
/** /**
* Start getBudgetsForMonth DONE * Start getBudgetsForMonth DONE
@@ -101,7 +102,7 @@ class ReportController extends Controller
* End getBudgetsForMonth DONE * End getBudgetsForMonth DONE
*/ */
return view('reports.budget', compact('subTitle', 'year', 'month', 'subTitleIcon', 'date', 'accounts', 'budgets', 'dayEarly')); return view('reports.budget', compact('subTitle', 'accountAmounts', 'year', 'month', 'subTitleIcon', 'date', 'accounts', 'budgets'));
} }

View File

@@ -0,0 +1,314 @@
<?php
namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests\TagFormRequest;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Input;
use Preferences;
use Redirect;
use Response;
use Session;
use URL;
use View;
/**
* Class TagController
*
* Remember: a balancingAct takes at most one expense and one transfer.
* an advancePayment takes at most one expense, infinite deposits and NO transfers.
*
* TODO transaction can only have one advancePayment OR balancingAct.
* TODO Other attempts to put in such a tag are blocked.
* TODO also show an error when editing a tag and it becomes either
* TODO of these two types. Or rather, block editing of the tag.
*
* @package FireflyIII\Http\Controllers
*/
class TagController extends Controller
{
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('title', 'Tags');
View::share('mainTitleIcon', 'fa-tags');
View::share('hideTags', true);
$tagOptions = [
'nothing' => 'Just a regular tag.',
'balancingAct' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
'advancePayment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
];
View::share('tagOptions', $tagOptions);
}
/**
* @return \Illuminate\View\View
*/
public function create()
{
$subTitle = 'New tag';
$subTitleIcon = 'fa-tag';
$preFilled = [
'tagMode' => 'nothing'
];
if (!Input::old('tagMode')) {
Session::flash('preFilled', $preFilled);
}
// put previous url in session if not redirect from store (not "create another").
if (Session::get('tags.create.fromStore') !== true) {
Session::put('tags.create.url', URL::previous());
}
Session::forget('tags.create.fromStore');
return view('tags.create', compact('subTitle', 'subTitleIcon'));
}
/**
* @param Tag $tag
*
* @return \Illuminate\View\View
*/
public function delete(Tag $tag)
{
$subTitle = 'Delete "' . e($tag->tag) . '"';
// put previous url in session
Session::put('tags.delete.url', URL::previous());
return view('tags.delete', compact('tag', 'subTitle'));
}
/**
* @param Tag $tag
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Tag $tag, TagRepositoryInterface $repository)
{
$tagName = $tag->tag;
$repository->destroy($tag);
Session::flash('success', 'Tag "' . e($tagName) . '" was deleted.');
return Redirect::to(route('tags.index'));
}
/**
* @param Tag $tag
*
* @return View
*/
public function edit(Tag $tag)
{
$subTitle = 'Edit tag "' . e($tag->tag) . '"';
$subTitleIcon = 'fa-tag';
/*
* Default tag options (again)
*/
$tagOptions = [
'nothing' => 'Just a regular tag.',
'balancingAct' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
'advancePayment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
];
/*
* Can this tag become another type?
*/
$allowToAdvancePayment = true;
$allowToBalancingAct = true;
/*
* If this tag is a balancing act, and it contains transfers, it cannot be
* changes to an advancePayment.
*/
if ($tag->tagMode == 'balancingAct') {
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Transfer') {
$allowToAdvancePayment = false;
}
}
}
/*
* If this tag contains more than one expenses, it cannot become an advance payment.
*/
$count = 0;
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Withdrawal') {
$count++;
}
}
if($count > 1) {
$allowToAdvancePayment = false;
}
/*
* If has more than two transactions already, cannot become a balancing act:
*/
if ($tag->transactionjournals->count() > 2) {
$allowToBalancingAct = false;
}
/*
* If any transaction is a deposit, cannot become a balancing act.
*/
$count = 0;
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Deposit') {
$count++;
}
}
if($count > 0) {
$allowToBalancingAct = false;
}
// edit tagoptions:
if ($allowToAdvancePayment === false) {
unset($tagOptions['advancePayment']);
}
if ($allowToBalancingAct === false) {
unset($tagOptions['balancingAct']);
}
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('tags.edit.fromUpdate') !== true) {
Session::put('tags.edit.url', URL::previous());
}
Session::forget('tags.edit.fromUpdate');
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions'));
}
/**
* @param $state
*/
public function hideTagHelp($state)
{
$state = $state == 'true' ? true : false;
Preferences::set('hideTagHelp', $state);
return Response::json(true);
}
/**
*
*/
public function index()
{
/** @var Preference $helpHiddenPref */
$helpHiddenPref = Preferences::get('hideTagHelp', false);
$title = 'Tags';
$mainTitleIcon = 'fa-tags';
$helpHidden = $helpHiddenPref->data;
$tags = Auth::user()->tags()->get();
return view('tags.index', compact('title', 'mainTitleIcon', 'helpHidden', 'tags'));
}
/**
* @param Tag $tag
*
* @return \Illuminate\View\View
*/
public function show(Tag $tag)
{
$subTitle = $tag->tag;
$subTitleIcon = 'fa-tag';
return view('tags.show', compact('tag', 'subTitle', 'subTitleIcon'));
}
/**
* @param TagFormRequest $request
*/
public function store(TagFormRequest $request, TagRepositoryInterface $repository)
{
if (Input::get('setTag') == 'true') {
$latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null;
$longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null;
$zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null;
} else {
$latitude = null;
$longitude = null;
$zoomLevel = null;
}
$data = [
'tag' => $request->get('tag'),
'date' => strlen($request->get('date')) > 0 ? new Carbon($request->get('date')) : null,
'description' => strlen($request->get('description')) > 0 ? $request->get('description') : '',
'latitude' => $latitude,
'longitude' => $longitude,
'zoomLevel' => $zoomLevel,
'tagMode' => $request->get('tagMode'),
];
$repository->store($data);
Session::flash('success', 'The tag has been created!');
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('tags.create.fromStore', true);
return Redirect::route('tags.create')->withInput();
}
// redirect to previous URL.
return Redirect::to(Session::get('tags.create.url'));
}
/**
* @param Tag $tag
*/
public function update(Tag $tag, TagFormRequest $request, TagRepositoryInterface $repository)
{
if (Input::get('setTag') == 'true') {
$latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null;
$longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null;
$zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null;
} else {
$latitude = null;
$longitude = null;
$zoomLevel = null;
}
$data = [
'tag' => $request->get('tag'),
'date' => strlen($request->get('date')) > 0 ? new Carbon($request->get('date')) : null,
'description' => strlen($request->get('description')) > 0 ? $request->get('description') : '',
'latitude' => $latitude,
'longitude' => $longitude,
'zoomLevel' => $zoomLevel,
'tagMode' => $request->get('tagMode'),
];
$repository->update($tag, $data);
Session::flash('success', 'Tag "' . e($data['tag']) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('tags.edit.fromUpdate', true);
return Redirect::route('tags.edit', $tag->id)->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return Redirect::to(Session::get('tags.edit.url'));
}
}

View File

@@ -28,6 +28,7 @@ class TransactionController extends Controller
*/ */
public function __construct() public function __construct()
{ {
parent::__construct();
View::share('title', 'Transactions'); View::share('title', 'Transactions');
View::share('mainTitleIcon', 'fa-repeat'); View::share('mainTitleIcon', 'fa-repeat');
} }
@@ -134,6 +135,12 @@ class TransactionController extends Controller
'budget_id' => 0, 'budget_id' => 0,
'piggy_bank_id' => 0 'piggy_bank_id' => 0
]; ];
// get tags:
$tags = [];
foreach ($journal->tags as $tag) {
$tags[] = $tag->tag;
}
$preFilled['tags'] = join(',', $tags);
$category = $journal->categories()->first(); $category = $journal->categories()->first();
if (!is_null($category)) { if (!is_null($category)) {
@@ -150,12 +157,14 @@ class TransactionController extends Controller
} }
$preFilled['amount'] = $journal->amount; $preFilled['amount'] = $journal->amount;
$preFilled['account_id'] = $repository->getAssetAccount($journal); $preFilled['account_id'] = $journal->assetAccount->id;
$preFilled['expense_account'] = $transactions[0]->account->name; $preFilled['expense_account'] = $transactions[0]->account->name;
$preFilled['revenue_account'] = $transactions[1]->account->name; $preFilled['revenue_account'] = $transactions[1]->account->name;
$preFilled['account_from_id'] = $transactions[1]->account->id; $preFilled['account_from_id'] = $transactions[1]->account->id;
$preFilled['account_to_id'] = $transactions[0]->account->id; $preFilled['account_to_id'] = $transactions[0]->account->id;
Session::flash('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "return_to_edit"). // put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('transactions.edit.fromUpdate') !== true) { if (Session::get('transactions.edit.fromUpdate') !== true) {
Session::put('transactions.edit.url', URL::previous()); Session::put('transactions.edit.url', URL::previous());
@@ -271,6 +280,7 @@ class TransactionController extends Controller
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{ {
$journalData = $request->getJournalData(); $journalData = $request->getJournalData();
$journal = $repository->store($journalData); $journal = $repository->store($journalData);

View File

@@ -0,0 +1,32 @@
<?php
namespace FireflyIII\Http\Requests;
use Auth;
/**
* Class DeleteAccountFormRequest
*
* @package FireflyIII\Http\Requests
*/
class DeleteAccountFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
return [
'password' => 'required',
];
}
}

View File

@@ -42,6 +42,7 @@ class JournalFormRequest extends Request
'date' => new Carbon($this->get('date')), 'date' => new Carbon($this->get('date')),
'budget_id' => intval($this->get('budget_id')), 'budget_id' => intval($this->get('budget_id')),
'category' => $this->get('category'), 'category' => $this->get('category'),
'tags' => explode(',', $this->get('tags')),
]; ];
} }

View File

@@ -0,0 +1,53 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 27/04/15
* Time: 12:50
*/
namespace FireflyIII\Http\Requests;
use Auth;
use FireflyIII\Models\Tag;
use Input;
/**
* Class TagFormRequest
*
* @package FireflyIII\Http\Requests
*/
class TagFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$idRule = '';
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,TRUE';
if (Tag::find(Input::get('id'))) {
$idRule = 'belongsToUser:tags';
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,TRUE,' . Input::get('id');
}
return [
'tag' => $tagRule,
'id' => $idRule,
'description' => 'min:1',
'date' => 'date',
'latitude' => 'numeric|min:-90|max:90',
'longitude' => 'numeric|min:-90|max:90',
'tagMode' => 'required|in:nothing,balancingAct,advancePayment'
];
}
}

View File

@@ -9,8 +9,9 @@ use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Tag;
/* /*
* Back home. * Back home.
*/ */
@@ -22,6 +23,14 @@ Breadcrumbs::register(
} }
); );
Breadcrumbs::register(
'index',
function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index'));
}
);
// accounts // accounts
Breadcrumbs::register( Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) { 'accounts.index', function (Generator $breadcrumbs, $what) {
@@ -95,6 +104,13 @@ Breadcrumbs::register(
} }
); );
Breadcrumbs::register(
'budgets.noBudget', function (Generator $breadcrumbs, $subTitle) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push($subTitle, route('budgets.noBudget'));
}
);
Breadcrumbs::register( Breadcrumbs::register(
'budgets.show', function (Generator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) { 'budgets.show', function (Generator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) {
$breadcrumbs->parent('budgets.index'); $breadcrumbs->parent('budgets.index');
@@ -142,6 +158,34 @@ Breadcrumbs::register(
} }
); );
Breadcrumbs::register(
'categories.noCategory', function (Generator $breadcrumbs, $subTitle) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push($subTitle, route('categories.noCategory'));
}
);
// currencies.
Breadcrumbs::register(
'currency.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Currencies', route('currency.index'));
}
);
Breadcrumbs::register(
'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push('Edit '.$currency->name, route('currency.edit', $currency->id));
}
);
Breadcrumbs::register(
'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push('Delete '.$currency->name, route('currency.delete', $currency->id));
}
);
// piggy banks // piggy banks
Breadcrumbs::register( Breadcrumbs::register(
@@ -350,3 +394,24 @@ Breadcrumbs::register(
} }
); );
// tags
Breadcrumbs::register(
'tags.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Tags', route('tags.index'));
}
);
Breadcrumbs::register(
'tags.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('tags.index');
$breadcrumbs->push('Create tag', route('tags.create'));
}
);
Breadcrumbs::register(
'tags.show', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.index');
$breadcrumbs->push(e($tag->tag), route('tags.show', $tag));
}
);

View File

@@ -6,8 +6,10 @@ use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
// models // models
@@ -15,102 +17,113 @@ Route::bind(
'account', 'account',
function ($value, $route) { function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
$account = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') $object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.editable', 1) ->where('account_types.editable', 1)
->where('accounts.id', $value) ->where('accounts.id', $value)
->where('user_id', Auth::user()->id) ->where('user_id', Auth::user()->id)
->first(['accounts.*']); ->first(['accounts.*']);
if ($account) { if ($object) {
return $account; return $object;
} }
} }
App::abort(404); throw new NotFoundHttpException;
} }
); );
Route::bind(
'tjSecond', function ($value, $route) {
if (Auth::check()) {
return TransactionJournal::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind( Route::bind(
'tj', function ($value, $route) { 'tj', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return TransactionJournal:: $object = TransactionJournal::where('id', $value)->where('user_id', Auth::user()->id)->first();
where('id', $value)->where('user_id', Auth::user()->id)->first(); if ($object) {
return $object;
}
} }
return null; throw new NotFoundHttpException;
} }
); );
Route::bind( Route::bind(
'currency', function ($value, $route) { 'currency', function ($value, $route) {
return TransactionCurrency::find($value); if (Auth::check()) {
$object = TransactionCurrency::find($value);
if ($object) {
return $object;
}
}
throw new NotFoundHttpException;
} }
); );
Route::bind( Route::bind(
'bill', function ($value, $route) { 'bill', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return Bill::where('id', $value)->where('user_id', Auth::user()->id)->first(); $object = Bill::where('id', $value)->where('user_id', Auth::user()->id)->first();
if ($object) {
return $object;
}
} }
return null; throw new NotFoundHttpException;
} }
); );
Route::bind( Route::bind(
'budget', function ($value, $route) { 'budget', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return Budget::where('id', $value)->where('user_id', Auth::user()->id)->first(); $object = Budget::where('id', $value)->where('user_id', Auth::user()->id)->first();
if ($object) {
return $object;
}
} }
return null; throw new NotFoundHttpException;
} }
); );
Route::bind( Route::bind(
'reminder', function ($value, $route) { 'reminder', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return Reminder::where('id', $value)->where('user_id', Auth::user()->id)->first(); $object = Reminder::where('id', $value)->where('user_id', Auth::user()->id)->first();
if ($object) {
return $object;
}
} }
return null; throw new NotFoundHttpException;
} }
); );
Route::bind( Route::bind(
'limitrepetition', function ($value, $route) { 'limitrepetition', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return LimitRepetition::where('limit_repetitions.id', $value) $object = LimitRepetition::where('limit_repetitions.id', $value)
->leftjoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id') ->leftjoin('budget_limits', 'budget_limits.id', '=', 'limit_repetitions.budget_limit_id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('budgets.user_id', Auth::user()->id) ->where('budgets.user_id', Auth::user()->id)
->first(['limit_repetitions.*']); ->first(['limit_repetitions.*']);
if ($object) {
return $object;
}
} }
return null; throw new NotFoundHttpException;
} }
); );
Route::bind( Route::bind(
'piggyBank', function ($value, $route) { 'piggyBank', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return PiggyBank:: $object = PiggyBank::where('piggy_banks.id', $value)
where('piggy_banks.id', $value) ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') ->where('accounts.user_id', Auth::user()->id)
->where('accounts.user_id', Auth::user()->id) ->first(['piggy_banks.*']);
->first(['piggy_banks.*']); if ($object) {
return $object;
}
} }
return null; throw new NotFoundHttpException;
} }
); );
@@ -118,9 +131,25 @@ Route::bind(
'category', function ($value, $route) { 'category', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
return Category::where('id', $value)->where('user_id', Auth::user()->id)->first(); return Category::where('id', $value)->where('user_id', Auth::user()->id)->first();
if ($object) {
$object = $object;
}
} }
return null; throw new NotFoundHttpException;
}
);
Route::bind(
'tag', function ($value, $route) {
if (Auth::check()) {
return Tag::where('id', $value)->where('user_id', Auth::user()->id)->first();
if ($object) {
$object = $object;
}
}
throw new NotFoundHttpException;
} }
); );
@@ -245,6 +274,7 @@ Route::group(
Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']); Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']);
Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']); Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']);
Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']); Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']);
Route::get('/json/tags', ['uses' => 'JsonController@tags', 'as' => 'json.tags']);
Route::get('/json/box/in', ['uses' => 'JsonController@boxIn', 'as' => 'json.box.in']); Route::get('/json/box/in', ['uses' => 'JsonController@boxIn', 'as' => 'json.box.in']);
Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']); Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']);
Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']); Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']);
@@ -282,18 +312,10 @@ Route::group(
*/ */
Route::get('/profile', ['uses' => 'ProfileController@index', 'as' => 'profile']); Route::get('/profile', ['uses' => 'ProfileController@index', 'as' => 'profile']);
Route::get('/profile/change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']); Route::get('/profile/change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
Route::get('/profile/delete-account', ['uses' => 'ProfileController@deleteAccount', 'as' => 'delete-account']);
Route::post('/profile/delete-account', ['uses' => 'ProfileController@postDeleteAccount', 'as' => 'delete-account-post']);
Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword', 'as' => 'change-password-post']); Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword', 'as' => 'change-password-post']);
/**
* Related transactions controller
*/
Route::get('/related/alreadyRelated/{tj}', ['uses' => 'RelatedController@alreadyRelated', 'as' => 'related.alreadyRelated']);
Route::post('/related/relate/{tj}/{tjSecond}', ['uses' => 'RelatedController@relate', 'as' => 'related.relate']);
Route::post('/related/removeRelation/{tj}/{tjSecond}', ['uses' => 'RelatedController@removeRelation', 'as' => 'related.removeRelation']);
Route::get('/related/remove/{tj}/{tjSecond}', ['uses' => 'RelatedController@getRemoveRelation', 'as' => 'related.getRemoveRelation']);
Route::get('/related/related/{tj}', ['uses' => 'RelatedController@related', 'as' => 'related.related']);
Route::post('/related/search/{tj}', ['uses' => 'RelatedController@search', 'as' => 'related.search']);
/** /**
* Reminder Controller * Reminder Controller
*/ */
@@ -325,6 +347,22 @@ Route::group(
*/ */
Route::get('/search', ['uses' => 'SearchController@index', 'as' => 'search']); Route::get('/search', ['uses' => 'SearchController@index', 'as' => 'search']);
/**
* Tag Controller
*/
Route::get('/tags', ['uses' => 'TagController@index', 'as' => 'tags.index']);
Route::get('/tags/create', ['uses' => 'TagController@create', 'as' => 'tags.create']);
Route::get('/tags/show/{tag}', ['uses' => 'TagController@show', 'as' => 'tags.show']);
Route::get('/tags/edit/{tag}', ['uses' => 'TagController@edit', 'as' => 'tags.edit']);
Route::get('/tags/delete/{tag}', ['uses' => 'TagController@delete', 'as' => 'tags.delete']);
Route::post('/tags/store', ['uses' => 'TagController@store', 'as' => 'tags.store']);
Route::post('/tags/update/{tag}', ['uses' => 'TagController@update', 'as' => 'tags.update']);
Route::post('/tags/destroy/{tag}', ['uses' => 'TagController@destroy', 'as' => 'tags.destroy']);
Route::post('/tags/hideTagHelp/{state}', ['uses' => 'TagController@hideTagHelp', 'as' => 'tags.hideTagHelp']);
/** /**
* Transaction Controller * Transaction Controller
*/ */

View File

@@ -14,7 +14,7 @@ class PiggyBank extends Model
use SoftDeletes; use SoftDeletes;
protected $fillable protected $fillable
= ['name', 'account_id', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me']; = ['name', 'account_id','order', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me'];
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo

128
app/Models/Tag.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
/**
* Class Tag
*
* @package FireflyIII\Models
*/
class Tag extends Model
{
use ValidatingTrait;
protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode'];
protected $rules
= [
'tag' => 'required|min:1|uniqueObjectForUser:tags,tag,TRUE',
'description' => 'min:1',
'date' => 'date',
'latitude' => 'numeric|min:-90|max:90',
'longitude' => 'numeric|min:-90|max:90',
'tagMode' => 'required|in:nothing,balancingAct,advancePayment'
];
/**
* @param array $fields
*
* @return Tag|null
*/
public static function firstOrCreateEncrypted(array $fields)
{
// everything but the tag:
if (isset($fields['tagMode'])) {
unset($fields['tagMode']);
}
$query = Tag::orderBy('id');
foreach ($fields as $name => $value) {
if ($name != 'tag') {
$query->where($name, $value);
}
}
$set = $query->get(['tags.*']);
/** @var Tag $tag */
foreach ($set as $tag) {
if ($tag->tag == $fields['tag']) {
return $tag;
}
}
// create it!
$fields['tagMode'] = 'nothing';
$fields['description'] = isset($fields['description']) && !is_null($fields['description']) ? $fields['description'] : '';
$tag = Tag::create($fields);
if (is_null($tag->id)) {
// could not create account:
App::abort(500, 'Could not create new tag with data: ' . json_encode($fields) . ' because ' . json_encode($tag->getErrors()));
}
return $tag;
}
/**
* @return array
*/
public function getDates()
{
return ['created_at', 'updated_at', 'date'];
}
/**
* @param $value
*
* @return string
*/
public function getDescriptionAttribute($value)
{
return Crypt::decrypt($value);
}
/**
* @param $value
*
* @return string
*/
public function getTagAttribute($value)
{
return Crypt::decrypt($value);
}
/**
* @param $value
*/
public function setDescriptionAttribute($value)
{
$this->attributes['description'] = Crypt::encrypt($value);
}
/**
* @param $value
*/
public function setTagAttribute($value)
{
$this->attributes['tag'] = Crypt::encrypt($value);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function transactionjournals()
{
return $this->belongsToMany('FireflyIII\Models\TransactionJournal');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@@ -68,6 +68,34 @@ class TransactionJournal extends Model
} }
} }
public function getAssetAccountAttribute()
{
$positive = true; // the asset account is in the transaction with the positive amount.
if ($this->transactionType->type === 'Withdrawal') {
$positive = false;
}
/** @var Transaction $transaction */
foreach ($this->transactions()->get() as $transaction) {
if (floatval($transaction->amount) > 0 && $positive === true) {
return $transaction->account;
}
if (floatval($transaction->amount) < 0 && $positive === false) {
return $transaction->account;
}
}
return $this->transactions()->first();
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactions()
{
return $this->hasMany('FireflyIII\Models\Transaction');
}
/** /**
* @return array * @return array
*/ */
@@ -201,6 +229,14 @@ class TransactionJournal extends Model
$this->attributes['encrypted'] = true; $this->attributes['encrypted'] = true;
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/ */
@@ -225,14 +261,6 @@ class TransactionJournal extends Model
return $this->belongsToMany('FireflyIII\Models\TransactionGroup'); return $this->belongsToMany('FireflyIII\Models\TransactionGroup');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactions()
{
return $this->hasMany('FireflyIII\Models\Transaction');
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/ */

View File

@@ -35,6 +35,7 @@ class AppServiceProvider extends ServiceProvider
'Illuminate\Contracts\Auth\Registrar', 'Illuminate\Contracts\Auth\Registrar',
'FireflyIII\Services\Registrar' 'FireflyIII\Services\Registrar'
); );
} }
} }

View File

@@ -23,7 +23,227 @@ class ConfigServiceProvider extends ServiceProvider
{ {
config( config(
[ [
// 'twigbridge' => [
'twig' => [
/*
|--------------------------------------------------------------------------
| Extension
|--------------------------------------------------------------------------
|
| File extension for Twig view files.
|
*/
'extension' => 'twig',
/*
|--------------------------------------------------------------------------
| Accepts all Twig environment configuration options
|--------------------------------------------------------------------------
|
| http://twig.sensiolabs.org/doc/api.html#environment-options
|
*/
'environment' => [
// When set to true, the generated templates have a __toString() method
// that you can use to display the generated nodes.
// default: false
'debug' => config('app.debug', false),
// The charset used by the templates.
// default: utf-8
'charset' => 'utf-8',
// The base template class to use for generated templates.
// default: TwigBridge\Twig\Template
'base_template_class' => 'TwigBridge\Twig\Template',
// An absolute path where to store the compiled templates, or false to disable caching. If null
// then the cache file path is used.
// default: cache file storage path
'cache' => null,
// When developing with Twig, it's useful to recompile the template
// whenever the source code changes. If you don't provide a value
// for the auto_reload option, it will be determined automatically based on the debug value.
'auto_reload' => true,
// If set to false, Twig will silently ignore invalid variables
// (variables and or attributes/methods that do not exist) and
// replace them with a null value. When set to true, Twig throws an exception instead.
// default: false
'strict_variables' => false,
// If set to true, auto-escaping will be enabled by default for all templates.
// default: true
'autoescape' => true,
// A flag that indicates which optimizations to apply
// (default to -1 -- all optimizations are enabled; set it to 0 to disable)
'optimizations' => -1,
],
/*
|--------------------------------------------------------------------------
| Global variables
|--------------------------------------------------------------------------
|
| These will always be passed in and can be accessed as Twig variables.
| NOTE: these will be overwritten if you pass data into the view with the same key.
|
*/
'globals' => [],
],
'extensions' => [
/*
|--------------------------------------------------------------------------
| Extensions
|--------------------------------------------------------------------------
|
| Enabled extensions.
|
| `Twig_Extension_Debug` is enabled automatically if twig.debug is TRUE.
|
*/
'enabled' => [
'TwigBridge\Extension\Loader\Facades',
'TwigBridge\Extension\Loader\Filters',
'TwigBridge\Extension\Loader\Functions',
'TwigBridge\Extension\Laravel\Auth',
'TwigBridge\Extension\Laravel\Config',
'TwigBridge\Extension\Laravel\Dump',
'TwigBridge\Extension\Laravel\Input',
'TwigBridge\Extension\Laravel\Session',
'TwigBridge\Extension\Laravel\String',
'TwigBridge\Extension\Laravel\Translator',
'TwigBridge\Extension\Laravel\Url',
// 'TwigBridge\Extension\Laravel\Form',
// 'TwigBridge\Extension\Laravel\Html',
// 'TwigBridge\Extension\Laravel\Legacy\Facades',
],
/*
|--------------------------------------------------------------------------
| Facades
|--------------------------------------------------------------------------
|
| Available facades. Access like `{{ Config.get('foo.bar') }}`.
|
| Each facade can take an optional array of options. To mark the whole facade
| as safe you can set the option `'is_safe' => true`. Setting the facade as
| safe means that any HTML returned will not be escaped.
|
| It is advisable to not set the whole facade as safe and instead mark the
| each appropriate method as safe for security reasons. You can do that with
| the following syntax:
|
| <code>
| 'Form' => [
| 'is_safe' => [
| 'open'
| ]
| ]
| </code>
|
| The values of the `is_safe` array must match the called method on the facade
| in order to be marked as safe.
|
*/
'facades' => [
'Breadcrumbs' => [
'is_safe' => [
'renderIfExists'
]
],
'Session',
'Route',
'Auth',
'URL',
'Config',
'ExpandedForm' => [
'is_safe' => [
'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location',
'multiRadio'
]
],
'Form' => [
'is_safe' => [
'input', 'select', 'checkbox', 'model', 'open','radio','textarea'
]
],
],
/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| Available functions. Access like `{{ secure_url(...) }}`.
|
| Each function can take an optional array of options. These options are
| passed directly to `Twig_SimpleFunction`.
|
| So for example, to mark a function as safe you can do the following:
|
| <code>
| 'link_to' => [
| 'is_safe' => ['html']
| ]
| </code>
|
| The options array also takes a `callback` that allows you to name the
| function differently in your Twig templates than what it's actually called.
|
| <code>
| 'link' => [
| 'callback' => 'link_to'
| ]
| </code>
|
*/
'functions' => [
'elixir',
'head',
'last',
'old'
],
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Available filters. Access like `{{ variable|filter }}`.
|
| Each filter can take an optional array of options. These options are
| passed directly to `Twig_SimpleFilter`.
|
| So for example, to mark a filter as safe you can do the following:
|
| <code>
| 'studly_case' => [
| 'is_safe' => ['html']
| ]
| </code>
|
| The options array also takes a `callback` that allows you to name the
| filter differently in your Twig templates than what is actually called.
|
| <code>
| 'snake' => [
| 'callback' => 'snake_case'
| ]
| </code>
|
*/
'filters' => [],
],
]
] ]
); );
} }

View File

@@ -2,13 +2,21 @@
namespace FireflyIII\Providers; namespace FireflyIII\Providers;
use App;
use FireflyIII\Models\Account;
use FireflyIII\Support\Amount; use FireflyIII\Support\Amount;
use FireflyIII\Support\ExpandedForm; use FireflyIII\Support\ExpandedForm;
use FireflyIII\Support\Navigation; use FireflyIII\Support\Navigation;
use FireflyIII\Support\Preferences; use FireflyIII\Support\Preferences;
use FireflyIII\Support\Steam; use FireflyIII\Support\Steam;
use FireflyIII\Support\Twig\Budget;
use FireflyIII\Support\Twig\General;
use FireflyIII\Support\Twig\Journal;
use FireflyIII\Support\Twig\PiggyBank;
use FireflyIII\Validation\FireflyValidator; use FireflyIII\Validation\FireflyValidator;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Twig;
use TwigBridge\Extension\Loader\Functions;
use Validator; use Validator;
/** /**
@@ -25,10 +33,22 @@ class FireflyServiceProvider extends ServiceProvider
return new FireflyValidator($translator, $data, $rules, $messages); return new FireflyValidator($translator, $data, $rules, $messages);
} }
); );
/*
* Default Twig configuration:
*/
$config = App::make('config');
Twig::addExtension(new Functions($config));
Twig::addExtension(new PiggyBank);
Twig::addExtension(new General);
Twig::addExtension(new Journal);
Twig::addExtension(new Budget);
} }
public function register() public function register()
{ {
$this->app->bind( $this->app->bind(
'preferences', function () { 'preferences', function () {
return new Preferences; return new Preferences;
@@ -63,6 +83,7 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Repositories\Bill\BillRepositoryInterface', 'FireflyIII\Repositories\Bill\BillRepository'); $this->app->bind('FireflyIII\Repositories\Bill\BillRepositoryInterface', 'FireflyIII\Repositories\Bill\BillRepository');
$this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository'); $this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository'); $this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
$this->app->bind('FireflyIII\Repositories\Tag\TagRepositoryInterface', 'FireflyIII\Repositories\Tag\TagRepository');
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search'); $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
@@ -71,6 +92,7 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper'); $this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery'); $this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery');
} }
} }

View File

@@ -4,10 +4,12 @@ namespace FireflyIII\Repositories\Journal;
use App; use App;
use Auth; use Auth;
use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
@@ -32,36 +34,6 @@ class JournalRepository implements JournalRepositoryInterface
return Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']); return Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
} }
/**
*
* Get the account_id, which is the asset account that paid for the transaction.
*
* @param TransactionJournal $journal
*
* @return mixed
*/
public function getAssetAccount(TransactionJournal $journal)
{
$positive = true; // the asset account is in the transaction with the positive amount.
switch ($journal->transactionType->type) {
case 'Withdrawal':
$positive = false;
break;
}
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if (floatval($transaction->amount) > 0 && $positive === true) {
return $transaction->account_id;
}
if (floatval($transaction->amount) < 0 && $positive === false) {
return $transaction->account_id;
}
}
return $journal->transactions()->first()->account_id;
}
/** /**
* @param TransactionType $dbType * @param TransactionType $dbType
* *
@@ -83,55 +55,26 @@ class JournalRepository implements JournalRepositoryInterface
} }
/** /**
* @param string $query
* @param TransactionJournal $journal
* *
* @return Collection * * Remember: a balancingAct takes at most one expense and one transfer.
* an advancePayment takes at most one expense, infinite deposits and NO transfers.
*
* @param TransactionJournal $journal
* @param array $array
*
* @return void
*/ */
public function searchRelated($query, TransactionJournal $journal) public function saveTags(TransactionJournal $journal, array $array)
{ {
$start = clone $journal->date; /** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */
$end = clone $journal->date; $tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
$start->startOfMonth();
$end->endOfMonth();
// get already related transactions: foreach ($array as $name) {
$exclude = [$journal->id]; if (strlen(trim($name)) > 0) {
foreach ($journal->transactiongroups()->get() as $group) { $tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]);
foreach ($group->transactionjournals()->get() as $current) { $tagRepository->connect($journal, $tag);
$exclude[] = $current->id;
} }
} }
$exclude = array_unique($exclude);
/** @var Collection $collection */
$collection = Auth::user()->transactionjournals()
->withRelevantData()
->before($end)->after($start)->where('encrypted', 0)
->whereNotIn('id', $exclude)
->where('description', 'LIKE', '%' . $query . '%')
->get();
// manually search encrypted entries:
/** @var Collection $encryptedCollection */
$encryptedCollection = Auth::user()->transactionjournals()
->withRelevantData()
->before($end)->after($start)
->where('encrypted', 1)
->whereNotIn('id', $exclude)
->get();
$encrypted = $encryptedCollection->filter(
function (TransactionJournal $journal) use ($query) {
$strPos = strpos(strtolower($journal->description), strtolower($query));
if ($strPos !== false) {
return $journal;
}
return null;
}
);
return $collection->merge($encrypted);
} }
/** /**
@@ -191,6 +134,11 @@ class JournalRepository implements JournalRepositoryInterface
$journal->completed = 1; $journal->completed = 1;
$journal->save(); $journal->save();
// store tags
if (isset($data['tags']) && is_array($data['tags'])) {
$this->saveTags($journal, $data['tags']);
}
return $journal; return $journal;
@@ -246,9 +194,54 @@ class JournalRepository implements JournalRepositoryInterface
$journal->save(); $journal->save();
// update tags:
if (isset($data['tags']) && is_array($data['tags'])) {
$this->updateTags($journal, $data['tags']);
}
return $journal; return $journal;
} }
/**
* @param TransactionJournal $journal
* @param array $array
*
* @return void
*/
public function updateTags(TransactionJournal $journal, array $array)
{
// create tag repository
/** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */
$tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
// find or create all tags:
$tags = [];
$ids = [];
foreach ($array as $name) {
if (strlen(trim($name)) > 0) {
$tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]);
$tags[] = $tag;
$ids[] = $tag->id;
}
}
// delete all tags connected to journal not in this array:
if (count($ids) > 0) {
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete();
}
// if count is zero, delete them all:
if(count($ids) == 0) {
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->delete();
}
// connect each tag to journal (if not yet connected):
/** @var Tag $tag */
foreach ($tags as $tag) {
$tagRepository->connect($journal, $tag);
}
}
/** /**
* @param TransactionType $type * @param TransactionType $type
* @param array $data * @param array $data

View File

@@ -21,16 +21,6 @@ interface JournalRepositoryInterface
*/ */
public function first(); public function first();
/**
*
* Get the account_id, which is the asset account that paid for the transaction.
*
* @param TransactionJournal $journal
*
* @return int
*/
public function getAssetAccount(TransactionJournal $journal);
/** /**
* @param TransactionType $dbType * @param TransactionType $dbType
* *
@@ -38,21 +28,28 @@ interface JournalRepositoryInterface
*/ */
public function getJournalsOfType(TransactionType $dbType); public function getJournalsOfType(TransactionType $dbType);
/**
* @param string $query
* @param TransactionJournal $journal
*
* @return Collection
*/
public function searchRelated($query, TransactionJournal $journal);
/** /**
* @param $type * @param $type
* *
* @return TransactionType * @return TransactionType
*/ */
public function getTransactionType($type); public function getTransactionType($type);
/**
* @param TransactionJournal $journal
* @param array $array
*
* @return void
/**
*
* @param TransactionJournal $journal
* @param array $array
*
* @return void
*/
public function saveTags(TransactionJournal $journal, array $array);
/** /**
* @param array $data * @param array $data
* *
@@ -67,4 +64,12 @@ interface JournalRepositoryInterface
* @return mixed * @return mixed
*/ */
public function update(TransactionJournal $journal, array $data); public function update(TransactionJournal $journal, array $data);
/**
* @param TransactionJournal $journal
* @param array $array
*
* @return mixed
*/
public function updateTags(TransactionJournal $journal, array $array);
} }

View File

@@ -3,8 +3,10 @@
namespace FireflyIII\Repositories\PiggyBank; namespace FireflyIII\Repositories\PiggyBank;
use Auth; use Auth;
use Carbon\Carbon;
use DB; use DB;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
@@ -69,6 +71,19 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $bars; return $bars;
} }
/**
* @param PiggyBank $piggyBank
* @param $amount
*
* @return bool
*/
public function createEvent(PiggyBank $piggyBank, $amount)
{
PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount, 'piggy_bank_id' => $piggyBank->id]);
return true;
}
/** /**
* @param array $data * @param array $data
* *
@@ -87,6 +102,16 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $part; return $part;
} }
/**
* @param PiggyBank $piggyBank
*
* @return bool
*/
public function destroy(PiggyBank $piggyBank)
{
return $piggyBank->delete();
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
@@ -97,7 +122,26 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]); return DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
} }
/**
* @param PiggyBank $piggyBank
*
* @return Collection
*/
public function getEvents(PiggyBank $piggyBank)
{
return $piggyBank->piggyBankEvents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get();
}
/**
* @return Collection
*/
public function getPiggyBanks()
{
/** @var Collection $set */
$set = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get();
return $set;
}
/** /**
* Set all piggy banks to order 0. * Set all piggy banks to order 0.

View File

@@ -27,6 +27,18 @@ interface PiggyBankRepositoryInterface
*/ */
public function calculateParts(PiggyBankRepetition $repetition); public function calculateParts(PiggyBankRepetition $repetition);
/**
* @return Collection
*/
public function getPiggyBanks();
/**
* @param PiggyBank $piggyBank
*
* @return Collection
*/
public function getEvents(PiggyBank $piggyBank);
/** /**
* @param array $data * @param array $data
* *
@@ -34,6 +46,14 @@ interface PiggyBankRepositoryInterface
*/ */
public function createPiggyBankPart(array $data); public function createPiggyBankPart(array $data);
/**
* @param PiggyBank $piggyBank
* @param $amount
*
* @return bool
*/
public function createEvent(PiggyBank $piggyBank, $amount);
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
@@ -41,6 +61,13 @@ interface PiggyBankRepositoryInterface
*/ */
public function getEventSummarySet(PiggyBank $piggyBank); public function getEventSummarySet(PiggyBank $piggyBank);
/**
* @param PiggyBank $piggyBank
*
* @return bool
*/
public function destroy(PiggyBank $piggyBank);
/** /**
* Set all piggy banks to order 0. * Set all piggy banks to order 0.
* *

View File

@@ -0,0 +1,180 @@
<?php
namespace FireflyIII\Repositories\Tag;
use Auth;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
/**
* Class TagRepository
*
* @package FireflyIII\Repositories\Tag
*/
class TagRepository implements TagRepositoryInterface
{
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
public function connect(TransactionJournal $journal, Tag $tag)
{
/*
* Already connected:
*/
if ($journal->tags()->find($tag->id)) {
return false;
}
if ($tag->tagMode == 'nothing') {
// save it, no problem:
$journal->tags()->save($tag);
return true;
}
/*
* get some withdrawal types:
*/
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
if ($tag->tagMode == 'balancingAct') {
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag);
return true;
}
// and only if this is the only transfer
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) {
$journal->tags()->save($tag);
return true;
}
// ignore expense
return false;
}
if ($tag->tagMode == 'advancePayment') {
// only if this is the only withdrawal
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag);
return true;
}
// only if this is a deposit.
if ($journal->transaction_type_id == $deposit->id) {
// if this is a deposit, account must match the current only journal
// (if already present):
$currentWithdrawal = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->first();
if ($currentWithdrawal && $currentWithdrawal->assetAccount->id == $journal->assetAccount->id) {
$journal->tags()->save($tag);
return true;
} else {
if (is_null($currentWithdrawal)) {
$journal->tags()->save($tag);
return true;
}
}
}
return false;
}
return false;
}
/**
* @param Tag $tag
*
* @return boolean
*/
public function destroy(Tag $tag)
{
$tag->delete();
return true;
}
/**
* @return Collection
*/
public function get()
{
/** @var Collection $tags */
$tags = Auth::user()->tags()->get();
$tags->sortBy(
function (Tag $tag) {
return $tag->tag;
}
);
return $tags;
}
/**
* @param array $data
*
* @return Tag
*/
public function store(array $data)
{
$tag = new Tag;
$tag->tag = $data['tag'];
$tag->date = $data['date'];
$tag->description = $data['description'];
$tag->latitude = $data['latitude'];
$tag->longitude = $data['longitude'];
$tag->zoomLevel = $data['zoomLevel'];
$tag->tagMode = $data['tagMode'];
$tag->user()->associate(Auth::user());
$tag->save();
return $tag;
}
/**
* @param Tag $tag
* @param array $data
*
* @return Tag
*/
public function update(Tag $tag, array $data)
{
$tag->tag = $data['tag'];
$tag->date = $data['date'];
$tag->description = $data['description'];
$tag->latitude = $data['latitude'];
$tag->longitude = $data['longitude'];
$tag->zoomLevel = $data['zoomLevel'];
$tag->tagMode = $data['tagMode'];
$tag->save();
return $tag;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace FireflyIII\Repositories\Tag;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Interface TagRepositoryInterface
*
* @package FireflyIII\Repositories\Tag
*/
interface TagRepositoryInterface {
/**
* @param array $data
*
* @return Tag
*/
public function store(array $data);
/**
* @return Collection
*/
public function get();
/**
* @param Tag $tag
* @param array $data
*
* @return Tag
*/
public function update(Tag $tag, array $data);
/**
* @param Tag $tag
*
* @return boolean
*/
public function destroy(Tag $tag);
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
public function connect(TransactionJournal $journal, Tag $tag);
}

View File

@@ -160,12 +160,15 @@ class Amount
$currencyPreference = Prefs::get('currencyPreference', 'EUR'); $currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
if ($currency) {
\Cache::forever('FFCURRENCYCODE', $currency->code); Cache::forever('FFCURRENCYCODE', $currency->code);
define('FFCURRENCYCODE', $currency->code);
define('FFCURRENCYCODE', $currency->code); return $currency->code;
}
return $currency->code; return 'EUR';
} }
public function getDefaultCurrency() public function getDefaultCurrency()

View File

@@ -62,7 +62,10 @@ class ExpandedForm
'account_id' => 'Asset account', 'account_id' => 'Asset account',
'budget_id' => 'Budget', 'budget_id' => 'Budget',
'openingBalance' => 'Opening balance', 'openingBalance' => 'Opening balance',
'tagMode' => 'Tag mode',
'tagPosition' => 'Tag location',
'virtualBalance' => 'Virtual balance', 'virtualBalance' => 'Virtual balance',
'longitude_latitude' => 'Location',
'targetamount' => 'Target amount', 'targetamount' => 'Target amount',
'accountRole' => 'Account role', 'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date', 'openingBalanceDate' => 'Opening balance date',
@@ -201,15 +204,17 @@ class ExpandedForm
* *
* @return string * @return string
*/ */
public function month($name, $value = null, array $options = []) public function integer($name, $value = null, array $options = [])
{ {
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value); $value = $this->fillFieldValue($name, $value);
$html = View::make('form.month', compact('classes', 'name', 'label', 'value', 'options'))->render(); $options['step'] = '1';
$html = View::make('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html; return $html;
} }
/** /**
@@ -219,14 +224,13 @@ class ExpandedForm
* *
* @return string * @return string
*/ */
public function integer($name, $value = null, array $options = []) public function location($name, $value = null, array $options = [])
{ {
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value); $value = $this->fillFieldValue($name, $value);
$options['step'] = '1'; $html = View::make('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render();
$html = View::make('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html; return $html;
@@ -265,6 +269,44 @@ class ExpandedForm
return $selectList; return $selectList;
} }
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function month($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$html = View::make('form.month', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function multiRadio($name, array $list = [], $selected = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$selected = $this->fillFieldValue($name, $selected);
unset($options['class']);
$html = View::make('form.multiRadio', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
return $html;
}
/** /**
* @param $type * @param $type
* @param $name * @param $name
@@ -336,4 +378,24 @@ class ExpandedForm
return $html; return $html;
} }
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function textarea($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['rows'] = 4;
$html = View::make('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
} }

View File

@@ -0,0 +1,53 @@
<?php
namespace FireflyIII\Support\Twig;
use Auth;
use DB;
use FireflyIII\Models\LimitRepetition;
use Twig_Extension;
use Twig_SimpleFunction;
/**
* Class Budget
*
* @package FireflyIII\Support\Twig
*/
class Budget extends Twig_Extension
{
/**
* {@inheritDoc}
*/
public function getFunctions()
{
$functions[] = new Twig_SimpleFunction(
'spentInRepetition', function (LimitRepetition $repetition) {
$sum = DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('transaction_journals.date', '>=', $repetition->startdate->format('Y-m-d'))
->where('transaction_journals.date', '<=', $repetition->enddate->format('Y-m-d'))
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('transactions.deleted_at')
->where('transactions.amount', '>', 0)
->where('limit_repetitions.id', '=', $repetition->id)
->sum('transactions.amount');
return floatval($sum);
}
);
return $functions;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'FireflyIII\Support\Twig\Budget';
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace FireflyIII\Support\Twig;
use App;
use Config;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use Route;
use Twig_Extension;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
/**
* Class TwigSupport
*
* @package FireflyIII\Support
*/
class General extends Twig_Extension
{
/**
* @return array
*/
public function getFilters()
{
$filters = [];
$filters[] = new Twig_SimpleFilter(
'formatAmount', function ($string) {
return App::make('amount')->format($string);
}, ['is_safe' => ['html']]
);
$filters[] = new Twig_SimpleFilter(
'formatTransaction', function (Transaction $transaction) {
return App::make('amount')->formatTransaction($transaction);
}, ['is_safe' => ['html']]
);
$filters[] = new Twig_SimpleFilter(
'formatAmountPlain', function ($string) {
return App::make('amount')->format($string, false);
}
);
$filters[] = new Twig_SimpleFilter(
'formatJournal', function ($journal) {
return App::make('amount')->formatJournal($journal);
}, ['is_safe' => ['html']]
);
$filters[] = new Twig_SimpleFilter(
'balance', function (Account $account = null) {
if (is_null($account)) {
return 'NULL';
}
return App::make('steam')->balance($account);
}
);
// should be a function but OK
$filters[] = new Twig_SimpleFilter(
'getAccountRole', function ($name) {
return Config::get('firefly.accountRoles.' . $name);
}
);
return $filters;
}
/**
* {@inheritDoc}
*/
public function getFunctions()
{
$functions = [];
$functions[] = new Twig_SimpleFunction(
'getCurrencyCode', function () {
return App::make('amount')->getCurrencyCode();
}
);
$functions[] = new Twig_SimpleFunction(
'getCurrencySymbol', function () {
return App::make('amount')->getCurrencySymbol();
}
);
$functions[] = new Twig_SimpleFunction(
'phpdate', function ($str) {
return date($str);
}
);
$functions[] = new Twig_SimpleFunction(
'env', function ($name, $default) {
return env($name, $default);
}
);
$functions[] = new Twig_SimpleFunction(
'activeRoute', function ($context) {
$args = func_get_args();
$route = $args[1];
$what = isset($args[2]) ? $args[2] : false;
$strict = isset($args[3]) ? $args[3] : false;
$activeWhat = isset($context['what']) ? $context['what'] : false;
// activeRoute
if (!($what === false)) {
if ($what == $activeWhat && Route::getCurrentRoute()->getName() == $route) {
return 'active because-active-what';
}
} else {
if (!$strict && !(strpos(Route::getCurrentRoute()->getName(), $route) === false)) {
return 'active because-route-matches-non-strict';
} else {
if ($strict && Route::getCurrentRoute()->getName() == $route) {
return 'active because-route-matches-strict';
}
}
}
return 'not-xxx-at-all';
}, ['needs_context' => true]
);
return $functions;
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'FireflyIII\Support\Twig\General';
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace FireflyIII\Support\Twig;
use App;
use FireflyIII\Models\TransactionJournal;
use Twig_Extension;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
/**
* Class Journal
*
* @package FireflyIII\Support\Twig
*/
class Journal extends Twig_Extension
{
/**
* @return array
*/
public function getFilters()
{
$filters = [];
$filters[] = new Twig_SimpleFilter(
'typeIcon', function (TransactionJournal $journal) {
$type = $journal->transactionType->type;
if ($type == 'Withdrawal') {
return '<span class="glyphicon glyphicon-arrow-left" title="Withdrawal"></span>';
}
if ($type == 'Deposit') {
return '<span class="glyphicon glyphicon-arrow-right" title="Deposit"></span>';
}
if ($type == 'Transfer') {
return '<i class="fa fa-fw fa-exchange" title="Transfer"></i>';
}
if ($type == 'Opening balance') {
return '<span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span>';
}
}, ['is_safe' => ['html']]
);
return $filters;
}
/**
* @return array
*/
public function getFunctions()
{
$functions = [];
$functions[] = new Twig_SimpleFunction(
'invalidJournal', function (TransactionJournal $journal) {
if (!isset($journal->transactions[1]) || !isset($journal->transactions[0])) {
return true;
}
return false;
}
);
$functions[] = new Twig_SimpleFunction(
'relevantTags', function (TransactionJournal $journal) {
if ($journal->tags->count() == 0) {
return App::make('amount')->formatJournal($journal);
}
foreach ($journal->tags as $tag) {
if ($tag->tagMode == 'balancingAct') {
// return tag formatted for a "balancing act".
$amount = App::make('amount')->formatJournal($journal, false);
return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</span>';
}
}
return 'TODO: ' . $journal->amount;
}
);
return $functions;
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'FireflyIII\Support\Twig\Journals';
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FireflyIII\Support\Twig;
use Twig_Extension;
use Twig_SimpleFunction;
use FireflyIII\Models\PiggyBank as PB;
/**
* Class PiggyBank
*
* @package FireflyIII\Support\Twig
*/
class PiggyBank extends Twig_Extension
{
/**
*
*/
public function getFunctions()
{
$functions = [];
$functions[] = new Twig_SimpleFunction(
'currentRelevantRepAmount', function (PB $piggyBank) {
return $piggyBank->currentRelevantRep()->currentamount;
}
);
return $functions;
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'FireflyIII\Support\Twig\PiggyBank';
}
}

View File

@@ -43,6 +43,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->hasMany('FireflyIII\Models\Account'); return $this->hasMany('FireflyIII\Models\Account');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tags()
{
return $this->hasMany('FireflyIII\Models\Tag');
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -13,6 +13,7 @@ use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use Log; use Log;
use Navigation; use Navigation;
use Symfony\Component\Translation\TranslatorInterface;
/** /**
* Class FireflyValidator * Class FireflyValidator
@@ -22,6 +23,18 @@ use Navigation;
class FireflyValidator extends Validator class FireflyValidator extends Validator
{ {
/**
* @param TranslatorInterface $translator
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
*/
public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{
parent::__construct($translator, $data, $rules, $messages);
}
/** /**
* @param $attribute * @param $attribute
* @param $value * @param $value
@@ -181,10 +194,14 @@ class FireflyValidator extends Validator
*/ */
public function validateUniqueObjectForUser($attribute, $value, $parameters) public function validateUniqueObjectForUser($attribute, $value, $parameters)
{ {
$table = $parameters[0]; $table = $parameters[0];
$field = $parameters[1]; $field = $parameters[1];
$encrypted = isset($parameters[2]) ? $parameters[2] : 'encrypted'; $encrypted = isset($parameters[2]) ? $parameters[2] : 'encrypted';
$exclude = isset($parameters[3]) ? $parameters[3] : null; $exclude = isset($parameters[3]) ? $parameters[3] : null;
$alwaysEncrypted = false;
if ($encrypted == 'TRUE') {
$alwaysEncrypted = true;
}
$query = DB::table($table)->where('user_id', Auth::user()->id); $query = DB::table($table)->where('user_id', Auth::user()->id);
@@ -195,8 +212,12 @@ class FireflyValidator extends Validator
$set = $query->get(); $set = $query->get();
foreach ($set as $entry) { foreach ($set as $entry) {
$isEncrypted = intval($entry->$encrypted) == 1 ? true : false; if (!$alwaysEncrypted) {
$checkValue = $isEncrypted ? Crypt::decrypt($entry->$field) : $entry->$field; $isEncrypted = intval($entry->$encrypted) == 1 ? true : false;
} else {
$isEncrypted = true;
}
$checkValue = $isEncrypted ? Crypt::decrypt($entry->$field) : $entry->$field;
if ($checkValue == $value) { if ($checkValue == $value) {
return false; return false;
} }

View File

@@ -11,6 +11,7 @@
| |
*/ */
$app = new Illuminate\Foundation\Application( $app = new Illuminate\Foundation\Application(
realpath(__DIR__ . '/../') realpath(__DIR__ . '/../')
); );
@@ -26,6 +27,8 @@ $app = new Illuminate\Foundation\Application(
| |
*/ */
$app->singleton( $app->singleton(
'Illuminate\Contracts\Http\Kernel', 'Illuminate\Contracts\Http\Kernel',
'FireflyIII\Http\Kernel' 'FireflyIII\Http\Kernel'
@@ -41,6 +44,9 @@ $app->singleton(
'FireflyIII\Exceptions\Handler' 'FireflyIII\Exceptions\Handler'
); );
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Return The Application | Return The Application

View File

@@ -26,7 +26,9 @@
"watson/validating": "~1.0", "watson/validating": "~1.0",
"doctrine/dbal": "~2.5", "doctrine/dbal": "~2.5",
"illuminate/html": "~5.0", "illuminate/html": "~5.0",
"league/commonmark": "0.7.*" "league/commonmark": "0.7.*",
"rcrowe/twigbridge": "0.7.x@dev",
"twig/extensions": "~1.2"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "@stable", "barryvdh/laravel-debugbar": "@stable",

327
composer.lock generated
View File

@@ -4,24 +4,24 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "0d43c4c85607c5cdc901cde2d18b75d5", "hash": "e3e90dd365b74f4878cf3b5b4a1c4007",
"packages": [ "packages": [
{ {
"name": "classpreloader/classpreloader", "name": "classpreloader/classpreloader",
"version": "1.2.0", "version": "1.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ClassPreloader/ClassPreloader.git", "url": "https://github.com/ClassPreloader/ClassPreloader.git",
"reference": "f0bfbf71fb3335c9473f695d4d966ba2fb879a9f" "reference": "0544616ba33fb2a6b792b3a7822650810c6d65d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/f0bfbf71fb3335c9473f695d4d966ba2fb879a9f", "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/0544616ba33fb2a6b792b3a7822650810c6d65d9",
"reference": "f0bfbf71fb3335c9473f695d4d966ba2fb879a9f", "reference": "0544616ba33fb2a6b792b3a7822650810c6d65d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "~1.0", "nikic/php-parser": "^1.2.2",
"php": ">=5.3.3", "php": ">=5.3.3",
"symfony/console": "~2.1", "symfony/console": "~2.1",
"symfony/filesystem": "~2.1", "symfony/filesystem": "~2.1",
@@ -36,7 +36,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.2-dev" "dev-master": "1.3-dev"
} }
}, },
"autoload": { "autoload": {
@@ -64,7 +64,7 @@
"class", "class",
"preload" "preload"
], ],
"time": "2015-01-26 22:06:19" "time": "2015-04-15 21:59:30"
}, },
{ {
"name": "danielstjules/stringy", "name": "danielstjules/stringy",
@@ -206,16 +206,16 @@
}, },
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
"version": "v1.2.3", "version": "v1.2.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/annotations.git", "url": "https://github.com/doctrine/annotations.git",
"reference": "eeda578cbe24a170331a1cfdf78be723412df7a4" "reference": "b5202eb9e83f8db52e0e58867e0a46e63be8332e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/eeda578cbe24a170331a1cfdf78be723412df7a4", "url": "https://api.github.com/repos/doctrine/annotations/zipball/b5202eb9e83f8db52e0e58867e0a46e63be8332e",
"reference": "eeda578cbe24a170331a1cfdf78be723412df7a4", "reference": "b5202eb9e83f8db52e0e58867e0a46e63be8332e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -270,20 +270,20 @@
"docblock", "docblock",
"parser" "parser"
], ],
"time": "2014-12-20 20:49:38" "time": "2014-12-23 22:40:37"
}, },
{ {
"name": "doctrine/cache", "name": "doctrine/cache",
"version": "v1.4.0", "version": "v1.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/cache.git", "url": "https://github.com/doctrine/cache.git",
"reference": "2346085d2b027b233ae1d5de59b07440b9f288c8" "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/2346085d2b027b233ae1d5de59b07440b9f288c8", "url": "https://api.github.com/repos/doctrine/cache/zipball/c9eadeb743ac6199f7eec423cb9426bc518b7b03",
"reference": "2346085d2b027b233ae1d5de59b07440b9f288c8", "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -294,13 +294,13 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": ">=3.7", "phpunit/phpunit": ">=3.7",
"predis/predis": "~0.8", "predis/predis": "~1.0",
"satooshi/php-coveralls": "~0.6" "satooshi/php-coveralls": "~0.6"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.4.x-dev" "dev-master": "1.5.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -340,25 +340,28 @@
"cache", "cache",
"caching" "caching"
], ],
"time": "2015-01-15 20:38:55" "time": "2015-04-15 00:11:59"
}, },
{ {
"name": "doctrine/collections", "name": "doctrine/collections",
"version": "v1.2", "version": "v1.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/collections.git", "url": "https://github.com/doctrine/collections.git",
"reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a",
"reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.2" "php": ">=5.3.2"
}, },
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@@ -375,17 +378,6 @@
"MIT" "MIT"
], ],
"authors": [ "authors": [
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com",
"homepage": "http://www.jwage.com/",
"role": "Creator"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com",
"homepage": "http://www.instaclick.com"
},
{ {
"name": "Roman Borschel", "name": "Roman Borschel",
"email": "roman@code-factory.org" "email": "roman@code-factory.org"
@@ -394,11 +386,17 @@
"name": "Benjamin Eberlei", "name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de" "email": "kontakt@beberlei.de"
}, },
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{ {
"name": "Johannes Schmitt", "name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com", "email": "schmittjoh@gmail.com"
"homepage": "https://github.com/schmittjoh",
"role": "Developer of wrapped JMSSerializerBundle"
} }
], ],
"description": "Collections Abstraction library", "description": "Collections Abstraction library",
@@ -408,7 +406,7 @@
"collections", "collections",
"iterator" "iterator"
], ],
"time": "2014-02-03 23:07:43" "time": "2015-04-14 22:21:58"
}, },
{ {
"name": "doctrine/common", "name": "doctrine/common",
@@ -945,16 +943,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v5.0.27", "version": "v5.0.28",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "4d6330118a295086ce9ff8eed2200d5b67f17688" "reference": "06a09429322cf53e5bd4587db1060f02a291562e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/4d6330118a295086ce9ff8eed2200d5b67f17688", "url": "https://api.github.com/repos/laravel/framework/zipball/06a09429322cf53e5bd4587db1060f02a291562e",
"reference": "4d6330118a295086ce9ff8eed2200d5b67f17688", "reference": "06a09429322cf53e5bd4587db1060f02a291562e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1024,7 +1022,7 @@
"predis/predis": "~1.0" "predis/predis": "~1.0"
}, },
"suggest": { "suggest": {
"aws/aws-sdk-php": "Required to use the SQS queue driver (~2.4).", "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~2.4).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).", "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).",
"guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers (~5.0).", "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers (~5.0).",
"iron-io/iron_mq": "Required to use the iron queue driver (~1.5).", "iron-io/iron_mq": "Required to use the iron queue driver (~1.5).",
@@ -1067,7 +1065,7 @@
"framework", "framework",
"laravel" "laravel"
], ],
"time": "2015-04-04 01:34:57" "time": "2015-04-21 01:44:32"
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
@@ -1529,6 +1527,70 @@
], ],
"time": "2015-03-26 18:43:54" "time": "2015-03-26 18:43:54"
}, },
{
"name": "rcrowe/twigbridge",
"version": "0.7.x-dev",
"source": {
"type": "git",
"url": "https://github.com/rcrowe/TwigBridge.git",
"reference": "ac0bfb5bcdb4fcd0cd01ab8425620ff07f6af026"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/ac0bfb5bcdb4fcd0cd01ab8425620ff07f6af026",
"reference": "ac0bfb5bcdb4fcd0cd01ab8425620ff07f6af026",
"shasum": ""
},
"require": {
"illuminate/support": "5.0.*",
"illuminate/view": "5.0.*",
"php": ">=5.4.0",
"twig/twig": "~1.15"
},
"require-dev": {
"laravel/framework": "5.0.*",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "~0.6",
"squizlabs/php_codesniffer": "~1.5"
},
"suggest": {
"laravelcollective/html": "For bringing back html/form in Laravel 5.x",
"twig/extensions": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.7-dev"
}
},
"autoload": {
"psr-4": {
"TwigBridge\\": "src",
"TwigBridge\\Tests\\": "tests"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
},
{
"name": "Rob Crowe",
"email": "hello@vivalacrowe.com"
}
],
"description": "Adds the power of Twig to Laravel",
"keywords": [
"laravel",
"twig"
],
"time": "2015-04-22 09:19:03"
},
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
"version": "v5.4.0", "version": "v5.4.0",
@@ -2293,6 +2355,115 @@
], ],
"time": "2015-03-31 08:12:29" "time": "2015-03-31 08:12:29"
}, },
{
"name": "twig/extensions",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig-extensions.git",
"reference": "8cf4b9fe04077bd54fc73f4fde83347040c3b8cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/8cf4b9fe04077bd54fc73f4fde83347040c3b8cd",
"reference": "8cf4b9fe04077bd54fc73f4fde83347040c3b8cd",
"shasum": ""
},
"require": {
"twig/twig": "~1.12"
},
"require-dev": {
"symfony/translation": "~2.3"
},
"suggest": {
"symfony/translation": "Allow the time_diff output to be translated"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Twig_Extensions_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Common additional features for Twig that do not directly belong in core",
"homepage": "http://twig.sensiolabs.org/doc/extensions/index.html",
"keywords": [
"i18n",
"text"
],
"time": "2014-10-30 14:30:03"
},
{
"name": "twig/twig",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9f70492f44398e276d1b81c1b43adfe6751c7b7f",
"reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f",
"shasum": ""
},
"require": {
"php": ">=5.2.7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "http://twig.sensiolabs.org/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2015-04-19 08:30:27"
},
{ {
"name": "vlucas/phpdotenv", "name": "vlucas/phpdotenv",
"version": "v1.1.0", "version": "v1.1.0",
@@ -3027,23 +3198,23 @@
}, },
{ {
"name": "phpspec/phpspec", "name": "phpspec/phpspec",
"version": "2.1.1", "version": "2.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpspec/phpspec.git", "url": "https://github.com/phpspec/phpspec.git",
"reference": "66a1df93099282b1514e9e001fcf6e9393f7783d" "reference": "9727d75919a00455433e867565bc022f0b985a39"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpspec/phpspec/zipball/66a1df93099282b1514e9e001fcf6e9393f7783d", "url": "https://api.github.com/repos/phpspec/phpspec/zipball/9727d75919a00455433e867565bc022f0b985a39",
"reference": "66a1df93099282b1514e9e001fcf6e9393f7783d", "reference": "9727d75919a00455433e867565bc022f0b985a39",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/instantiator": "~1.0,>=1.0.1", "doctrine/instantiator": "^1.0.1",
"php": ">=5.3.3", "php": ">=5.3.3",
"phpspec/php-diff": "~1.0.0", "phpspec/php-diff": "~1.0.0",
"phpspec/prophecy": "~1.1", "phpspec/prophecy": "~1.4",
"sebastian/exporter": "~1.0", "sebastian/exporter": "~1.0",
"symfony/console": "~2.3", "symfony/console": "~2.3",
"symfony/event-dispatcher": "~2.1", "symfony/event-dispatcher": "~2.1",
@@ -3052,9 +3223,11 @@
"symfony/yaml": "~2.1" "symfony/yaml": "~2.1"
}, },
"require-dev": { "require-dev": {
"behat/behat": "~3.0,>=3.0.11", "behat/behat": "^3.0.11",
"bossa/phpspec2-expect": "~1.0", "bossa/phpspec2-expect": "~1.0",
"symfony/filesystem": "~2.1" "phpunit/phpunit": "~4.4",
"symfony/filesystem": "~2.1",
"symfony/process": "~2.1"
}, },
"suggest": { "suggest": {
"phpspec/nyan-formatters": "~1.0 Adds Nyan formatters" "phpspec/nyan-formatters": "~1.0 Adds Nyan formatters"
@@ -3065,7 +3238,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.1.x-dev" "dev-master": "2.2.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -3099,20 +3272,20 @@
"testing", "testing",
"tests" "tests"
], ],
"time": "2015-01-09 13:21:45" "time": "2015-04-18 16:22:51"
}, },
{ {
"name": "phpspec/prophecy", "name": "phpspec/prophecy",
"version": "1.4.0", "version": "v1.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpspec/prophecy.git", "url": "https://github.com/phpspec/prophecy.git",
"reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5" "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373",
"reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5", "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3159,20 +3332,20 @@
"spy", "spy",
"stub" "stub"
], ],
"time": "2015-03-27 19:31:25" "time": "2015-04-27 22:15:08"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "2.0.15", "version": "2.0.16",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "34cc484af1ca149188d0d9e91412191e398e0b67" "reference": "934fd03eb6840508231a7f73eb8940cf32c3b66c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/34cc484af1ca149188d0d9e91412191e398e0b67", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/934fd03eb6840508231a7f73eb8940cf32c3b66c",
"reference": "34cc484af1ca149188d0d9e91412191e398e0b67", "reference": "934fd03eb6840508231a7f73eb8940cf32c3b66c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3221,7 +3394,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2015-01-24 10:06:35" "time": "2015-04-11 04:35:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@@ -3360,16 +3533,16 @@
}, },
{ {
"name": "phpunit/php-token-stream", "name": "phpunit/php-token-stream",
"version": "1.4.0", "version": "1.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git", "url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74" "reference": "eab81d02569310739373308137284e0158424330"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db32c18eba00b121c145575fcbcd4d4d24e6db74", "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/eab81d02569310739373308137284e0158424330",
"reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74", "reference": "eab81d02569310739373308137284e0158424330",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3405,20 +3578,20 @@
"keywords": [ "keywords": [
"tokenizer" "tokenizer"
], ],
"time": "2015-01-17 09:51:32" "time": "2015-04-08 04:46:07"
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "4.6.1", "version": "4.6.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "08b2aacdd8433abbba468f995d6d64b76a7a62ec" "reference": "163232991e652e6efed2f8470326fffa61e848e2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/08b2aacdd8433abbba468f995d6d64b76a7a62ec", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/163232991e652e6efed2f8470326fffa61e848e2",
"reference": "08b2aacdd8433abbba468f995d6d64b76a7a62ec", "reference": "163232991e652e6efed2f8470326fffa61e848e2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3457,9 +3630,6 @@
"autoload": { "autoload": {
"classmap": [ "classmap": [
"src/" "src/"
],
"files": [
"src/Framework/Assert/Functions.php"
] ]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@@ -3480,7 +3650,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2015-04-03 13:46:59" "time": "2015-04-11 05:23:21"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@@ -4182,6 +4352,7 @@
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": { "stability-flags": {
"rcrowe/twigbridge": 20,
"barryvdh/laravel-debugbar": 0 "barryvdh/laravel-debugbar": 0
}, },
"prefer-stable": false, "prefer-stable": false,

View File

@@ -136,7 +136,11 @@ return [
'Illuminate\Validation\ValidationServiceProvider', 'Illuminate\Validation\ValidationServiceProvider',
'Illuminate\View\ViewServiceProvider', 'Illuminate\View\ViewServiceProvider',
'Illuminate\Html\HtmlServiceProvider', 'Illuminate\Html\HtmlServiceProvider',
'TwigBridge\ServiceProvider',
'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'DaveJamesMiller\Breadcrumbs\ServiceProvider',
'Barryvdh\Debugbar\ServiceProvider',
'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
/* /*
* Application Service Providers... * Application Service Providers...
@@ -149,6 +153,7 @@ return [
'FireflyIII\Providers\FireflyServiceProvider', 'FireflyIII\Providers\FireflyServiceProvider',
'FireflyIII\Providers\TestingServiceProvider', 'FireflyIII\Providers\TestingServiceProvider',
], ],
/* /*
@@ -198,13 +203,14 @@ return [
'View' => 'Illuminate\Support\Facades\View', 'View' => 'Illuminate\Support\Facades\View',
'Form' => 'Illuminate\Html\FormFacade', 'Form' => 'Illuminate\Html\FormFacade',
'Html' => 'Illuminate\Html\HtmlFacade', 'Html' => 'Illuminate\Html\HtmlFacade',
'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade',
'Preferences' => 'FireflyIII\Support\Facades\Preferences', 'Preferences' => 'FireflyIII\Support\Facades\Preferences',
'Navigation' => 'FireflyIII\Support\Facades\Navigation', 'Navigation' => 'FireflyIII\Support\Facades\Navigation',
'Amount' => 'FireflyIII\Support\Facades\Amount', 'Amount' => 'FireflyIII\Support\Facades\Amount',
'Steam' => 'FireflyIII\Support\Facades\Steam', 'Steam' => 'FireflyIII\Support\Facades\Steam',
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
'Twig' => 'TwigBridge\Facade\Twig',
], ],

View File

@@ -48,7 +48,7 @@ return [
'sqlite' => [ 'sqlite' => [
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => ':memory:', 'database' => __DIR__.'/../storage/database/testing.db',
'prefix' => '', 'prefix' => '',
], ],

View File

@@ -14,7 +14,7 @@ return [
*/ */
'paths' => [ 'paths' => [
realpath(base_path('resources/views')) realpath(base_path('resources/twig'))
], ],
/* /*

View File

@@ -41,6 +41,9 @@ class CreateAccountMetaTable extends Migration
$table->unique(['account_id', 'name']); $table->unique(['account_id', 'name']);
// link to account!
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
} }
); );

View File

@@ -0,0 +1,75 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class ChangesForV3310 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('tag_transaction_journal');
Schema::drop('tags');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
Schema::table(
'transaction_groups', function (Blueprint $table) {
// drop column "relation"
$table->dropColumn('relation');
}
);
/*
* New table!
*/
Schema::create(
'tags', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('user_id')->unsigned();
$table->string('tag', 1024);
$table->string('tagMode', 1024);
$table->date('date')->nullable();
$table->text('description')->nullable();
$table->decimal('latitude', 18, 12)->nullable();
$table->decimal('longitude', 18, 12)->nullable();
$table->smallInteger('zoomLevel', false, true)->nullable();
// connect reminders to users
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
Schema::create('tag_transaction_journal',function (Blueprint $table) {
$table->increments('id');
$table->integer('tag_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
// link to foreign tables.
$table->foreign('tag_id', 'tag_grp_id')->references('id')->on('tags')->onDelete('cascade');
$table->foreign('transaction_journal_id', 'tag_trj_id')->references('id')->on('transaction_journals')->onDelete('cascade');
// add unique.
$table->unique(['tag_id', 'transaction_journal_id'], 'tag_t_joined');
});
}
}

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV3310a
*/
class ChangesForV3310a extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table(
'transaction_groups', function (Blueprint $table) {
// new column, relation.
$table->string('relation', 50)->nullable();
}
);
// make new column "relation"
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
/**
* Class ChangesForV3310b
*/
class ChangesForV3310b extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// set all current entries to be "balance"
DB::table('transaction_groups')->update(['relation' => 'balance']);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@@ -7,7 +7,7 @@
convertNoticesToExceptions="true" convertNoticesToExceptions="true"
convertWarningsToExceptions="true" convertWarningsToExceptions="true"
processIsolation="false" processIsolation="false"
stopOnFailure="false" stopOnFailure="true"
syntaxCheck="false"> syntaxCheck="false">
<testsuites> <testsuites>
<testsuite name="Application Test Suite"> <testsuite name="Application Test Suite">

5
pu.sh
View File

@@ -1,8 +1,5 @@
#!/bin/bash #!/bin/bash
# backup .env file.
cp .env .env.backup
# set testing environment # set testing environment
cp .env.testing .env cp .env.testing .env
@@ -18,4 +15,4 @@ then
fi fi
# restore .env file # restore .env file
mv .env.backup .env cp .env.local .env

View File

@@ -5,4 +5,9 @@
.ui-sortable-placeholder { .ui-sortable-placeholder {
display: inline-block; display: inline-block;
height: 1px; height: 1px;
}
#map-canvas {
height: 100%;
margin: 0px;
padding: 0px
} }

View File

@@ -0,0 +1 @@
{"version":3,"file":"dist/bootstrap-tagsinput-angular.min.js","sources":["src/bootstrap-tagsinput-angular.js"],"names":["angular","module","directive","getItemProperty","scope","property","isFunction","$parent","item","undefined","restrict","model","template","replace","link","element","attrs","$","isArray","select","typeaheadSourceArray","typeaheadSource","split","length","tagsinput","options","typeahead","source","itemValue","itemvalue","itemText","itemtext","confirmKeys","confirmkeys","JSON","parse","tagClass","tagclass","i","on","event","indexOf","push","idx","splice","prev","slice","$watch","added","filter","removed"],"mappings":";;;;;AAAAA,QAAQC,OAAO,0BACdC,UAAU,sBAAuB,WAEhC,QAASC,GAAgBC,EAAOC,GAC9B,MAAKA,GAGDL,QAAQM,WAAWF,EAAMG,QAAQF,IAC5BD,EAAMG,QAAQF,GAEhB,SAASG,GACd,MAAOA,GAAKH,IANLI,OAUX,OACEC,SAAU,KACVN,OACEO,MAAO,YAETC,SAAU,6BACVC,SAAS,EACTC,KAAM,SAASV,EAAOW,EAASC,GAC7BC,EAAE,WACKjB,QAAQkB,QAAQd,EAAMO,SACzBP,EAAMO,SAER,IAAIQ,GAASF,EAAE,SAAUF,GACrBK,EAAuBJ,EAAMK,gBAAkBL,EAAMK,gBAAgBC,MAAM,KAAO,KAClFD,EAAkBD,EACjBA,EAAqBG,OAAS,EAC3BnB,EAAMG,QAAQa,EAAqB,IAAIA,EAAqB,IAC1DhB,EAAMG,QAAQa,EAAqB,IACvC,IAEND,GAAOK,UAAUpB,EAAMG,QAAQS,EAAMS,SAAW,MAC9CC,WACEC,OAAW3B,QAAQM,WAAWe,GAAmBA,EAAkB,MAErEO,UAAWzB,EAAgBC,EAAOY,EAAMa,WACxCC,SAAW3B,EAAgBC,EAAOY,EAAMe,UACxCC,YAAc7B,EAAgBC,EAAOY,EAAMiB,aAAeC,KAAKC,MAAMnB,EAAMiB,cAAgB,IAC3FG,SAAWpC,QAAQM,WAAWF,EAAMG,QAAQS,EAAMqB,WAAajC,EAAMG,QAAQS,EAAMqB,UAAY,WAAiB,MAAOrB,GAAMqB,WAG/H,KAAK,GAAIC,GAAI,EAAGA,EAAIlC,EAAMO,MAAMY,OAAQe,IACtCnB,EAAOK,UAAU,MAAOpB,EAAMO,MAAM2B,GAGtCnB,GAAOoB,GAAG,YAAa,SAASC,GACU,KAApCpC,EAAMO,MAAM8B,QAAQD,EAAMhC,OAC5BJ,EAAMO,MAAM+B,KAAKF,EAAMhC,QAG3BW,EAAOoB,GAAG,cAAe,SAASC,GAChC,GAAIG,GAAMvC,EAAMO,MAAM8B,QAAQD,EAAMhC,KACxB,MAARmC,GACFvC,EAAMO,MAAMiC,OAAOD,EAAK,IAK5B,IAAIE,GAAOzC,EAAMO,MAAMmC,OACvB1C,GAAM2C,OAAO,QAAS,WACpB,GAEIT,GAFAU,EAAQ5C,EAAMO,MAAMsC,OAAO,SAASX,GAAI,MAA2B,KAApBO,EAAKJ,QAAQH,KAC5DY,EAAUL,EAAKI,OAAO,SAASX,GAAI,MAAkC,KAA3BlC,EAAMO,MAAM8B,QAAQH,IAMlE,KAHAO,EAAOzC,EAAMO,MAAMmC,QAGdR,EAAI,EAAGA,EAAIY,EAAQ3B,OAAQe,IAC9BnB,EAAOK,UAAU,SAAU0B,EAAQZ,GAOrC,KAHAnB,EAAOK,UAAU,WAGZc,EAAI,EAAGA,EAAIU,EAAMzB,OAAQe,IAC5BnB,EAAOK,UAAU,MAAOwB,EAAMV,MAE/B"}

View File

@@ -3,7 +3,7 @@ google.setOnLoadCallback(drawChart);
function drawChart() { function drawChart() {
googleLineChart('chart/home/account', 'accounts-chart'); googleLineChart('chart/home/account', 'accounts-chart');
googleBarChart('chart/home/budgets', 'budgets-chart'); googleColumnChart('chart/home/budgets', 'budgets-chart');
googleColumnChart('chart/home/categories', 'categories-chart'); googleColumnChart('chart/home/categories', 'categories-chart');
googlePieChart('chart/home/bills', 'bills-chart'); googlePieChart('chart/home/bills', 'bills-chart');
getBoxAmounts(); getBoxAmounts();

View File

@@ -5,14 +5,43 @@ $(function () {
if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') { if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') {
googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history'); googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history');
} }
$('#sortable').sortable(
$('#sortable tbody').sortable(
{ {
helper: fixHelper,
stop: stopSorting, stop: stopSorting,
handle: '.handle' handle: '.handle',
start: function (event, ui) {
// Build a placeholder cell that spans all the cells in the row
var cellCount = 0;
$('td, th', ui.helper).each(function () {
// For each TD or TH try and get it's colspan attribute, and add that or 1 to the total
var colspan = 1;
var colspanAttr = $(this).attr('colspan');
if (colspanAttr > 1) {
colspan = colspanAttr;
}
cellCount += colspan;
});
// Add the placeholder UI - note that this is the item's content, so TD rather than TR
ui.placeholder.html('<td colspan="' + cellCount + '">&nbsp;</td>');
}
} }
); );
}); });
// Return a helper with preserved width of cells
var fixHelper = function(e, tr) {
var $originals = tr.children();
var $helper = tr.clone();
$helper.children().each(function (index) {
// Set helper cell sizes to match the original sizes
$(this).width($originals.eq(index).width());
});
return $helper;
}
function addMoney(e) { function addMoney(e) {
var pigID = parseInt($(e.target).data('id')); var pigID = parseInt($(e.target).data('id'));
$('#moneyManagementModal').empty().load('piggy-banks/add/' + pigID, function () { $('#moneyManagementModal').empty().load('piggy-banks/add/' + pigID, function () {
@@ -33,7 +62,7 @@ function removeMoney(e) {
function stopSorting() { function stopSorting() {
$('.loadSpin').addClass('fa fa-refresh fa-spin'); $('.loadSpin').addClass('fa fa-refresh fa-spin');
var order = []; var order = [];
$.each($('#sortable>div'), function(i,v) { $.each($('#sortable>tbody>tr'), function(i,v) {
var holder = $(v); var holder = $(v);
var id = holder.data('id'); var id = holder.data('id');
order.push(id); order.push(id);

View File

@@ -1,112 +0,0 @@
$(document).ready(function () {
$('.relateTransaction').click(relateTransactionDialog);
//$('.unrelate-checkbox').click(unrelateTransaction);
});
function unrelateTransaction(e) {
var target = $(e.target);
var id = target.data('id');
var parent = target.data('parent');
if(typeof id == "undefined" && typeof parent == "undefined") {
target = target.parent();
id = target.data('id');
parent = target.data('parent');
}
console.log('unlink ' + id + ' from ' + parent);
$.post('related/removeRelation/' + id + '/' + parent, {_token: token}).success(function (data) {
target.parent().parent().remove();
}).fail(function () {
alert('Could not!');
});
return false;
//$.post('related/removeRelation/' + id + '/' + relatedTo, {_token: token}).success(function (data) {
// target.parent().parent().remove();
//}).fail(function () {
// alert('Could not!');
//});
}
function relateTransactionDialog(e) {
var target = $(e.target);
var ID = target.data('id');
$('#relationModal').empty().load('related/related/' + ID, function () {
$('#relationModal').modal('show');
getAlreadyRelatedTransactions(e, ID);
$('#searchRelated').submit(function (e) {
searchRelatedTransactions(e, ID);
return false;
});
});
return false;
}
function searchRelatedTransactions(e, ID) {
var searchValue = $('#relatedSearchValue').val();
if (searchValue != '') {
$.post('related/search/' + ID, {searchValue: searchValue, _token: token}).success(function (data) {
// post the results to some div.
$('#relatedSearchResultsTitle').show();
$('#relatedSearchResults').empty().html(data);
// remove any clicks.
$('.relate').unbind('click').on('click', doRelateNewTransaction);
}).fail(function () {
alert('Could not search. Sorry.');
});
}
return false;
}
function doRelateNewTransaction(e) {
// remove the row from the table:
var target = $(e.target);
var id = target.data('id');
var parent = target.data('parent');
if (typeof id == "undefined" && typeof parent == "undefined") {
target = target.parent();
console.log(target);
id = target.data('id');
parent = target.data('parent');
}
console.log('Relate ' + id + ' to ' + parent);
$.post('related/relate/' + parent + '/' + id, {_token: token}).success(function (data) {
// success! remove entry:
target.parent().parent().remove();
// get related stuff (again).
getAlreadyRelatedTransactions(null, parent);
}).fail(function () {
// could not relate.
alert('Could not relate this transaction to the intended target.');
});
return false;
}
function getAlreadyRelatedTransactions(e, ID) {
//#alreadyRelated
$.get('related/alreadyRelated/' + ID).success(function (data) {
$('#alreadyRelated').empty().html(data);
// some event triggers.
$('.unrelate').unbind('click').on('click', unrelateTransaction);
}).fail(function () {
alert('Cannot get related stuff.');
});
}

View File

@@ -29,8 +29,8 @@ $(function() {
var url = window.location; var url = window.location;
var element = $('ul.nav a').filter(function() { var element = $('ul.nav a').filter(function() {
return this.href == url || url.href.indexOf(this.href) == 0; return this.href == url || url.href.indexOf(this.href) == 0;
}).addClass('active').parent().parent().addClass('in').parent(); }).parent().parent().addClass('in').parent();/*addClass('active')*/
if (element.is('li')) { if (element.is('li')) {
element.addClass('active'); //element.addClass('active');
} }
}); });

115
public/js/tags.js Normal file
View File

@@ -0,0 +1,115 @@
$(function () {
/*
Hide and show the tag index help.
*/
$('#tagHelp').on('show.bs.collapse', function () {
// set hideTagHelp = false
$.post('/tags/hideTagHelp/false', {_token: token});
$('#tagHelpButton').text('Hide help');
}).on('hide.bs.collapse', function () {
// set hideTagHelp = true
$.post('/tags/hideTagHelp/true', {_token: token});
$('#tagHelpButton').text('Show help');
});
$('#clearLocation').click(clearLocation);
});
/*
Some vars as prep for the map:
*/
var map;
var markers = [];
var setTag = false;
var mapOptions = {
zoom: zoomLevel,
center: new google.maps.LatLng(latitude, longitude),
disableDefaultUI: true
};
/*
Clear location and reset zoomLevel.
*/
function clearLocation() {
"use strict";
deleteMarkers();
$('input[name="latitude"]').val("");
$('input[name="longitude"]').val("");
$('input[name="zoomLevel"]').val("6");
setTag = false;
$('input[name="setTag"]').val('false');
return false;
}
function initialize() {
/*
Create new map:
*/
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
/*
Respond to click event.
*/
google.maps.event.addListener(map, 'rightclick', function (event) {
placeMarker(event);
});
/*
Respond to zoom event.
*/
google.maps.event.addListener(map, 'zoom_changed', function () {
saveZoomLevel(event);
});
/*
Maybe place marker?
*/
if(doPlaceMarker) {
var myLatlng = new google.maps.LatLng(latitude,longitude);
var fakeEvent = {};
fakeEvent.latLng = myLatlng;
placeMarker(fakeEvent);
}
}
/**
* save zoom level of map into hidden input.
*/
function saveZoomLevel() {
"use strict";
$('input[name="zoomLevel"]').val(map.getZoom());
}
/**
* Place marker on map.
* @param event
*/
function placeMarker(event) {
deleteMarkers();
var marker = new google.maps.Marker({position: event.latLng, map: map});
$('input[name="latitude"]').val(event.latLng.lat());
$('input[name="longitude"]').val(event.latLng.lng());
markers.push(marker);
setTag = true;
$('input[name="setTag"]').val('true');
}
/**
* Deletes all markers in the array by removing references to them.
*/
function deleteMarkers() {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
markers = [];
}
google.maps.event.addDomListener(window, 'load', initialize);

View File

@@ -8,6 +8,20 @@ $(document).ready(function () {
$('input[name="expense_account"]').typeahead({source: data}); $('input[name="expense_account"]').typeahead({source: data});
}); });
} }
if ($('input[name="tags"]').length > 0) {
$.getJSON('json/tags').success(function (data) {
var opt = {
typeahead: {
source: data
}
};
$('input[name="tags"]').tagsinput(
opt
);
});
}
if ($('input[name="revenue_account"]').length > 0) { if ($('input[name="revenue_account"]').length > 0) {
$.getJSON('json/revenue-accounts').success(function (data) { $.getJSON('json/revenue-accounts').success(function (data) {
$('input[name="revenue_account"]').typeahead({source: data}); $('input[name="revenue_account"]').typeahead({source: data});

View File

@@ -0,0 +1,61 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }}
<form action="{{ route('accounts.store') }}" method="post" id="store" class="form-horizontal">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input type="hidden" name="what" value="{{ what }}" />
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{ subTitleIcon }}"></i> Mandatory fields
</div>
<div class="panel-body">
{{ ExpandedForm.text('name') }}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
{% if what == 'asset' %}
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
{{ ExpandedForm.balance('openingBalance') }}
{{ ExpandedForm.date('openingBalanceDate', phpdate('Y-m-d')) }}
{{ ExpandedForm.select('accountRole', Config.get('firefly.accountRoles'),null,{'helpText' : 'Any extra options resulting from your choice can be set later.'}) }}
{{ ExpandedForm.balance('virtualBalance') }}
</div>
</div>
{% endif %}
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
{{ ExpandedForm.optionsList('create','account') }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new {{ what }} account
</button>
</p>
</div>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,36 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }}
{{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('accounts.destroy',account.id)}) }}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red">
<div class="panel-heading">
Delete account "{{ account.name }}"
</div>
<div class="panel-body">
<p>
Are you sure that you want to delete the {{account.accountType.type|lower}} "{{account.name}}"?
</p>
{% if account.transactions|length > 0 %}
<p class="text-danger">
{{account.accountType.type|capitalize}} "{{ account.name }}" still has {{ account.transactions|length }} transaction(s) associated to it. These will be deleted as well.
</p>
{% endif %}
{% if account.piggyBanks|length > 0 %}
<p class="text-danger">
{{account.accountType.type|capitalize}} "{{ account.name }}" still has {{ account.piggyBanks|length }} piggy bank(s) associated to it. These will be deleted as well.
</p>
{% endif %}
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL.previous()}}" class="btn-default btn">Cancel</a >
</p>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,71 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }}
{{ Form.model(account, {'class' : 'form-horizontal','id' : 'update','url' : route('accounts.update',account.id) } ) }}
<input type="hidden" name="id" value="{{account.id}}" />
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{ subTitleIcon }}"></i> Mandatory fields
</div>
<div class="panel-body">
{{ ExpandedForm.text('name') }}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
{% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %}
{{ ExpandedForm.balance('openingBalance',null, {'currency' : openingBalance ? openingBalance.transactionCurrency : null}) }}
{{ ExpandedForm.date('openingBalanceDate') }}
{{ ExpandedForm.select('accountRole',Config.get('firefly.accountRoles')) }}
{{ ExpandedForm.balance('virtualBalance',null) }}
{% endif %}
{{ ExpandedForm.checkbox('active','1') }}
</div>
</div>
<!-- panel for credit card options -->
{% if Session.get('preFilled').accountRole == 'ccAsset' %}
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-credit-card"></i> Credit card options
</div>
<div class="panel-body">
{{ ExpandedForm.select('ccType',Config.get('firefly.ccTypes')) }}
{{ ExpandedForm.date('ccMonthlyPaymentDate',null,{'helpText' : 'Select any year and any month, it will be ignored anway. Only the day of the month is relevant.'}) }}
</div>
</div>
{% endif %}
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
{{ ExpandedForm.optionsList('update','account') }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
Update account
</button>
</p>
</div>
</div>
</form>
{% endblock %}

View File

@@ -1,11 +1,11 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $what) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }}
<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="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> {{{$subTitle}}} <i class="fa {{ subTitleIcon }}"></i> {{ subTitle}}
<!-- ACTIONS MENU --> <!-- ACTIONS MENU -->
<div class="pull-right"> <div class="pull-right">
@@ -15,27 +15,26 @@
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu pull-right" role="menu"> <ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('accounts.create',$what)}}"><i class="fa fa-plus fa-fw"></i> New {{$what}} account</a></li> <li><a href="{{route('accounts.create', what)}}"><i class="fa fa-plus fa-fw"></i> New {{ what }} account</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
@include('list.accounts') {% include 'list/accounts.twig' %}
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}
@section('styles') {% block styles %}
<link rel="stylesheet" href="css/bootstrap-sortable.css" type="text/css" media="all" /> <link rel="stylesheet" href="css/bootstrap-sortable.css" type="text/css" media="all" />
@stop {% endblock %}
@section('scripts') {% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
var what = '{{{$what}}}'; var what = '{{ what }}';
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script> </script>
<!-- load the libraries and scripts necessary for Google Charts: --> <!-- load the libraries and scripts necessary for Google Charts: -->
@@ -44,4 +43,4 @@
<script type="text/javascript" src="js/gcharts.js"></script> <script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript" src="js/bootstrap-sortable.js"></script> <script type="text/javascript" src="js/bootstrap-sortable.js"></script>
<script type="text/javascript" src="js/accounts.js"></script> <script type="text/javascript" src="js/accounts.js"></script>
@stop {% endblock %}

View File

@@ -1,11 +1,11 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, account) }}
<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="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-fw {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}} <i class="fa fa-fw {{ subTitleIcon }} fa-fw"></i> {{ account.name }}
<!-- ACTIONS MENU --> <!-- ACTIONS MENU -->
@@ -16,8 +16,8 @@
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu pull-right" role="menu"> <ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('accounts.edit',$account->id)}}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li> <li><a href="{{route('accounts.edit', account.id)}}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li>
<li><a href="{{route('accounts.delete',$account->id)}}"><i class="fa fa-trash fa-fw"></i> Delete</a></li> <li><a href="{{route('accounts.delete', account.id)}}"><i class="fa fa-trash fa-fw"></i> Delete</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -37,19 +37,18 @@
<i class="fa fa-repeat fa-fw"></i> Transactions <i class="fa fa-repeat fa-fw"></i> Transactions
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['sorting' => true]) {% include 'list/journals.twig' with {sorting:true} %}
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}
@section('scripts')
{% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
var accountID = {{{$account->id}}}; var accountID = {{ account.id }};
var currencyCode = '{{Amount::getCurrencyCode()}}';
var token = "{{csrf_token()}}";
</script> </script>
<!-- load the libraries and scripts necessary for Google Charts: --> <!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script>
@@ -57,4 +56,5 @@
<script type="text/javascript" src="js/gcharts.js"></script> <script type="text/javascript" src="js/gcharts.js"></script>
<script src="js/jquery-ui.min.js" type="text/javascript"></script> <script src="js/jquery-ui.min.js" type="text/javascript"></script>
<script src="js/accounts.js" type="text/javascript"></script> <script src="js/accounts.js" type="text/javascript"></script>
@stop
{% endblock %}

View File

@@ -1,16 +1,17 @@
@extends('layouts.guest') {% extends "./layout/guest.twig" %}
@section('content')
@if($errors->has('email')) {% block content %}
{% if errors.has('email') %}
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="alert alert-danger alert-dismissible" role="alert"> <div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button> <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<strong>Error!</strong> {{$errors->get('email')[0]}} <strong>Error!</strong> {{ errors.get('email')[0] }}
</div> </div>
</div> </div>
</div> </div>
@endif {% endif %}
<div class="row"> <div class="row">
@@ -42,9 +43,9 @@
<button type="submit" class="btn btn-lg btn-success btn-block">Login</button> <button type="submit" class="btn btn-lg btn-success btn-block">Login</button>
</p> </p>
<div class="btn-group btn-group-justified btn-group-sm"> <div class="btn-group btn-group-justified btn-group-sm">
@if(Config::get('auth.allow_register') === true) {% if Config.get('auth.allow_register') %}
<a href="{{route('register')}}" class="btn btn-default">Register</a> <a href="{{ route('register') }}" class="btn btn-default">Register</a>
@endif {% endif %}
<a href="/password/email" class="btn btn-default">Forgot your password?</a> <a href="/password/email" class="btn btn-default">Forgot your password?</a>
</div> </div>
</form> </form>
@@ -52,4 +53,5 @@
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}

View File

@@ -1,6 +1,6 @@
@extends('layouts.guest') {% extends "./layout/guest.twig" %}
@section('content') {% block content %}
<div class="row"> <div class="row">
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">
<div class="login-panel panel panel-default"> <div class="login-panel panel panel-default">
@@ -8,28 +8,28 @@
<h3 class="panel-title">Firefly III &mdash; Reset Password</h3> <h3 class="panel-title">Firefly III &mdash; Reset Password</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
@if (session('status')) {% if session.status %}
<div class="alert alert-success"> <div class="alert alert-success">
{{ session('status') }} {{ session.status }}
</div> </div>
@endif {% endif %}
@if (count($errors) > 0) {% if errors|length > 0 %}
<div class="alert alert-danger"> <div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br> <strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul> <ul>
@foreach ($errors->all() as $error) {% for error in errors.all %}
<li>{{ $error }}</li> <li>{{ error }}</li>
@endforeach {% endfor %}
</ul> </ul>
</div> </div>
@endif {% endif %}
<form role="form" method="POST" action="/password/email"> <form role="form" method="POST" action="/password/email">
<input type="hidden" name="_token" value="{{ csrf_token() }}"> <input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<label class="control-label">E-Mail</label> <label class="control-label">E-Mail</label>
<input type="email" class="form-control" placeholder="E-Mail" name="email" value="{{ old('email') }}"> <input type="email" class="form-control" placeholder="E-Mail" name="email" value="{{ old.email }}">
</div> </div>
<p> <p>
@@ -42,4 +42,4 @@
</div> </div>
</div> </div>
</div> </div>
@endsection {% endblock %}

View File

@@ -1,6 +1,6 @@
@extends('layouts.guest') {% extends "./layout/guest.twig" %}
@section('content') {% block content %}
<div class="row"> <div class="row">
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">
<div class="login-panel panel panel-default"> <div class="login-panel panel panel-default">
@@ -11,23 +11,23 @@
<p> <p>
Registering an account on Firefly requires an e-mail address. Registering an account on Firefly requires an e-mail address.
</p> </p>
@if (count($errors) > 0) {% if errors|length > 0 %}
<div class="alert alert-danger"> <div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br> <strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul> <ul>
@foreach ($errors->all() as $error) {% for error in errors.all %}
<li>{{ $error }}</li> <li>{{ error }}</li>
@endforeach {% endfor %}
</ul> </ul>
</div> </div>
@endif {% endif %}
<form role="form" id="register" method="POST" action="/auth/register"> <form role="form" id="register" method="POST" action="/auth/register">
<input type="hidden" name="_token" value="{{ csrf_token() }}"> <input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group"> <div class="form-group">
<label class="control-label">E-Mail</label> <label class="control-label">E-Mail</label>
<input type="email" class="form-control" placeholder="E-Mail" name="email" value="{{ old('email') }}"> <input type="email" class="form-control" placeholder="E-Mail" name="email" value="{{ old.email }}">
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -55,5 +55,4 @@
</div> </div>
</div> </div>
</div> </div>
</div> {% endblock %}
@endsection

View File

@@ -1,7 +1,7 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'store','url' => route('bills.store')]) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'store','url' : route('bills.store')}) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="col-lg-6 col-md-12 col-sm-6">
@@ -11,19 +11,15 @@
<i class="fa fa-exclamation-circle"></i> Mandatory fields <i class="fa fa-exclamation-circle"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::text('name') !!} {{ ExpandedForm.text('name') }}
{!! ExpandedForm::tags('match') !!} {{ ExpandedForm.tags('match') }}
{!! ExpandedForm::amount('amount_min') !!} {{ ExpandedForm.amount('amount_min') }}
{!! ExpandedForm::amount('amount_max') !!} {{ ExpandedForm.amount('amount_max') }}
{!! ExpandedForm::date('date',Carbon\Carbon::now()->addDay()->format('Y-m-d')) !!} {{ ExpandedForm.date('date',phpdate('Y-m-d')) }}
{!! ExpandedForm::select('repeat_freq',$periods,'monthly') !!} {{ ExpandedForm.select('repeat_freq',periods,'monthly') }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new bill
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="col-lg-6 col-md-12 col-sm-6">
<!-- panel for optional fields --> <!-- panel for optional fields -->
@@ -32,9 +28,9 @@
<i class="fa fa-smile-o"></i> Optional fields <i class="fa fa-smile-o"></i> Optional fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::integer('skip',0) !!} {{ ExpandedForm.integer('skip',0) }}
{!! ExpandedForm::checkbox('automatch',1,true) !!} {{ ExpandedForm.checkbox('automatch',1,true) }}
{!! ExpandedForm::checkbox('active',1,true) !!} {{ ExpandedForm.checkbox('active',1,true) }}
</div> </div>
</div> </div>
@@ -44,20 +40,30 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('create','bill') !!} {{ ExpandedForm.optionsList('create','bill') }}
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new bill
</button>
</p>
</div>
</div> </div>
{!! Form::close() !!} </form>
{% endblock %}
@stop {% block styles %}
@section('styles')
<link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all">
@stop {% endblock %}
@section('scripts') {% block scripts %}
<script type="text/javascript" src="js/bootstrap-tagsinput.min.js"></script> <script type="text/javascript" src="js/bootstrap-tagsinput.min.js"></script>
@stop {% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }}
{{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('bills.destroy',bill.id)}) }}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red">
<div class="panel-heading">
Delete bill "{{ bill.name }}"
</div>
<div class="panel-body">
<p>
Are you sure that you want to delete bill "{{ bill.name }}"?
</p>
{% if bill.transactionjournals|length > 0 %}
<p class="text-info">
Bill "{{ bill.name }}" still has {{ bill.transactionjournals|length }} transactions connected
to it. These will <strong>not</strong> be removed but will lose their connection to this bill.
</p>
{% endif %}
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{ URL.previous() }}" class="btn-default btn">Cancel</a >
</p>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@@ -1,43 +1,37 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $bill) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }}
{!! Form::model($bill, ['class' => 'form-horizontal','id' => 'update','url' => route('bills.update', $bill->id)]) !!} {{ Form.model(bill, {'class' : 'form-horizontal','id' : 'update','url' : route('bills.update', bill.id)}) }}
<input type="hidden" name="id" value="{{$bill->id}}" /> <input type="hidden" name="id" value="{{bill.id}}" />
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="col-lg-6 col-md-12 col-sm-6">
<!-- panel for mandatory fields -->
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-exclamation-circle"></i> Mandatory fields <i class="fa fa-exclamation-circle"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::text('name') !!} {{ ExpandedForm.text('name') }}
{!! ExpandedForm::tags('match') !!} {{ ExpandedForm.tags('match') }}
{!! ExpandedForm::amount('amount_min') !!} {{ ExpandedForm.amount('amount_min') }}
{!! ExpandedForm::amount('amount_max') !!} {{ ExpandedForm.amount('amount_max') }}
{!! ExpandedForm::date('date',$bill->date->format('Y-m-d')) !!} {{ ExpandedForm.date('date',bill.date.format('Y-m-d')) }}
{!! ExpandedForm::select('repeat_freq',$periods) !!} {{ ExpandedForm.select('repeat_freq',periods) }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Update bill
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="col-lg-6 col-md-12 col-sm-6">
<!-- panel for optional fields -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields <i class="fa fa-smile-o"></i> Optional fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::integer('skip') !!} {{ ExpandedForm.integer('skip') }}
{!! ExpandedForm::checkbox('automatch',1) !!} {{ ExpandedForm.checkbox('automatch',1) }}
{!! ExpandedForm::checkbox('active',1) !!} {{ ExpandedForm.checkbox('active',1) }}
</div> </div>
</div> </div>
@@ -46,19 +40,27 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('update','bill') !!} {{ ExpandedForm.optionsList('update','bill') }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{!! Form::close() !!} <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Update bill
</button>
</p>
</div>
</div>
</form>
{% endblock %}
@stop {% block styles %}
@section('styles')
<link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all">
@stop {% endblock %}
@section('scripts') {% block scripts %}
<script type="text/javascript" src="js/bootstrap-tagsinput.min.js"></script> <script type="text/javascript" src="js/bootstrap-tagsinput.min.js"></script>
@stop {% endblock %}

View File

@@ -0,0 +1,27 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }}
<div class="row">
<div class="col-lg-12 col-sm-12 col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa {{ mainTitleIcon }}"></i> {{ title }}
<!-- ACTIONS MENU -->
<div class="pull-right">
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('bills.create')}}"><i class="fa fa-plus fa-fw"></i> New bill</a></li>
</ul>
</div>
</div>
</div>
{% include 'list/bills.twig' %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,25 +1,24 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $bill) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-sm-12 col-md-12"> <div class="col-lg-6 col-sm-12 col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-rotate-right"></i> {{{$bill->name}}} <i class="fa fa-rotate-right"></i> {{ bill.name }}
@if($bill->active) {% if bill.active %}
<i class="fa fa-check fa-fw" title="Active"></i> <i class="fa fa-check fa-fw" title="Active"></i>
@else {% else %}
<i class="fa fa-times fa-fw" title="Inactive"></i> <i class="fa fa-times fa-fw" title="Inactive"></i>
@endif {% endif %}
@if($bill->automatch) {% if bill.automatch %}
<i class="fa fa-check fa-fw" title="Automatically matched by Firefly"></i> <i class="fa fa-check fa-fw" title="Automatically matched by Firefly"></i>
@else {% else %}
<i class="fa fa-times fa-fw" title="Not automatically matched by Firefly"></i> <i class="fa fa-times fa-fw" title="Not automatically matched by Firefly"></i>
@endif {% endif %}
<!-- ACTIONS MENU -->
<div class="pull-right"> <div class="pull-right">
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown"> <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
@@ -27,8 +26,8 @@
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu pull-right" role="menu"> <ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('bills.edit',$bill->id)}}"><i class="fa fa-fw fa-pencil"></i> edit</a></li> <li><a href="{{route('bills.edit',bill.id)}}"><i class="fa fa-fw fa-pencil"></i> edit</a></li>
<li><a href="{{route('bills.delete',$bill->id)}}"><i class="fa fa-fw fa-trash-o"></i> delete</a></li> <li><a href="{{route('bills.delete',bill.id)}}"><i class="fa fa-fw fa-trash-o"></i> delete</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -39,21 +38,21 @@
<tr> <tr>
<td colspan="2"> <td colspan="2">
Matching on Matching on
@foreach(explode(',',$bill->match) as $word) {% for word in bill.match|split(',') %}
<span class="label label-info">{{{$word}}}</span> <span class="label label-info">{{ word }}</span>
@endforeach {% endfor %}
between {!! Amount::format($bill->amount_min) !!} and {!! Amount::format($bill->amount_max) !!}. between {{ bill.amount_min|formatAmount }} and {{ bill.amount_max|formatAmount }}.
Repeats {!! $bill->repeat_freq !!}.</td> Repeats {{ bill.repeat_freq }}.</td>
</tr> </tr>
<tr> <tr>
<td>Next expected match</td> <td>Next expected match</td>
<td> <td>
@if($bill->nextExpectedMatch) {% if bill.nextExpectedMatch %}
{{$bill->nextExpectedMatch->format('j F Y')}} {{bill.nextExpectedMatch.format('j F Y')}}
@else {% else %}
<em>Unknown</em> <em>Unknown</em>
@endif {% endif %}
</td> </td>
</tr> </tr>
</table> </table>
@@ -67,7 +66,7 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<p> <p>
<a href="{{route('bills.rescan',$bill->id)}}" class="btn btn-default">Rescan old transactions</a> <a href="{{route('bills.rescan',bill.id)}}" class="btn btn-default">Rescan old transactions</a>
</p> </p>
</div> </div>
</div> </div>
@@ -94,23 +93,21 @@
Connected transaction journals Connected transaction journals
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['sorting' => false]) {% include 'list/journals' %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}
@section('scripts') {% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
var billID = {{{$bill->id}}}; var billID = {{ bill.id }};
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script> </script>
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="js/gcharts.options.js"></script> <script type="text/javascript" src="js/gcharts.options.js"></script>
<script type="text/javascript" src="js/gcharts.js"></script> <script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript" src="js/bills.js"></script> <script type="text/javascript" src="js/bills.js"></script>
@stop {% endblock %}

View File

@@ -1,7 +1,7 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'store','url' => route('budgets.store')]) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'store','url' : route('budgets.store')}) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary"> <div class="panel panel-primary">
@@ -9,15 +9,9 @@
<i class="fa fa-exclamation"></i> Mandatory fields <i class="fa fa-exclamation"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::text('name') !!} {{ ExpandedForm.text('name') }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new budget
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<!-- panel for options --> <!-- panel for options -->
@@ -26,17 +20,26 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('create','budget') !!} {{ ExpandedForm.optionsList('create','budget') }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new budget
</button>
</p>
</div>
</div>
{!! Form::close() !!} </form>
@stop {% endblock %}

View File

@@ -1,28 +1,28 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $budget) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'destroy','url' => route('budgets.destroy',$budget->id)]) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('budgets.destroy',budget.id) }) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red"> <div class="panel panel-red">
<div class="panel-heading"> <div class="panel-heading">
Delete budget "{{{$budget->name}}}" Delete budget "{{ budget.name}}"
</div> </div>
<div class="panel-body"> <div class="panel-body">
<p> <p>
Are you sure that you want to delete budget "{{{$budget->name}}}"? Are you sure that you want to delete budget "{{ budget.name }}"?
</p> </p>
@if($budget->transactionjournals()->count() > 0) {% if budget.transactionjournals|length > 0 %}
<p class="text-info"> <p class="text-info">
Budget "{{{$budget->name}}}" still has {{$budget->transactionjournals()->count()}} transactions connected Budget "{{ budget.name }}" still has {{ budget.transactionjournals|length }} transactions connected
to it. These will <strong>not</strong> be removed but will lose their connection to this budget. to it. These will <strong>not</strong> be removed but will lose their connection to this budget.
</p> </p>
@endif {% endif %}
<p> <p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button> <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > <a href="{{URL.previous()}}" class="btn-default btn">Cancel</a >
</p> </p>
</div> </div>
</div> </div>
@@ -39,6 +39,5 @@
</div> </div>
</div> </div>
</form>
{!! Form::close() !!} {% endblock %}
@stop

View File

@@ -1,14 +1,14 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $budget) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget) }}
<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">
<p class="lead">Use budgets to organize and limit your expenses.</p> <p class="lead">Use budgets to organize and limit your expenses.</p>
</div> </div>
</div> </div>
{!! Form::model($budget, ['class' => 'form-horizontal','id' => 'update','url' => route('budgets.update',$budget->id)]) !!} {{ Form.model(budget, {'class' : 'form-horizontal','id' : 'update','url' : route('budgets.update',budget.id) } ) }}
<input type="hidden" name="id" value="{{$budget->id}}" /> <input type="hidden" name="id" value="{{budget.id}}" />
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="col-lg-6 col-md-12 col-sm-6">
<div class="panel panel-primary"> <div class="panel panel-primary">
@@ -16,15 +16,11 @@
<i class="fa fa-fw fa-exclamation"></i> Mandatory fields <i class="fa fa-fw fa-exclamation"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::checkbox('active') !!} {{ ExpandedForm.checkbox('active') }}
{!! ExpandedForm::text('name') !!} {{ ExpandedForm.text('name') }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-pencil"></i> Update budget
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="col-lg-6 col-md-12 col-sm-6">
<!-- panel for options --> <!-- panel for options -->
@@ -33,12 +29,21 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('update','budget') !!} {{ ExpandedForm.optionsList('update','budget') }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{!! Form::close() !!} <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-pencil"></i> Update budget
</button>
</p>
</div>
</div>
</form>
@stop {% endblock %}

View File

@@ -1,16 +1,16 @@
<form style="display: inline;" id="income" action="{{route('budgets.postIncome')}}" method="POST"> <form style="display: inline;" id="income" action="{{route('budgets.postIncome')}}" method="POST">
<input type="hidden" name="_token" value="{{ csrf_token() }}"
{!! Form::token() !!}
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Update (expected) available amount for {{Session::get('start', \Carbon\Carbon::now()->startOfMonth())->format('F Y')}}</h4> <h4 class="modal-title" id="myModalLabel">Update (expected) available amount for {{Session.get('start').format('F Y')}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"></div> <div class="input-group-addon">{{ getCurrencySymbol() }}</div>
<input step="any" class="form-control" id="amount" value="{{$amount->data}}" autocomplete="off" name="amount" type="number"> <input step="any" class="form-control" id="amount" value="{{ amount.data }}" autocomplete="off" name="amount" type="number">
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@@ -0,0 +1,192 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
<div class="row">
<div class="col-lg-9 col-sm-8 col-md-8">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-calendar fa-fw"></i>
{{ Session.get('start').format('F Y') }}
</div>
<div class="panel-body">
<div class="row">
<div class="col-lg-6 col-md-4 col-sm-3">
<small>Budgeted: <span id="budgetedAmount" data-value="300"></span></small>
</div>
<div class="col-lg-6 col-md-4 col-sm-3" style="text-align:right;">
<small>Available in {{ Session.get('start').format('F Y') }}:
<a href="#" class="updateIncome"><span id="totalAmount" data-value="{{ amount }}">{{ amount|formatAmount }}</span></a></small>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="progress progress-striped">
<div class="progress-bar progress-bar-info" id="progress-bar-default" role="progressbar" aria-valuenow="0" aria-valuemin="0"
aria-valuemax="100" style="width: 0;"></div>
<div class="progress-bar progress-bar-danger" id="progress-bar-danger" role="progressbar" aria-valuenow="0" aria-valuemin="0"
aria-valuemax="100" style="width: 0;"></div>
<div class="progress-bar progress-bar-warning" id="progress-bar-warning" role="progressbar" aria-valuenow="10" aria-valuemin="0"
aria-valuemax="100" style="width: 0;"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-4 col-sm-3">
<small>Spent: {{ spent|formatAmount }}</small>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="progress progress-striped">
{% if overspent %}
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="{{ spentPCT }}" aria-valuemin="0"
aria-valuemax="100" style="width: {{ spentPCT }}%;"></div>
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{ 100-spentPCT }}" aria-valuemin="0"
aria-valuemax="100" style="width: {{ 100-spentPCT }}%;"></div>
{% else %}
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{ spentPCT }}" aria-valuemin="0"
aria-valuemax="100" style="width: {{ spentPCT }}%;"></div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-4 col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-tags"></i>
Transactions without a budget
</div>
<div class="panel-body">
<p>
<a href="{{ route('budgets.noBudget') }}">Transactions without a budget in
{{ Session.get('start').format('F Y') }}.</a>
</p>
</div>
</div>
</div>
</div>
<div class="row">
{% for budget in budgets %}
<div class="col-lg-3 col-sm-4 col-md-6" style="height:180px;">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-tasks"></i>
{% if budget.currentRep %}
<a href="{{ route('budgets.show', [budget.id, budget.currentRep.id]) }}" id="budget-link-{{ budget.id }}">{{ budget.name }}</a>
{% else %}
<a href="{{ route('budgets.show',budget.id) }}" id="budget-link-{{ budget.id }}">{{ budget.name }}</a>
{% endif %}
<!-- ACTIONS MENU -->
<div class="pull-right">
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{ route('budgets.edit',budget.id) }}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li>
<li><a href="{{ route('budgets.delete',budget.id) }}"><i class="fa fa-trash fa-fw"></i> Delete</a></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<!-- the range in which the budget can be set -->
<p>
{% if budget.currentRep %}
<input type="range" data-id="{{ budget.id }}" data-spent="{{ budget.spent }}" id="budget-range-{{ budget.id }}"
max="{{ budgetMaximum }}" min="0" value="{{ budget.currentRep.amount }}"/>
{% else %}
<input type="range" data-id="{{ budget.id }}" data-spent="{{ budget.spent }}" id="budget-range-{{ budget.id }}"
max="{{ budgetMaximum }}" min="0" value="0"/>
{% endif %}
</p>
<!-- some textual info about the budget. Updates dynamically. -->
<p>
<!-- budget-holder-X holds all the elements -->
<span id="budget-holder-{{ budget.id }}">
{% if budget.currentRep %}
<!-- budget-description-X holds the description. -->
<span id="budget-description-{{ budget.id }}">Budgeted: </span>
<!-- budget-info-X holds the input and the euro-sign: -->
<span id="budget-info-{{ budget.id }}">
{% if budget.currentRep.amount > budget.spent %}
<span class="text-success">{{ getCurrencySymbol() }}</span> <input type="number" min="0" max="{{ budgetMaximum }}" data-id="{{ budget.id }}"
step="1" value="{{ budget.currentRep.amount }}"
style="width:90px;color:#3c763d;"/>
{% else %}
<span class="text-danger">{{ getCurrencySymbol() }}</span> <input type="number" min="0" max="{{ budgetMaximum }}" data-id="{{ budget.id }}"
step="1" value="{{ budget.currentRep.amount }}"
style="width:90px;color:#a94442;"/>
{% endif %}
</span>
{% else %}
<span id="budget-description-{{ budget.id }}"><em>No budget</em></span>
<span id="budget-info-{{ budget.id }}">
<span class="text-success" style="display:none;">{{ Amount.getCurrencySymbol() }}</span> <input data-id="{{ budget.id }}" type="number"
min="0" max="{{ budgetMaximum }}" step="1"
value="0"
style="width:50px;color:#3c763d;display:none;"/>
</span>
{% endif %}
</span>
</p>
<p>
<span id="spent-{{ budget.id }}" data-value="{{ budget.spent }}">Spent: {{ budget.spent|formatAmount }}</span>
</p>
</div>
</div>
</div>
{% endfor %}
<div class="col-lg-3 col-sm-4 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-plus-circle"></i>
Create budget
</div>
<div class="panel-body">
<a href="{{ route('budgets.create') }}" class="btn btn-success"><i class="fa fa-fw fa-plus"></i> Create new budget</a>
</div>
</div>
</div>
{% if inactive|length > 0 %}
<div class="col-lg-3 col-sm-4 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-minus-circle"></i>
Inactive budgets
</div>
<div class="panel-body">
{% for index,budget in inactive %}
{% if index != inactive|length-1 %}
<a href="{{ route('budgets.show',budget.id) }}">{{ budget.name }}</a>,
{% else %}
<a href="{{ route('budgets.show',budget.id) }}">{{ budget.name }}</a>
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% endif %}
</div>
<!-- DIALOG -->
<div class="modal fade" id="monthlyBudgetModal">
</div><!-- /.modal -->
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="js/budgets.js"></script>
{% endblock %}

View File

@@ -1,18 +1,17 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) }} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, subTitle) }}
<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="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{{{$subTitle}}} {{ subTitle }}
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['journals' => $list,'sorting' => false]) {% include 'list/journals.twig' with {'journals': list} %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %}
@stop

View File

@@ -0,0 +1,107 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName,budget, repetition) }}
<div class="row">
<div class="col-lg-9 col-md-9 col-sm-7">
<div class="panel panel-default">
<div class="panel-heading">
Overview
<!-- ACTIONS MENU -->
<div class="pull-right">
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{ route('budgets.edit',budget.id) }}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li>
<li><a href="{{ route('budgets.delete',budget.id) }}"><i class="fa fa-trash fa-fw"></i> Delete</a></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<div id="budgetOverview"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Transactions
</div>
{% include 'list/journals.twig' %}
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-5">
{% if limits|length == 1 %}
<p class="small text-center"><a href="{{ route('budgets.show',budget.id) }}">Show everything</a></p>
{% endif %}
{% for limit in limits %}
{% for rep in limit.limitRepetitions %}
<div class="panel panel-default">
<div class="panel-heading">
<a href="{{route('budgets.show',[budget.id,rep.id])}}">{{rep.startdate.format('F Y')}}</a>
</div>
<div class="panel-body">
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-6">
Amount: {{ rep.amount|formatAmount }}
</div>
<div class="col-lg-6 col-md-6 col-sm-6">
Spent: {{ spentInRepetition(rep)|formatAmount }}
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
{% set overspent = spentInRepetition(rep) > rep.amount %}
{% if overspent %}
{% set spent = spentInRepetition(rep) %}
{% set pct = (spent != 0 ? (rep.amount / spent)*100 : 0) %}
<div class="progress progress-striped">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{pct|round}}" aria-valuemin="0" aria-valuemax="100" style="width: {{pct|round}}%;"></div>
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{(100-pct)|round}}" aria-valuemin="0" aria-valuemax="100" style="width: {{(100-pct)|round}}%;"></div>
</div>
{% else %}
{% set amount = rep.amount %}
{% set pct = (amount != 0 ? (spentInRepetition(rep) / amount)*100 : 0) %}
<div class="progress progress-striped">
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{pct|round}}" aria-valuemin="0" aria-valuemax="100" style="width: {{pct|round}}%;"></div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endfor %}
{% if limits|length == 1 %}
<p class="small text-center"><a href="{{route('budgets.show',budget.id)}}">Show everything</a></p>
{% endif %}
</div>
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript">
var budgetID = {{budget.id}};
{% if repetition.id %}
var repetitionID = {{repetition.id}};
var year = {{repetition.startdate.format('Y')}};
{% else %}
var year = {{Session.get('start').format('Y')}};
{% endif %}
</script>
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="js/gcharts.options.js"></script>
<script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript" src="js/budgets.js"></script>
{% endblock %}

View File

@@ -1,7 +1,7 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'store','url' => route('categories.store')]) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'store','url' : route('categories.store')}) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
@@ -10,14 +10,9 @@
<i class="fa fa-exclamation"></i> Mandatory fields <i class="fa fa-exclamation"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::text('name') !!} {{ ExpandedForm.text('name') }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new category
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
@@ -28,14 +23,23 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('create','category') !!} {{ ExpandedForm.optionsList('create','category') }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new category
</button>
</p>
</div>
</div>
{!! Form::close() !!} </form>
{% endblock %}
@stop

View File

@@ -1,34 +1,33 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $category) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'destroy','url' => route('categories.destroy',$category->id)]) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('categories.destroy',category.id)}) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red"> <div class="panel panel-red">
<div class="panel-heading"> <div class="panel-heading">
Delete category "{{{$category->name}}}" Delete category "{{ category.name }}"
</div> </div>
<div class="panel-body"> <div class="panel-body">
<p> <p>
Are you sure that you want to delete category "{{$category->name}}"? Are you sure that you want to delete category "{{ category.name }}"?
</p> </p>
@if($category->transactionjournals()->count() > 0) {% if category.transactionjournals|length > 0 %}
<p class="text-info"> <p class="text-info">
Category "{{{$category->name}}}" still has {{$category->transactionjournals()->count()}} transactions connected Category "{{ category.name }}" still has {{ category.transactionjournals|length }} transactions connected
to it. These will <strong>not</strong> be removed but will lose their connection to this category. to it. These will <strong>not</strong> be removed but will lose their connection to this category.
</p> </p>
@endif {% endif %}
<p> <p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button> <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > <a href="{{ URL.previous() }}" class="btn-default btn">Cancel</a >
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</form>
{!! Form::close()!!} {% endblock %}
@stop

View File

@@ -1,8 +1,8 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $category) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }}
{!! Form::model($category, ['class' => 'form-horizontal','id' => 'update','url' => route('categories.update',$category->id)]) !!} {{ Form.model(category, {'class' : 'form-horizontal','id' : 'update','url' : route('categories.update',category.id)}) }}
<input type="hidden" name="id" value="{{$category->id}}" /> <input type="hidden" name="id" value="{{ category.id }}" />
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary"> <div class="panel panel-primary">
@@ -10,14 +10,10 @@
<i class="fa fa-exclamation"></i> Mandatory fields <i class="fa fa-exclamation"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::text('name') !!} {{ ExpandedForm.text('name') }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
Update category
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
@@ -28,12 +24,21 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('update','category') !!} {{ ExpandedForm.optionsList('update','category') }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
Update category
</button>
</p>
</div>
</div>
{!! Form::close() !!} </form>
@stop {% endblock %}

View File

@@ -1,11 +1,11 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
<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="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa {{{$mainTitleIcon}}}"></i> Categories <i class="fa {{ mainTitleIcon }}"></i> Categories
<!-- ACTIONS MENU --> <!-- ACTIONS MENU -->
<div class="pull-right"> <div class="pull-right">
@@ -15,30 +15,26 @@
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu pull-right" role="menu"> <ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('categories.create')}}"><i class="fa fa-plus fa-fw"></i> New category</a></li> <li><a href="{{ route('categories.create') }}"><i class="fa fa-plus fa-fw"></i> New category</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
@include('list.categories') {% include 'list/categories.twig' %}
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}
@section('styles') {% block styles %}
<link rel="stylesheet" href="css/bootstrap-sortable.css" type="text/css" media="all" /> <link rel="stylesheet" href="css/bootstrap-sortable.css" type="text/css" media="all" />
@stop {% endblock %}
@section('scripts') {% block scripts %}
<script type="text/javascript">
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script>
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="js/gcharts.options.js"></script> <script type="text/javascript" src="js/gcharts.options.js"></script>
<script type="text/javascript" src="js/gcharts.js"></script> <script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript" src="js/bootstrap-sortable.js"></script> <script type="text/javascript" src="js/bootstrap-sortable.js"></script>
<script type="text/javascript" src="js/categories.js"></script> <script type="text/javascript" src="js/categories.js"></script>
@stop {% endblock %}

View File

@@ -1,18 +1,18 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, subTitle) }}
<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="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
{{{$subTitle}}} {{ subTitle }}
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['journals' => $list,'sorting' => false]) {% include 'list/journals.twig' with {'journals': list} %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}

View File

@@ -1,6 +1,6 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $category) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default"> <div class="panel panel-default">
@@ -34,17 +34,16 @@
Transactions Transactions
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['sorting' => false]) {% include 'list/journals' %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}
@section('scripts') {% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
var categoryID = {{$category->id}}; var categoryID = {{ category.id }};
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script> </script>
<!-- load the libraries and scripts necessary for Google Charts: --> <!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script>
@@ -52,4 +51,4 @@
<script type="text/javascript" src="js/gcharts.js"></script> <script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript" src="js/categories.js"></script> <script type="text/javascript" src="js/categories.js"></script>
@stop {% endblock %}

View File

@@ -1,24 +1,20 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'store','route' => 'currency.store']) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'store','route' : 'currency.store'}) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> Mandatory fields <i class="fa {{ subTitleIcon }}"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::text('name',null,['maxlength' => 48]) !!} {{ ExpandedForm.text('name',null,{'maxlength' : 48}) }}
{!! ExpandedForm::text('symbol',null,['maxlength' => 8]) !!} {{ ExpandedForm.text('symbol',null,{'maxlength': 8}) }}
{!! ExpandedForm::text('code',null,['maxlength' => 3]) !!} {{ ExpandedForm.text('code',null,{'maxlength' : 3}) }}
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new currency
</button>
</p>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
@@ -29,12 +25,20 @@
<i class="fa fa-bolt"></i> Options <i class="fa fa-bolt"></i> Options
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::optionsList('create','currency') !!} {{ ExpandedForm.optionsList('create','currency') }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
{!! Form::close() !!} <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
@stop <p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new currency
</button>
</p>
</div>
</div>
</form>
{% endblock %}

View File

@@ -1,25 +1,26 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $currency) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, currency) }}
{!! Form::open(['class' => 'form-horizontal','id' => 'destroy','url' => route('currency.destroy',$currency->id)]) !!} {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('currency.destroy',currency.id)}) }}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red"> <div class="panel panel-red">
<div class="panel-heading"> <div class="panel-heading">
Delete currency "{{{$currency->name}}}" Delete currency "{{ currency.name }}"
</div> </div>
<div class="panel-body"> <div class="panel-body">
<p> <p>
Are you sure that you want to delete currency "{{{$currency->name}}}"? Are you sure that you want to delete currency "{{ currency.name }}"?
</p> </p>
<p> <p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button> <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > <a href="{{ URL.previous }}" class="btn-default btn">Cancel</a >
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{!! Form::close() !!} </form>
@stop
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "./layout/default.twig" %}
{% block content %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, currency) }}
{{ Form.model(currency, {'class' : 'form-horizontal','id' : 'update','url' : route('currency.update',currency.id)}) }}
<input type="hidden" name="id" value="{{currency.id}}" />
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{ subTitleIcon }}"></i> Mandatory fields
</div>
<div class="panel-body">
{{ ExpandedForm.text('name',null,{'maxlength' : 48}) }}
{{ ExpandedForm.text('symbol',null,{'maxlength' : 8}) }}
{{ ExpandedForm.text('code',null,{'maxlength' : 3}) }}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
{{ ExpandedForm.optionsList('update','currency') }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Update currency
</button>
</p>
</div>
</div>
</form>
{% endblock %}

View File

@@ -1,6 +1,6 @@
@extends('layouts.default') {% extends "./layout/default.twig" %}
@section('content') {% block content %}
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
<div class="row"> <div class="row">
<div class="col-lg-12 col-sm-12 col-md-12"> <div class="col-lg-12 col-sm-12 col-md-12">
@@ -13,37 +13,37 @@
Firefly III supports various currencies which you can set and enable here. Firefly III supports various currencies which you can set and enable here.
</p> </p>
<ul> <ul>
@if(count($currencies) > 0) {% if currencies|length > 0 %}
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th colspan="2">Currency</th> <th colspan="2">Currency</th>
</tr> </tr>
@foreach($currencies as $currency) {% for currency in currencies %}
<tr> <tr>
<td> <td>
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-xs">
<a class="btn btn-default" href="{{route('currency.edit',$currency->id)}}"><i class="fa fa-fw fa-pencil"></i></a> <a class="btn btn-default" href="{{route('currency.edit',currency.id)}}"><i class="fa fa-fw fa-pencil"></i></a>
<a class="btn btn-default" href="{{route('currency.delete',$currency->id)}}"><i class="fa fa-fw fa-trash"></i></a> <a class="btn btn-default" href="{{route('currency.delete',currency.id)}}"><i class="fa fa-fw fa-trash"></i></a>
</div> </div>
</td> </td>
<td>{{{$currency->name}}} ({{{$currency->code}}}) ({{{$currency->symbol}}})</td> <td>{{ currency.name }} ({{ currency.code }}) ({{ currency.symbol|raw }})</td>
<td> <td>
@if($currency->id == $defaultCurrency->id) {% if currency.id == defaultCurrency.id %}
<span class="label label-success">default</span> <span class="label label-success">default</span>
@else {% else %}
<a class="btn btn-info" href="{{route('currency.default',$currency->id)}}">make default</a> <a class="btn btn-info btn-xs" href="{{route('currency.default',currency.id)}}">make default</a>
@endif {% endif %}
</td> </td>
</tr> </tr>
@endforeach {% endfor %}
</table> </table>
@endif {% endif %}
<p><a class="btn btn-success" href="{{route('currency.create')}}"><i class="fa fa-fw fa-plus-circle"></i> Add another currency</a></p> <p><a class="btn btn-success" href="{{route('currency.create')}}"><i class="fa fa-fw fa-plus-circle"></i> Add another currency</a></p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@stop {% endblock %}

View File

@@ -0,0 +1 @@
Click here to reset your password: {{ url('password/reset/' . token) }}

Some files were not shown because too many files have changed in this diff Show More