Compare commits

...

92 Commits
3.1 ... 3.1.5

Author SHA1 Message Date
Sander Dorigo
83594e6f1f Merge branch 'release/3.1.5' 2014-11-09 11:09:38 +01:00
Sander Dorigo
0f6008705c Added a screenshot 2014-11-09 11:08:53 +01:00
Sander Dorigo
c58b653bb7 Updated read me file. 2014-11-09 11:01:57 +01:00
Sander Dorigo
f69b6f9b4e New chart for budget-overview. 2014-11-09 08:42:09 +01:00
Sander Dorigo
7750b06476 Added a "spent"-bar in budgets. 2014-11-09 08:02:47 +01:00
Sander Dorigo
873384a34b Built the 'show'-view for budgets. 2014-11-08 19:11:51 +01:00
Sander Dorigo
ac299e7279 More rounding. 2014-11-08 11:38:50 +01:00
Sander Dorigo
7895d7f5d0 Format amount. 2014-11-08 11:37:55 +01:00
Sander Dorigo
fe05d218fc Allow piggy bank edit/update. 2014-11-08 11:36:20 +01:00
Sander Dorigo
8196313ac0 Optimized queries. 2014-11-08 10:16:12 +01:00
Sander Dorigo
6d8f84654f Also add stuff not in budgets. 2014-11-08 09:15:03 +01:00
Sander Dorigo
ab4f34a96b Extended reports 2014-11-07 22:06:30 +01:00
Sander Dorigo
139d985904 All kinds of fixes and things. I should really start organizing. 2014-11-07 11:18:06 +01:00
Sander Dorigo
44705f0e18 Some bugfixes and cleanup. 2014-11-06 20:33:37 +01:00
Sander Dorigo
ddea7d696a Delete and update routines. 2014-11-06 07:38:15 +01:00
Sander Dorigo
f814f45e36 Test fix for budgeting. 2014-11-05 21:37:24 +01:00
Sander Dorigo
f7117d47c2 Whoops, bugfix. 2014-11-05 21:23:23 +01:00
Sander Dorigo
01b0a1058d Form fix. 2014-11-05 21:22:31 +01:00
Sander Dorigo
21f362c7b9 Lots of stuff for budgets, accounts and others! 2014-11-05 19:57:56 +01:00
Sander Dorigo
aaab7f8e0e Some fixes for budgets 2014-11-04 20:37:00 +01:00
Sander Dorigo
09e1f68c69 Font fix. 2014-11-02 21:26:41 +01:00
Sander Dorigo
03729aa5ae Chart cleanup 2014-11-02 21:24:50 +01:00
Sander Dorigo
ef39f31ea1 First reports. 2014-11-02 18:46:01 +01:00
Sander Dorigo
0f1437dd6a Delete piggy banks. 2014-11-02 16:47:01 +01:00
Sander Dorigo
03aac2f744 Implemented method stub. 2014-11-02 14:59:09 +01:00
Sander Dorigo
2f8b10e82c All kinds of new code, especially for the piggy banks. 2014-11-02 14:58:12 +01:00
Sander Dorigo
3231effd20 Cleanup and fix everything related to piggy banks. 2014-10-31 07:32:43 +01:00
Sander Dorigo
f7722c1189 Clean up piggy bank controller. 2014-10-30 19:26:52 +01:00
Sander Dorigo
70c2450ac4 Clean up the routes. 2014-10-30 19:26:43 +01:00
Sander Dorigo
2d5b0d0f99 Moved some stuff around. 2014-10-30 19:26:28 +01:00
Sander Dorigo
f0c0002a6d Some cleanup 2014-10-30 18:24:10 +01:00
Sander Dorigo
dd9f08d4fa New code for charts. 2014-10-30 18:12:27 +01:00
Sander Dorigo
de2e384225 Merge branch 'feature/google-charts' into develop
Conflicts:
	app/views/accounts/show.blade.php
2014-10-30 18:09:03 +01:00
Sander Dorigo
ffcd1fde0f Even more charts and tables. 2014-10-30 18:06:29 +01:00
Sander Dorigo
d5e1da5948 Some more stats on the piggy banks. 2014-10-29 12:39:15 +01:00
Sander Dorigo
ad479a5c7f Added some placeholders. 2014-10-29 12:33:19 +01:00
Sander Dorigo
0707603b63 Add buttons and a placeholder. 2014-10-29 12:26:26 +01:00
Sander Dorigo
2f9c383004 All new stufs! 2014-10-29 10:30:52 +01:00
Sander Dorigo
8ad0d7af93 Update views and CSS 2014-10-28 16:29:31 +01:00
Sander Dorigo
9b4391c0bf Initial set of code required for GCharts. 2014-10-28 16:29:24 +01:00
Sander Dorigo
da7802a0a4 New controller, updated composer. 2014-10-28 16:15:16 +01:00
Sander Dorigo
9c69949e8c Added edit and new buttons. 2014-10-28 15:59:10 +01:00
Sander Dorigo
b1d7a9451a Cleaner piggybank view. 2014-10-28 11:25:22 +01:00
Sander Dorigo
004488d453 New todo items. 2014-10-28 11:10:40 +01:00
Sander Dorigo
fc91372dd0 Merge branch 'feature/account-cleanup' into develop 2014-10-28 09:33:54 +01:00
Sander Dorigo
5970a9dc91 Merge branch 'master' of https://github.com/JC5/firefly-iii into feature/account-cleanup 2014-10-28 09:32:22 +01:00
Sander Dorigo
264cac4f9b Merge branch 'develop' of https://github.com/JC5/firefly-iii into feature/account-cleanup 2014-10-28 09:32:16 +01:00
Sander Dorigo
633328a965 Removed references to sankey. 2014-10-28 09:29:47 +01:00
Sander Dorigo
4d4b62a766 Add todo-text 2014-10-28 09:28:14 +01:00
Sander Dorigo
aeb2c7deeb Remove function. 2014-10-28 09:27:46 +01:00
Sander Dorigo
c323942d92 Add placeholder. 2014-10-28 09:27:36 +01:00
Sander Dorigo
a0afa25145 Cleanup and prep-work for new charts (will be a new feature). 2014-10-28 09:25:29 +01:00
Sander Dorigo
4533b46436 Updated menu logic 2014-10-28 06:00:41 +01:00
Sander Dorigo
e5f8db78f9 New views and layouts for the account controller. 2014-10-28 05:59:14 +01:00
Sander Dorigo
899f61671f All new library set for the account controller (and others). 2014-10-28 05:58:48 +01:00
Sander Dorigo
d84d88cc10 Completely revamped the account controller. 2014-10-28 05:58:32 +01:00
Sander Dorigo
97e7ac4052 New routes for accounts. 2014-10-28 05:58:10 +01:00
Sander Dorigo
ba4ffa44d2 Fixed a spelling error. 2014-10-27 21:28:58 +01:00
Sander Dorigo
07caeccf68 Code formatting and a reference to a new form element. 2014-10-27 21:28:30 +01:00
Sander Dorigo
d54832f61f Code formatting. 2014-10-27 21:28:15 +01:00
Sander Dorigo
b212753633 A new form field. 2014-10-27 21:27:45 +01:00
James Cole
f38d80cbf5 Update composer.json 2014-10-23 16:42:11 +02:00
James Cole
8bea1acd8e Update composer.json 2014-10-23 16:42:01 +02:00
Sander Dorigo
42458ce11d Merge branch 'release/3.1.4' 2014-10-14 07:32:00 +02:00
Sander Dorigo
aceb683d07 Remove chart when nothing to show. 2014-10-14 07:27:06 +02:00
Sander Dorigo
b7517b49ed Expand match to include expense account. 2014-10-14 07:24:59 +02:00
Sander Dorigo
849b711b79 Recycle code instead of copy pasting. 2014-10-14 07:24:41 +02:00
Sander Dorigo
25585b28c7 Removed "date in the future" demand. 2014-10-14 07:24:27 +02:00
Sander Dorigo
073da8fb2a Small layout changes. 2014-10-13 20:47:36 +02:00
Sander Dorigo
a787ff3f3c Merge branch 'release/3.1.3' 2014-10-13 19:13:44 +02:00
Sander Dorigo
733b6d7eb7 Expand the chart. 2014-10-13 18:50:37 +02:00
Sander Dorigo
36d8dee853 Building a chart for the recurring transactions. 2014-10-13 17:54:20 +02:00
Sander Dorigo
65a2e07d24 Cleaned out most of the "reminders" code. 2014-10-12 09:45:57 +02:00
Sander Dorigo
7c97c558ab No reference to reminders. 2014-10-12 09:41:10 +02:00
Sander Dorigo
a6bb61050c Remove piggy bank reminders. 2014-10-12 09:41:00 +02:00
Sander Dorigo
b184aa2315 No trigger for recurring transactions. 2014-10-12 09:39:40 +02:00
Sander Dorigo
e4595333e7 Do not destroy reminders since there aren't any. 2014-10-12 09:39:29 +02:00
Sander Dorigo
41dd139bde Trigger no longer fires or creates reminders for piggy banks. 2014-10-12 09:37:31 +02:00
Sander Dorigo
c577dd302a Removed unused reminder methods. 2014-10-12 09:36:24 +02:00
Sander Dorigo
0ab87de78b Removed unused routes. 2014-10-12 09:34:47 +02:00
Sander Dorigo
8a22509b41 Removed a no longer used view. 2014-10-12 09:34:30 +02:00
Sander Dorigo
b024c18441 Cleaned out the reminder controller. 2014-10-12 09:34:10 +02:00
Sander Dorigo
d9ac681a68 Bug fixes in recurring transaction matching. 2014-10-12 09:31:25 +02:00
Sander Dorigo
637a5579ec Small cleanup. 2014-10-12 09:29:19 +02:00
Sander Dorigo
4794156e80 Merge branch 'master' into develop 2014-10-12 08:20:31 +02:00
Sander Dorigo
5f4db7874c New view and what-not for feature. 2014-10-12 08:19:18 +02:00
Sander Dorigo
b4ea1839a5 Fixed some bugs in the recurring transaction match. 2014-10-12 08:03:35 +02:00
Sander Dorigo
6a6d889983 Merge branch 'release/3.1.1' 2014-10-12 07:39:27 +02:00
Sander Dorigo
287c2e7af8 Added the ability to change the range preference on the fly. 2014-10-12 07:33:45 +02:00
Sander Dorigo
0fe6acc8cf Merge branch 'feature/recurring-expand' into develop 2014-10-11 21:24:38 +02:00
Sander Dorigo
7d2dab7ca0 First set of code. 2014-10-11 21:23:31 +02:00
Sander Dorigo
f68c1aff26 Cleaned up the show view. 2014-10-11 18:52:24 +02:00
147 changed files with 7893 additions and 4733 deletions

View File

@@ -28,7 +28,6 @@ Everything is organised:
- Clear views that should show you how you're doing;
- Easy navigation through your records;
- Browse back and forth to see previous months or even years;
- Lots of help text in case you don't get it;
- Lots of charts because we all love them.
## Changes
@@ -36,6 +35,7 @@ Everything is organised:
Firefly III will feature, but does not feature yet:
- Financial reporting showing you how well you are doing;
- Lots of help text in case you don't get it;
- More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts
@@ -51,6 +51,10 @@ Some stuff has been removed:
- The nesting of budgets, categories and beneficiaries is removed because it was pretty pointless.
- Firefly will not encrypt the content of the (MySQL) tables. Old versions of Firefly had this capability but it sucks when searching, sorting and organizing entries.
## Screenshots
![Index](http://i.imgur.com/oUQ1UhU.png)
## Current state
I have the basics up and running. Test coverage is currently non-existent.

View File

@@ -43,7 +43,7 @@ return [
'Firefly\Helper\HelperServiceProvider',
'Firefly\Validation\ValidationServiceProvider',
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
'TwigBridge\ServiceProvider'
'Grumpydictator\Gchart\GchartServiceProvider',
],
'manifest' => storage_path() . '/meta',
'aliases' => [

View File

@@ -2,10 +2,14 @@
use Carbon\Carbon;
return [
'index_periods' => ['1D', '1W', '1M', '3M', '6M', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'piggybank_periods' => ['day', 'week', 'month', 'year'],
'periods_to_text' => [
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'piggybank_periods' => [
'week' => 'Week',
'month' => 'Month',
'year' => 'Year'
],
'periods_to_text' => [
'weekly' => 'A week',
'monthly' => 'A month',
'quarterly' => 'A quarter',
@@ -13,7 +17,7 @@ return [
'yearly' => 'A year',
],
'range_to_text' => [
'range_to_text' => [
'1D' => 'day',
'1W' => 'week',
'1M' => 'month',
@@ -21,7 +25,15 @@ return [
'6M' => 'half year',
'custom' => '(custom)'
],
'range_to_repeat_freq' => [
'range_to_name' => [
'1D' => 'one day',
'1W' => 'one week',
'1M' => 'one month',
'3M' => 'three months',
'6M' => 'six months',
'1Y' => 'one year',
],
'range_to_repeat_freq' => [
'1D' => 'weekly',
'1W' => 'weekly',
'1M' => 'monthly',

View File

@@ -1,31 +1,116 @@
<?php
use Firefly\Helper\Controllers\AccountInterface as AI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class AccountController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class AccountController extends \BaseController
class AccountController extends BaseController
{
protected $_repository;
protected $_accounts;
/**
* @param ARI $repository
* @param AI $accounts
*
*/
public function __construct(ARI $repository, AI $accounts)
public function __construct()
{
$this->_accounts = $accounts;
$this->_repository = $repository;
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts');
}
/**
* @param string $what
*
* @return View
* @throws FireflyException
*/
public function index($what = 'default')
{
switch ($what) {
default:
throw new FireflyException('Cannot handle account type "' . e($what) . '".');
break;
case 'asset':
View::share('subTitleIcon', 'fa-money');
View::share('subTitle', 'Asset accounts');
break;
case 'expense':
View::share('subTitleIcon', 'fa-shopping-cart');
View::share('subTitle', 'Expense accounts');
break;
case 'revenue':
View::share('subTitleIcon', 'fa-download');
View::share('subTitle', 'Revenue accounts');
break;
}
return View::make('accounts.index')->with('what', $what);
}
/**
* @param string $what
*
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
*/
public function json($what = 'default')
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Shared\Json\Json $json */
$json = App::make('FireflyIII\Shared\Json\Json');
$parameters = $json->dataTableParameters();
switch ($what) {
default:
throw new FireflyException('Cannot handle account type "' . e($what) . '".');
break;
case 'asset':
$accounts = $acct->getAssetAccounts($parameters);
$count = $acct->countAssetAccounts();
break;
case 'expense':
$accounts = $acct->getExpenseAccounts($parameters);
$count = $acct->countExpenseAccounts();
break;
case 'revenue':
$accounts = $acct->getRevenueAccounts($parameters);
$count = $acct->countRevenueAccounts();
break;
}
/*
* Output the set compatible with data tables:
*/
$return = [
'draw' => intval(Input::get('draw')),
'recordsTotal' => $count,
'recordsFiltered' => $accounts->count(),
'data' => [],
];
/*
* Loop the accounts:
*/
/** @var \Account $account */
foreach ($accounts as $account) {
$entry = [
'name' => ['name' => $account->name, 'url' => route('accounts.show', $account->id)],
'balance' => $account->balance(),
'id' => [
'edit' => route('accounts.edit', $account->id),
'delete' => route('accounts.delete', $account->id),
]
];
$return['data'][] = $entry;
}
return Response::jsoN($return);
}
/**
* @return \Illuminate\View\View
*/
@@ -43,52 +128,7 @@ class AccountController extends \BaseController
break;
}
return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with(
'what', $what
);
}
/**
* @return $this
*/
public function asset()
{
View::share('subTitleIcon', 'fa-money');
$accounts = $this->_repository->getOfTypes(['Asset account', 'Default account']);
return View::make('accounts.asset')->with('subTitle', 'Asset accounts')->with(
'accounts', $accounts
);
}
/**
* @return $this
*/
public function expense()
{
View::share('subTitleIcon', 'fa-shopping-cart');
$accounts = $this->_repository->getOfTypes(['Expense account', 'Beneficiary account']);
return View::make('accounts.expense')->with('subTitle', 'Expense accounts')->with(
'accounts', $accounts
);
}
/**
* @return $this
*/
public function revenue()
{
View::share('subTitleIcon', 'fa-download');
$accounts = $this->_repository->getOfTypes(['Revenue account']);
return View::make('accounts.revenue')->with('subTitle', 'Revenue accounts')->with(
'accounts', $accounts
);
return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with('what', $what);
}
/**
@@ -99,9 +139,9 @@ class AccountController extends \BaseController
public function delete(Account $account)
{
return View::make('accounts.delete')->with('account', $account)
->with(
'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
);
->with(
'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
);
}
/**
@@ -111,20 +151,67 @@ class AccountController extends \BaseController
*/
public function destroy(Account $account)
{
$type = $account->accountType->type;
$this->_repository->destroy($account);
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal');
/*
* Find the "initial balance account", should it exist:
*/
$initialBalance = $acct->findInitialBalanceAccount($account);
/*
* Get all the transaction journals that are part of this/these account(s):
*/
$journals = [];
if ($initialBalance) {
$transactions = $initialBalance->transactions()->get();
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
$journals[] = $transaction->transaction_journal_id;
}
}
/** @var \Transaction $transaction */
foreach ($account->transactions() as $transaction) {
$journals[] = $transaction->transaction_journal_id;
}
$journals = array_unique($journals);
/*
* Delete the journals. Should get rid of the transactions as well.
*/
foreach ($journals as $id) {
$journal = $jrnls->find($id);
$journal->delete();
}
/*
* Delete it
*/
if ($initialBalance) {
$acct->destroy($initialBalance);
}
$acct->destroy($account);
Session::flash('success', 'The account was deleted.');
switch ($type) {
case 'Asset account':
case 'Default account':
return Redirect::route('accounts.asset');
return Redirect::route('accounts.index', 'asset');
break;
case 'Expense account':
case 'Beneficiary account':
return Redirect::route('accounts.expense');
return Redirect::route('accounts.index', 'expense');
break;
case 'Revenue account':
return Redirect::route('accounts.revenue');
return Redirect::route('accounts.index', 'revenue');
break;
}
@@ -153,18 +240,23 @@ class AccountController extends \BaseController
break;
}
$openingBalance = $this->_accounts->openingBalanceTransaction($account);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
->with('subTitle', 'Edit ' . strtolower($account->accountType->type) . ' "' . $account->name . '"');
}
$openingBalance = $acct->openingBalanceTransaction($account);
Session::forget('prefilled');
if (!is_null($openingBalance)) {
$prefilled['openingbalancedate'] = $openingBalance->date->format('Y-m-d');
$prefilled['openingbalance'] = floatval($openingBalance->transactions()->where('account_id', $account->id)->first()->amount);
Session::flash('prefilled', $prefilled);
}
/**
* @return $this
*/
public function index()
{
return View::make('error')->with('message', 'This view has been disabled');
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)->with(
'subTitle', 'Edit ' . strtolower(
$account->accountType->type
) . ' "' . $account->name . '"'
);
}
/**
@@ -189,86 +281,116 @@ class AccountController extends \BaseController
}
$data = $this->_accounts->show($account, 40);
return View::make('accounts.show')->with('account', $account)->with('show', $data)->with(
'subTitle',
'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
);
//$data = $this->_accounts->show($account, 40);
return View::make('accounts.show')
->with('account', $account)
->with('subTitle', 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"');
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
* @throws FireflyException
*/
public function store()
{
$data = Input::all();
$data['what'] = isset($data['what']) && $data['what'] != '' ? $data['what'] : 'asset';
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
switch ($data['what']) {
switch ($data['post_submit_action']) {
default:
case 'asset':
$data['account_type'] = 'Asset account';
break;
case 'expense':
$data['account_type'] = 'Expense account';
break;
case 'revenue':
$data['account_type'] = 'Revenue account';
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $acct->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save account: ' . $messages['errors']->first());
return Redirect::route('accounts.create', $data['what'])->withInput()->withErrors($messages['errors']);
}
// store!
$acct->store($data);
Session::flash('success', 'New account stored!');
}
$account = $this->_repository->store($data);
if ($account->validate()) {
// saved! return to wherever.
Session::flash('success', 'Account "' . $account->name . '" created!');
if (intval(Input::get('create')) === 1) {
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('accounts.create', $data['what']);
} else {
return Redirect::route('accounts.index', $data['what']);
}
break;
case 'validate_only':
$messageBags = $acct->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('accounts.create', $data['what'])->withInput();
} else {
return Redirect::route('accounts.' . e($data['what']));
}
} else {
// did not save, return with error:
Session::flash('error', 'Could not save the new account: ' . $account->errors()->first());
return Redirect::route('accounts.create', $data['what'])->withErrors($account->errors())->withInput();
break;
}
}
/**
* @param Account $account
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return $this
* @throws FireflyException
*/
public function update(Account $account)
{
/** @var \Account $account */
$account = $this->_repository->update($account, Input::all());
if ($account->validate()) {
Session::flash('success', 'Account "' . $account->name . '" updated.');
switch ($account->accountType->type) {
case 'Asset account':
case 'Default account':
return Redirect::route('accounts.asset');
break;
case 'Expense account':
case 'Beneficiary account':
return Redirect::route('accounts.expense');
break;
case 'Revenue account':
return Redirect::route('accounts.revenue');
break;
}
} else {
Session::flash('error', 'Could not update account: ' . $account->errors()->first());
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$data = Input::except('_token');
return Redirect::route('accounts.edit', $account->id)->withInput()->withErrors($account->errors());
switch ($account->accountType->type) {
default:
throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"');
break;
case 'Default account':
$data['what'] = 'asset';
break;
case 'Beneficiary account':
$data['what'] = 'expense';
break;
case 'Revenue account':
$data['what'] = 'revenue';
break;
}
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'create_another':
case 'update':
$messages = $acct->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save account: ' . $messages['errors']->first());
return Redirect::route('accounts.edit', $account->id)->withInput()->withErrors($messages['errors']);
}
// store!
$acct->update($account, $data);
Session::flash('success', 'Account updated!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('accounts.edit', $account->id);
} else {
return Redirect::route('accounts.index',$data['what']);
}
case 'validate_only':
$messageBags = $acct->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('accounts.edit', $account->id)->withInput();
break;
}
}

View File

@@ -4,72 +4,175 @@ use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Firefly\Helper\Controllers\BudgetInterface as BI;
use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\MessageBag;
/**
* Class BudgetController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
*/
class BudgetController extends BaseController
{
protected $_budgets;
protected $_repository;
/**
* @param BI $budgets
* @param BRI $repository
*/
public function __construct(BI $budgets, BRI $repository)
public function __construct()
{
$this->_budgets = $budgets;
$this->_repository = $repository;
View::share('title', 'Budgets');
View::share('mainTitleIcon', 'fa-tasks');
}
public function nobudget($view = 'session') {
switch($view) {
default:
throw new FireflyException('Cannot show transactions without a budget for view "'.$view.'".');
break;
case 'session':
$start = Session::get('start');
$end = Session::get('end');
break;
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function postUpdateIncome()
{
/** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */
$preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface');
$date = Session::get('start');
// Add expenses that have no budget:
$set = \Auth::user()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget');
}
)->before($end)->after($start)->get();
return View::make('budgets.nobudget')
->with('view', $view)
->with('transactions',$set)
->with('subTitle', 'Transactions without a budget');
$value = intval(Input::get('amount'));
$preferences->set('budgetIncomeTotal' . $date->format('FY'), $value);
return Redirect::route('budgets.index');
}
/**
* @return $this|\Illuminate\View\View
* Update the amount for a budget's limitrepetition and/or create it.
*
* @param Budget $budget
*/
public function amount(Budget $budget)
{
$amount = intval(Input::get('amount'));
$date = Session::get('start');
/** @var \Limit $limit */
$limit = $budget->limits()->where('startdate', $date->format('Y-m-d'))->first();
if (!$limit) {
// create one!
$limit = new Limit;
$limit->budget()->associate($budget);
$limit->startdate = $date;
$limit->amount = $amount;
$limit->repeat_freq = 'monthly';
$limit->repeats = 0;
$limit->save();
Event::fire('limits.store', [$limit]);
} else {
if ($amount > 0) {
$limit->amount = $amount;
$limit->save();
Event::fire('limits.update', [$limit]);
} else {
$limit->delete();
}
}
// try to find the limit repetition for this limit:
$repetition = $limit->limitrepetitions()->first();
if ($repetition) {
return Response::json(['name' => $budget->name, 'repetition' => $repetition->id]);
} else {
return Response::json(['name' => $budget->name, 'repetition' => null]);
}
}
public function index()
{
/** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */
$preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface');
$date = Session::get('start');
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$budgets = $repos->get();
// get the limits for the current month.
$date = \Session::get('start');
$spent = 0;
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$budget->spent = $repos->spentInMonth($budget, $date);
$spent += $budget->spent;
$budget->pct = 0;
$budget->limit = 0;
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
if ($repetition->startdate == $date) {
$budget->currentRep = $repetition;
$budget->limit = floatval($repetition->amount);
if ($budget->limit > $budget->spent) {
// not overspent:
$budget->pct = 30;
} else {
$budget->pct = 50;
}
}
}
}
}
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
$amount = floatval($budgetAmount->data);
$overspent = $spent > $amount;
if($overspent) {
// overspent on total amount
$spentPCT = ceil($amount / $spent * 100);
} else {
// not overspent on total amount.
$spentPCT = ceil($spent / $amount * 100);
}
return View::make('budgets.index', compact('budgets','spent','spentPCT','overspent'))->with('budgetAmount', $budgetAmount);
}
/**
* @return $this
*/
public function updateIncome()
{
$date = Session::get('start');
/** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */
$preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface');
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
return View::make('budgets.income')->with('amount', $budgetAmount)->with('date', $date);
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Illuminate\View\View
*/
public function show(Budget $budget, LimitRepetition $repetition = null)
{
if (!is_null($repetition) && $repetition->limit->budget->id != $budget->id) {
App::abort(500);
}
if (is_null($repetition)) {
// get all other repetitions:
$limits = $budget->limits()->orderBy('startdate', 'DESC')->get();
} else {
// get nothing? i dunno
$limits = [$repetition->limit];
}
return View::make('budgets.show', compact('limits', 'budget', 'repetition'));
}
/**
* @return $this
*/
public function create()
{
$periods = \Config::get('firefly.periods_to_text');
return View::make('budgets.create')->with('periods', $periods)->with('subTitle', 'Create a new budget');
return View::make('budgets.create')->with('subTitle', 'Create a new budget');
}
/**
@@ -79,27 +182,17 @@ class BudgetController extends BaseController
*/
public function delete(Budget $budget)
{
return View::make('budgets.delete')->with('budget', $budget)
->with('subTitle', 'Delete budget "' . $budget->name . '"');
return View::make('budgets.delete')->with('budget', $budget)->with('subTitle', 'Delete budget "' . $budget->name . '"');
}
/**
* @param Budget $budget
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Budget $budget)
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
// remove budget
Event::fire('budgets.destroy', [$budget]); // just before deletion.
$this->_repository->destroy($budget);
$repos->destroy($budget);
Session::flash('success', 'The budget was deleted.');
// redirect:
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
}
return Redirect::route('budgets.index.budget');
return Redirect::route('budgets.index');
}
@@ -110,142 +203,96 @@ class BudgetController extends BaseController
*/
public function edit(Budget $budget)
{
return View::make('budgets.edit')->with('budget', $budget)
->with('subTitle', 'Edit budget "' . $budget->name . '"');
Session::flash('prefilled', ['name' => $budget->name]);
return View::make('budgets.edit')->with('budget', $budget)->with('subTitle', 'Edit budget "' . $budget->name . '"');
}
/**
* @return $this|\Illuminate\View\View
*/
public function indexByBudget()
{
View::share('subTitleIcon', 'fa-folder-open');
$budgets = $this->_repository->get();
return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon)
->with('subTitle', 'Grouped by budget');
}
/**
* @return $this
*/
public function indexByDate()
{
View::share('subTitleIcon', 'fa-calendar');
// get a list of dates by getting all repetitions:
$set = $this->_repository->get();
$budgets = $this->_budgets->organizeByDate($set);
return View::make('budgets.indexByDate')->with('budgets', $budgets)
->with('subTitle', 'Grouped by date');
}
/**
* Three use cases for this view:
*
* - Show everything.
* - Show a specific repetition.
* - Show everything shows NO repetition.
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return int
*/
public function show(Budget $budget, \LimitRepetition $repetition = null)
{
$useSessionDates = Input::get('useSession') == 'true' ? true : false;
$view = null;
$title = null;
\Log::debug('Is envelope true? ' . (Input::get('noenvelope') == 'true'));
switch (true) {
case (!is_null($repetition)):
$data = $this->_budgets->organizeRepetition($repetition);
$view = 1;
$title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf(
$repetition->limit->amount,
false
);
break;
case (Input::get('noenvelope') == 'true'):
$data = $this->_budgets->outsideRepetitions($budget);
$view = 2;
$title = $budget->name . ', transactions outside an envelope';
break;
default:
$data = $this->_budgets->organizeRepetitions($budget, $useSessionDates);
$view = $useSessionDates ? 3 : 4;
$title = $useSessionDates ? $budget->name . ' in session period' : $budget->name;
break;
}
return View::make('budgets.show')
->with('budget', $budget)
->with('repetitions', $data)
->with('view', $view)
->with('highlight', Input::get('highlight'))
->with('useSessionDates', $useSessionDates)
->with('subTitle', 'Overview for ' . $title);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function store()
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
$budget = $this->_repository->store(Input::all());
if ($budget->validate()) {
Event::fire('budgets.store', [$budget]);
Session::flash('success', 'Budget created!');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
return Redirect::route('budgets.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New budget stored!');
if (Input::get('create') == '1') {
return Redirect::route('budgets.create', ['from' => Input::get('from')]);
}
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not save the new budget');
return Redirect::route('budgets.create')->withInput()->withErrors($budget->errors());
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('budgets.create');
} else {
return Redirect::route('budgets.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.create')->withInput();
break;
}
}
/**
* @param Budget $budget
*
* @return $this|\Illuminate\Http\RedirectResponse
* @throws FireflyException
*/
public function update(Budget $budget)
{
$budget = $this->_repository->update($budget, Input::all());
if ($budget->validate()) {
Event::fire('budgets.update', [$budget]);
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not update budget: ' . $budget->errors()->first());
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($budget->errors());
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'create_another':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($budget, $data);
Session::flash('success', 'Budget updated!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('budgets.edit', $budget->id);
} else {
return Redirect::route('budgets.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.edit', $budget->id)->withInput();
break;
}
}
}
}

View File

@@ -1,8 +1,10 @@
<?php
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Firefly\Helper\Controllers\ChartInterface;
use Firefly\Storage\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
/**
* Class ChartController
@@ -18,7 +20,7 @@ class ChartController extends BaseController
/**
* @param ChartInterface $chart
* @param ChartInterface $chart
* @param AccountRepositoryInterface $accounts
*/
public function __construct(ChartInterface $chart, AccountRepositoryInterface $accounts)
@@ -57,13 +59,13 @@ class ChartController extends BaseController
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' => 'All envelopes',
'series' => [
'subtitle' => 'All envelopes',
'series' => [
[
'type' => 'line',
'type' => 'line',
'yAxis' => 1,
'name' => 'Amount in envelope',
'data' => $envelope
'name' => 'Amount in envelope',
'data' => $envelope
],
[
'type' => 'column',
@@ -71,10 +73,10 @@ class ChartController extends BaseController
'data' => $expense
],
[
'type' => 'line',
'type' => 'line',
'yAxis' => 1,
'name' => 'Spent percentage for envelope',
'data' => $left
'name' => 'Spent percentage for envelope',
'data' => $left
]
@@ -111,14 +113,14 @@ class ChartController extends BaseController
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' =>
'subtitle' =>
'Between ' . $rep->startdate->format('M jS, Y') . ' and ' . $rep->enddate->format('M jS, Y'),
'series' => [
'series' => [
[
'type' => 'column',
'name' => 'Expenses per day',
'type' => 'column',
'name' => 'Expenses per day',
'yAxis' => 1,
'data' => $expense
'data' => $expense
],
[
'type' => 'line',
@@ -175,8 +177,8 @@ class ChartController extends BaseController
}
$return = [
'chart_title' => 'Overview for ' . $budget->name,
'subtitle' => 'Not organized by an envelope',
'series' => [
'subtitle' => 'Not organized by an envelope',
'series' => [
[
'type' => 'column',
'name' => 'Expenses per day',
@@ -245,11 +247,11 @@ class ChartController extends BaseController
// create a serie for the repetition.
$currentSerie = [
'type' => 'spline',
'id' => 'rep-' . $repetition->id,
'type' => 'spline',
'id' => 'rep-' . $repetition->id,
'yAxis' => 1,
'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(),
'data' => []
'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(),
'data' => []
];
$current = clone $repetition->startdate;
while ($current <= $repetition->enddate) {
@@ -271,11 +273,11 @@ class ChartController extends BaseController
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' =>
'subtitle' =>
'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format(
'M jS, Y'
),
'series' => $series
'series' => $series
];
return Response::json($return);
@@ -296,8 +298,8 @@ class ChartController extends BaseController
$serie = $this->_chart->categoryShowChart($category, $range, $start, $end);
$data = [
'chart_title' => $category->name,
'subtitle' => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>',
'series' => $serie
'subtitle' => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>',
'series' => $serie
];
return Response::json($data);
@@ -336,8 +338,8 @@ class ChartController extends BaseController
'<a href="' . route('accounts.index') . '">View more</a>';
$data = [
'chart_title' => count($accounts) == 1 ? $accounts[0]->name : 'All accounts',
'subtitle' => $url,
'series' => []
'subtitle' => $url,
'series' => []
];
foreach ($accounts as $account) {
@@ -431,11 +433,11 @@ class ChartController extends BaseController
if ($limit > 0 || $expenses > 0) {
$data['labels'][] = $budget->name;
$data['series'][0]['data'][] = [
'y' => $limit,
'y' => $limit,
'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter
];
$data['series'][1]['data'][] = [
'y' => $expenses,
'y' => $expenses,
'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter
];
}
@@ -444,26 +446,26 @@ class ChartController extends BaseController
$set = \Auth::user()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget');
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget');
}
)->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->sum('amount');
// This can be debugged by using get(['transaction_journals.*','transactions.amount']);
$data['labels'][] = 'No budget';
$data['series'][0]['data'][] = [
'y' => 0,
'url' => route('budgets.nobudget','session')
'y' => 0,
'url' => route('budgets.nobudget', 'session')
];
$data['series'][1]['data'][] = [
'y' => floatval($set) * -1,
'url' => route('budgets.nobudget','session')
'y' => floatval($set) * -1,
'url' => route('budgets.nobudget', 'session')
];
return Response::json($data);
@@ -482,4 +484,120 @@ class ChartController extends BaseController
}
/**
* This method checks all recurring transactions, calculates the current "moment" and returns
* a list of yet to be paid (and paid) recurring transactions. This list can be used to show a beautiful chart
* to the end user who will love it and cherish it.
*
* @throws FireflyException
*/
public function homeRecurring()
{
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
/*
* Set of paid transaction journals.
* Set of unpaid recurring transactions.
*/
$paid = [];
$unpaid = [];
/*
* Loop the recurring transactions.
*/
/** @var \RecurringTransaction $recurring */
foreach (\Auth::user()->recurringtransactions()->get() as $recurring) {
/*
* Start another loop starting at the $date.
*/
$start = clone $recurring->date;
$end = Carbon::now();
/*
* The jump we make depends on the $repeat_freq
*/
$current = clone $start;
while ($current <= $end) {
/*
* Get end of period for $current:
*/
$currentEnd = clone $current;
$toolkit->endOfPeriod($currentEnd, $recurring->repeat_freq);
/*
* In the current session range?
*/
if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) {
/*
* Lets see if we've already spent money on this recurring transaction (it hath recurred).
*/
/** @var TransactionJournal $set */
$transaction = \Auth::user()->transactionjournals()->where('recurring_transaction_id', $recurring->id)->after($current)->before($currentEnd)->first();
if(is_null($transaction)) {
$unpaid[] = $recurring;
} else {
$paid[] = $transaction;
}
}
/*
* Add some time for the next loop!
*/
$toolkit->addPeriod($current, $recurring->repeat_freq, intval($recurring->skip));
}
}
/*
* Get some colors going.
*/
$unPaidColours = $toolkit->colorRange('AA4643', 'FFFFFF', count($unpaid) == 0 ? 1 : count($unpaid));
$paidColours = $toolkit->colorRange('4572A7', 'FFFFFF', count($paid) == 0 ? 1 : count($paid));
/*
* The chart serie:
*/
$serie = [
'type' => 'pie',
'name' => 'Amount',
'data' => []
];
/*
* Loop paid and unpaid to make two haves for a pie chart.
*/
/** @var TransactionJournal $entry */
foreach ($paid as $index => $entry) {
$transactions = $entry->transactions()->get();
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));
$serie['data'][] = [
'name' => 'Already paid "'.$entry->description.'"',
'y' => $amount,
'url' => route('transactions.show',$entry->id),
'objType' => 'paid',
'color' => $paidColours[$index]
];
}
/** @var RecurringTransaction $entry */
foreach ($unpaid as $index => $entry) {
$amount = (floatval($entry->amount_max) + floatval($entry->amount_min)) / 2;
$serie['data'][] = [
'name' => 'Still due: '.$entry->name,
'y' => $amount,
'url' => route('recurring.show',$entry->id),
'objType' => 'unpaid',
'color' => $unPaidColours[$index]
];
}
return Response::json([$serie]);
}
}

View File

@@ -0,0 +1,600 @@
<?php
use Carbon\Carbon;
/**
* Class GoogleChartController
*/
class GoogleChartController extends BaseController
{
/**
* This method renders the b
*/
public function allAccountsBalanceChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day of the month', 'date');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$pref = $preferences->get('frontpageAccounts');
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$accounts = $acct->getByIds($pref->data);
/*
* Add a column for each account.
*/
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
}
/*
* Loop the date, then loop the accounts, then add balance.
*/
$start = Session::get('start');
$end = Session::get('end');
$current = clone $start;
while ($end >= $current) {
$row = [clone $current];
foreach ($accounts as $account) {
//if ($current > Carbon::now()) {
// $row[] = 0;
//} else {
$row[] = $account->balance($current);
//}
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExp($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
// total income:
$income = $tj->getSumOfIncomesByMonth($start);
$expense = $tj->getSumOfExpensesByMonth($start);
$chart->addRow(clone $start, $income, $expense);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExpSum($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
$end = clone $start;
$end->endOfYear();
$income = 0;
$expense = 0;
$count = 0;
while ($start < $end) {
// total income:
$income += $tj->getSumOfIncomesByMonth($start);
$expense += $tj->getSumOfExpensesByMonth($start);
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function budgetsReportChart($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
$budgets = $bdt->get();
$chart->addColumn('Month', 'date');
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$chart->addColumn('No budget','number');
/*
* Loop budgets this year.
*/
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach($budgets as $budget) {
$row[] = $bdt->spentInMonth($budget, $start);
}
/*
* Without a budget:
*/
$endOfMonth = clone $start;
$endOfMonth->endOfMonth();
$set = $bdt->transactionsWithoutBudgetInDateRange($start, $endOfMonth);
$row[] = floatval($set->sum('amount')) * -1;
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
public function budgetsAndSpending(Budget $budget, $year) {
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$end = clone $start;
$end->endOfYear();
while($start <= $end) {
$spent = $bdt->spentInMonth($budget, $start);
$repetition = $bdt->repetitionOnStartingOnDate($budget, $start);
if($repetition) {
$budgeted = floatval($repetition->amount);
} else {
$budgeted = 0;
}
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function allBudgetsHomeChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
$budgets = $bdt->get();
/*
* Loop budgets:
*/
/** @var Budget $budget */
foreach ($budgets as $budget) {
/*
* Is there a repetition starting on this particular date? We can use that.
*/
/** @var \LimitRepetition $repetition */
$repetition = $bdt->repetitionOnStartingOnDate($budget, Session::get('start'));
/*
* If there is, use it. Otherwise, forget it.
*/
if (is_null($repetition)) {
// use the session start and end for our search query
$searchStart = Session::get('start');
$searchEnd = Session::get('end');
// the limit is zero:
$limit = 0;
} else {
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
// the limit is the repetitions limit:
$limit = floatval($repetition->amount);
}
/*
* No matter the result of the search for the repetition, get all the transactions associated
* with the budget, and sum up the expenses made.
*/
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name, $limit, $expenses);
}
}
/*
* Finally, get all transactions WITHOUT a budget and add those as well.
* (yes this method is oddly specific).
*/
$noBudgetSet = $bdt->transactionsWithoutBudgetInDateRange(Session::get('start'), Session::get('end'));
$sum = $noBudgetSet->sum('amount') * -1;
$chart->addRow('No budget', 0, $sum);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function allCategoriesHomeChart()
{
$data = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/*
* Get the journals:
*/
$journals = $tj->getInDateRange(Session::get('start'), Session::get('end'));
/** @var \TransactionJournal $journal */
foreach ($journals as $journal) {
if ($journal->transactionType->type == 'Withdrawal') {
$amount = $journal->getAmount();
$category = $journal->categories()->first();
if (!is_null($category)) {
if (isset($data[$category->name])) {
$data[$category->name] += $amount;
} else {
$data[$category->name] = $amount;
}
}
}
}
arsort($data);
foreach ($data as $key => $entry) {
$chart->addRow($key, $entry);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
* @throws \Firefly\Exception\FireflyException
*/
public function recurringTransactionsOverview()
{
/*
* Set of paid transaction journals.
* Set of unpaid recurring transactions.
*/
$paid = [
'items' => [],
'amount' => 0
];
$unpaid = [
'items' => [],
'amount' => 0
];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
/** @var \FireflyIII\Database\Recurring $rcr */
$rcr = App::make('FireflyIII\Database\Recurring');
/** @var \FireflyIII\Shared\Toolkit\Date $dateKit */
$dateKit = App::make('FireflyIII\Shared\Toolkit\Date');
$recurring = $rcr->get();
/** @var \RecurringTransaction $entry */
foreach ($recurring as $entry) {
/*
* Start another loop starting at the $date.
*/
$start = clone $entry->date;
$end = Carbon::now();
/*
* The jump we make depends on the $repeat_freq
*/
$current = clone $start;
while ($current <= $end) {
/*
* Get end of period for $current:
*/
$currentEnd = clone $current;
$dateKit->endOfPeriod($currentEnd, $entry->repeat_freq);
/*
* In the current session range?
*/
if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) {
/*
* Lets see if we've already spent money on this recurring transaction (it hath recurred).
*/
/** @var TransactionJournal $set */
$journal = $rcr->getJournalForRecurringInRange($entry, $current, $currentEnd);
if (is_null($journal)) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += (($entry->amount_max + $entry->amount_min) / 2);
} else {
$amount = $journal->getAmount();
$paid['items'][] = $journal->description;
$paid['amount'] += $amount;
}
}
/*
* Add some time for the next loop!
*/
$dateKit->addPeriod($current, $entry->repeat_freq, intval($entry->skip));
}
}
/** @var \RecurringTransaction $entry */
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
*/
public function accountBalanceChart(Account $account)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
/*
* Loop the date, then loop the accounts, then add balance.
*/
$start = Session::get('start');
$end = Session::get('end');
$current = clone $start;
while ($end >= $current) {
$row = [clone $current];
if ($current > Carbon::now()) {
$row[] = null;
} else {
$row[] = $account->balance($current);
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountSankeyOutChart(Account $account)
{
// collect all relevant entries.
$set = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('From', 'string');
$chart->addColumn('To', 'string', 'domain');
$chart->addColumn('Weight', 'number');
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions', 'transactionjournal.budgets', 'transactionjournal.transactiontype',
'transactionjournal.categories']
)->before(Session::get('end'))->after(
Session::get('start')
)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = floatval($transaction->amount);
$type = $transaction->transactionJournal->transactionType->type;
if ($amount < 0 && $type != 'Transfer') {
// from account to a budget (if present).
$budgetName = isset($transaction->transactionJournal->budgets[0]) ? $transaction->transactionJournal->budgets[0]->name : '(no budget)';
$set[] = [$account->name, $budgetName, $amount * -1];
// from budget to category.
$categoryName = isset($transaction->transactionJournal->categories[0]) ? ' ' . $transaction->transactionJournal->categories[0]->name
: '(no cat)';
$set[] = [$budgetName, $categoryName, $amount * -1];
}
}
// loop the set, group everything together:
$grouped = [];
foreach ($set as $entry) {
$key = $entry[0] . $entry[1];
if (isset($grouped[$key])) {
$grouped[$key][2] += $entry[2];
} else {
$grouped[$key] = $entry;
}
}
// add rows to the chart:
foreach ($grouped as $entry) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountSankeyInChart(Account $account)
{
// collect all relevant entries.
$set = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('From', 'string');
$chart->addColumn('To', 'string', 'domain');
$chart->addColumn('Weight', 'number');
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions' => function ($q) {
$q->where('amount', '<', 0);
}, 'transactionjournal.budgets', 'transactionjournal.transactiontype', 'transactionjournal.categories']
)->before(Session::get('end'))->after(
Session::get('start')
)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = floatval($transaction->amount);
$type = $transaction->transactionJournal->transactionType->type;
if ($amount > 0 && $type != 'Transfer') {
$otherAccount = $transaction->transactionJournal->transactions[0]->account->name;
$categoryName = isset($transaction->transactionJournal->categories[0]) ? $transaction->transactionJournal->categories[0]->name
: '(no cat)';
$set[] = [$otherAccount, $categoryName, $amount];
$set[] = [$categoryName, $account->name, $amount];
}
}
// loop the set, group everything together:
$grouped = [];
foreach ($set as $entry) {
$key = $entry[0] . $entry[1];
if (isset($grouped[$key])) {
$grouped[$key][2] += $entry[2];
} else {
$grouped[$key] = $entry;
}
}
// add rows to the chart:
foreach ($grouped as $entry) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,240 @@
<?php
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
/**
* Class GoogleTableController
*/
class GoogleTableController extends BaseController
{
/**
* @param $what
*
* @throws FireflyException
*/
public function accountList($what)
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
switch ($what) {
default:
throw new FireflyException('Cannot handle "' . e($what) . '" in accountList.');
break;
case 'asset':
$list = $acct->getAssetAccounts();
break;
case 'expense':
$list = $acct->getExpenseAccounts();
break;
case 'revenue':
$list = $acct->getRevenueAccounts();
break;
}
$chart = App::make('gchart');
$chart->addColumn('ID', 'number');
$chart->addColumn('ID_Edit', 'string');
$chart->addColumn('ID_Delete', 'string');
$chart->addColumn('Name_URL', 'string');
$chart->addColumn('Name', 'string');
$chart->addColumn('Balance', 'number');
/** @var \Account $entry */
foreach ($list as $entry) {
$edit = route('accounts.edit', $entry->id);
$delete = route('accounts.delete', $entry->id);
$show = route('accounts.show', $entry->id);
$chart->addRow($entry->id, $edit, $delete, $show, $entry->name, $entry->balance());
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
*/
public function transactionsByBudget(Budget $budget, LimitRepetition $repetition = null)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('ID', 'number');
$chart->addColumn('ID_Edit', 'string');
$chart->addColumn('ID_Delete', 'string');
$chart->addColumn('Date', 'date');
$chart->addColumn('Description_URL', 'string');
$chart->addColumn('Description', 'string');
$chart->addColumn('Amount', 'number');
$chart->addColumn('From_URL', 'string');
$chart->addColumn('From', 'string');
$chart->addColumn('To_URL', 'string');
$chart->addColumn('To', 'string');
$chart->addColumn('Budget_URL', 'string');
$chart->addColumn('Budget', 'string');
$chart->addColumn('Category_URL', 'string');
$chart->addColumn('Category', 'string');
if (is_null($repetition)) {
$journals = $budget->transactionjournals()->with(['budgets', 'categories', 'transactions', 'transactions.account'])->orderBy('date', 'DESC')->get();
} else {
$journals = $budget->transactionjournals()->with(['budgets', 'categories', 'transactions', 'transactions.account'])->
after($repetition->startdate)->before($repetition->enddate)->orderBy('date', 'DESC')->get();
}
/** @var TransactionJournal $transaction */
foreach ($journals as $journal) {
$date = $journal->date;
$descriptionURL = route('transactions.show', $journal->id);
$description = $journal->description;
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
if (floatval($transaction->amount) > 0) {
$amount = floatval($transaction->amount);
$to = $transaction->account->name;
$toURL = route('accounts.show', $transaction->account->id);
} else {
$from = $transaction->account->name;
$fromURL = route('accounts.show', $transaction->account->id);
}
}
if (isset($journal->budgets[0])) {
$budgetURL = route('budgets.show', $journal->budgets[0]->id);
$budget = $journal->budgets[0]->name;
} else {
$budgetURL = '';
$budget = '';
}
if (isset($journal->categories[0])) {
$categoryURL = route('categories.show', $journal->categories[0]->id);
$category = $journal->categories[0]->name;
} else {
$categoryURL = '';
$category = '';
}
$id = $journal->id;
$edit = route('transactions.edit', $journal->id);
$delete = route('transactions.delete', $journal->id);
$chart->addRow(
$id, $edit, $delete, $date, $descriptionURL, $description, $amount, $fromURL, $from, $toURL, $to, $budgetURL, $budget, $categoryURL,
$category
);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
*/
public function transactionsByAccount(Account $account)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('ID', 'number');
$chart->addColumn('ID_Edit', 'string');
$chart->addColumn('ID_Delete', 'string');
$chart->addColumn('Date', 'date');
$chart->addColumn('Description_URL', 'string');
$chart->addColumn('Description', 'string');
$chart->addColumn('Amount', 'number');
$chart->addColumn('From_URL', 'string');
$chart->addColumn('From', 'string');
$chart->addColumn('To_URL', 'string');
$chart->addColumn('To', 'string');
$chart->addColumn('Budget_URL', 'string');
$chart->addColumn('Budget', 'string');
$chart->addColumn('Category_URL', 'string');
$chart->addColumn('Category', 'string');
/*
* Find transactions:
*/
$accountID = $account->id;
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions' => function ($q) use ($accountID) {
$q->where('account_id', '!=', $accountID);
}, 'transactionjournal.budgets', 'transactionjournal.transactiontype',
'transactionjournal.categories']
)->before(Session::get('end'))->after(
Session::get('start')
)->orderBy('date', 'DESC')->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$date = $transaction->transactionJournal->date;
$descriptionURL = route('transactions.show', $transaction->transaction_journal_id);
$description = $transaction->transactionJournal->description;
$amount = floatval($transaction->amount);
if ($transaction->transactionJournal->transactions[0]->account->id == $account->id) {
$opposingAccountURI = route('accounts.show', $transaction->transactionJournal->transactions[1]->account->id);
$opposingAccountName = $transaction->transactionJournal->transactions[1]->account->name;
} else {
$opposingAccountURI = route('accounts.show', $transaction->transactionJournal->transactions[0]->account->id);
$opposingAccountName = $transaction->transactionJournal->transactions[0]->account->name;
}
if (isset($transaction->transactionJournal->budgets[0])) {
$budgetURL = route('budgets.show', $transaction->transactionJournal->budgets[0]->id);
$budget = $transaction->transactionJournal->budgets[0]->name;
} else {
$budgetURL = '';
$budget = '';
}
if (isset($transaction->transactionJournal->categories[0])) {
$categoryURL = route('categories.show', $transaction->transactionJournal->categories[0]->id);
$category = $transaction->transactionJournal->categories[0]->name;
} else {
$categoryURL = '';
$category = '';
}
if ($amount < 0) {
$from = $account->name;
$fromURL = route('accounts.show', $account->id);
$to = $opposingAccountName;
$toURL = $opposingAccountURI;
} else {
$to = $account->name;
$toURL = route('accounts.show', $account->id);
$from = $opposingAccountName;
$fromURL = $opposingAccountURI;
}
$id = $transaction->transactionJournal->id;
$edit = route('transactions.edit', $transaction->transactionJournal->id);
$delete = route('transactions.delete', $transaction->transactionJournal->id);
$chart->addRow(
$id, $edit, $delete, $date, $descriptionURL, $description, $amount, $fromURL, $from, $toURL, $to, $budgetURL, $budget, $categoryURL, $category
);
}
// <th>Date</th>
// <th>Description</th>
// <th>Amount (&euro;)</th>
// <th>From</th>
// <th>To</th>
// <th>Budget / category</th>
// <th>ID</th>
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -1,7 +1,6 @@
<?php
use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
/**
@@ -14,24 +13,22 @@ class HomeController extends BaseController
protected $_accounts;
protected $_preferences;
protected $_journal;
protected $_reminders;
/**
* @param ARI $accounts
* @param PHI $preferences
* @param TJRI $journal
* @param RRI $reminders
*/
public function __construct(ARI $accounts, PHI $preferences, TJRI $journal, RRI $reminders)
public function __construct(ARI $accounts, PHI $preferences, TJRI $journal)
{
$this->_accounts = $accounts;
$this->_preferences = $preferences;
$this->_journal = $journal;
$this->_reminders = $reminders;
}
public function jobDev() {
$fullName = storage_path().DIRECTORY_SEPARATOR.'firefly-export-2014-07-23.json';
public function jobDev()
{
$fullName = storage_path() . DIRECTORY_SEPARATOR . 'firefly-export-2014-07-23.json';
\Log::notice('Pushed start job.');
Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => 1]);
@@ -40,7 +37,8 @@ class HomeController extends BaseController
/*
*
*/
public function sessionPrev() {
public function sessionPrev()
{
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->prev();
@@ -51,7 +49,8 @@ class HomeController extends BaseController
/*
*
*/
public function sessionNext() {
public function sessionNext()
{
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->next();
@@ -59,6 +58,18 @@ class HomeController extends BaseController
//return Redirect::route('index');
}
public function rangeJump($range)
{
$valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
if (in_array($range, $valid)) {
$this->_preferences->set('viewRange', $range);
Session::forget('range');
}
return Redirect::back();
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
@@ -102,6 +113,61 @@ class HomeController extends BaseController
// build the home screen:
return View::make('index')->with('count', $count)->with('transactions', $transactions)->with('title', 'Firefly')
->with('subTitle', 'What\'s playing?')->with('mainTitleIcon','fa-fire');
->with('subTitle', 'What\'s playing?')->with('mainTitleIcon', 'fa-fire');
}
public function cleanup()
{
/** @var \FireflyIII\Database\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\Account $acct */
$acct = \App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$rightAcctType = $acctType->findByWhat('revenue');
$all = $jrnls->get();
/** @var \TransactionJournal $entry */
foreach ($all as $entry) {
$wrongFromType = false;
$wrongToType = false;
$transactions = $entry->transactions;
if (count($transactions) == 2) {
switch ($entry->transactionType->type) {
case 'Deposit':
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
if (floatval($transaction->amount) < 0) {
$accountType = $transaction->account->accountType;
if ($accountType->type == 'Beneficiary account') {
// should be a Revenue account!
$name = $transaction->account->name;
/** @var \Account $account */
$account = \Auth::user()->accounts()->where('name', $name)->where('account_type_id', $rightAcctType->id)->first();
if (!$account) {
$new = [
'name' => $name,
'what' => 'revenue'
];
$account = $acct->store($new);
}
$transaction->account()->associate($account);
$transaction->save();
}
echo 'Paid by: ' . $transaction->account->name . ' (' . $transaction->account->accountType->type . ')<br />';
}
}
break;
}
}
}
}
}

View File

@@ -19,7 +19,6 @@ class JsonController extends BaseController
$this->helper = $helper;
}
/**
@@ -97,11 +96,32 @@ class JsonController extends BaseController
return Response::json($resultSet);
}
/**
*
*/
public function recurringjournals(RecurringTransaction $recurringTransaction)
{
$parameters = $this->helper->dataTableParameters();
$parameters['transactionTypes'] = ['Withdrawal'];
$parameters['amount'] = 'negative';
$query = $this->helper->journalQuery($parameters);
$query->where('recurring_transaction_id', $recurringTransaction->id);
$resultSet = $this->helper->journalDataset($parameters, $query);
/*
* Build return data:
*/
return Response::json($resultSet);
}
public function recurring()
{
$parameters = $this->helper->dataTableParameters();
$query = $this->helper->recurringTransactionsQuery($parameters);
$resultSet = $this->helper->recurringTransactionsDataset($parameters, $query);
$query = $this->helper->recurringTransactionsQuery($parameters);
$resultSet = $this->helper->recurringTransactionsDataset($parameters, $query);
return Response::json($resultSet);
}

View File

@@ -1,31 +1,42 @@
<?php
use Firefly\Exception\FireflyException;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class PiggybankController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyMethods)
*
*/
class PiggybankController extends BaseController
{
protected $_accounts;
protected $_repository;
/**
*
*/
public function __construct()
{
}
/**
* @param PRI $repository
* @param ARI $accounts
* @throws NotImplementedException
*/
public function __construct(PRI $repository, ARI $accounts)
public function create()
{
$this->_repository = $repository;
$this->_accounts = $accounts;
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Shared\Toolkit\Form $toolkit */
$toolkit = App::make('FireflyIII\Shared\Toolkit\Form');
$periods = Config::get('firefly.piggybank_periods');
$accounts = $toolkit->makeSelectList($acct->getAssetAccounts());
return View::make('piggybanks.create', compact('accounts', 'periods'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc')
->with('subTitle', 'Create new piggy bank')->with('subTitleIcon', 'fa-plus');
}
/**
@@ -33,74 +44,13 @@ class PiggybankController extends BaseController
*
* @return $this
*/
public function addMoney(Piggybank $piggyBank)
public function delete(Piggybank $piggybank)
{
$what = 'add';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$maxRemove = null;
return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with(
'maxRemove', $maxRemove
)->with('piggybank', $piggyBank);
}
/**
* @return $this
*/
public function createPiggybank()
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$periods = Config::get('firefly.piggybank_periods');
$list = $this->_accounts->getActiveDefault();
$accounts = $toolkit->makeSelectList($list);
View::share('subTitle', 'Delete "' . $piggybank->name . '"');
View::share('title', 'Piggy banks');
View::share('subTitle', 'Create new');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.create-piggybank')->with('accounts', $accounts)
->with('periods', $periods);
}
/**
* @return $this
*/
public function createRepeated()
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$periods = Config::get('firefly.piggybank_periods');
$list = $this->_accounts->getActiveDefault();
$accounts = $toolkit->makeSelectList($list);
View::share('title', 'Repeated expenses');
View::share('subTitle', 'Create new');
View::share('mainTitleIcon', 'fa-rotate-right');
return View::make('piggybanks.create-repeated')->with('accounts', $accounts)->with('periods', $periods);
}
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function delete(Piggybank $piggyBank)
{
View::share('subTitle', 'Delete "' . $piggyBank->name . '"');
if ($piggyBank->repeats == 1) {
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-right');
} else {
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
return View::make('piggybanks.delete')->with('piggybank', $piggyBank);
return View::make('piggybanks.delete')->with('piggybank', $piggybank);
}
/**
@@ -111,18 +61,13 @@ class PiggybankController extends BaseController
public function destroy(Piggybank $piggyBank)
{
Event::fire('piggybanks.destroy', [$piggyBank]);
if ($piggyBank->repeats == 1) {
$route = 'piggybanks.index.repeated';
$message = 'Repeated expense';
} else {
$route = 'piggybanks.index.piggybanks';
$message = 'Piggybank';
}
$this->_repository->destroy($piggyBank);
Session::flash('success', $message . ' deleted.');
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$repos->destroy($piggyBank);
Session::flash('success', 'Piggy bank deleted.');
return Redirect::route($route);
return Redirect::route('piggybanks.index');
}
/**
@@ -130,245 +75,203 @@ class PiggybankController extends BaseController
*
* @return $this
*/
public function edit(Piggybank $piggyBank)
public function edit(Piggybank $piggybank)
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$list = $this->_accounts->getActiveDefault();
$accounts = $toolkit->makeSelectList($list);
$periods = Config::get('firefly.piggybank_periods');
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Shared\Toolkit\Form $toolkit */
$toolkit = App::make('FireflyIII\Shared\Toolkit\Form');
View::share('subTitle', 'Edit "' . $piggyBank->name . '"');
$periods = Config::get('firefly.piggybank_periods');
$accounts = $toolkit->makeSelectList($acct->getAssetAccounts());
if ($piggyBank->repeats == 1) {
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
return View::make('piggybanks.edit-repeated')->with('piggybank', $piggyBank)->with('accounts', $accounts)
->with('periods', $periods);
} else {
// piggy bank.
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.edit-piggybank')->with('piggybank', $piggyBank)->with('accounts', $accounts)
->with('periods', $periods);
}
/*
* Flash some data to fill the form.
*/
$prefilled = [
'name' => $piggybank->name,
'account_id' => $piggybank->account_id,
'targetamount' => $piggybank->targetamount,
'targetdate' => $piggybank->targetdate,
'remind_me' => intval($piggybank->remind_me) == 1 ? true : false
];
Session::flash('prefilled', $prefilled);
return View::make('piggybanks.edit', compact('piggybank', 'accounts', 'periods', 'prefilled'))->with('title', 'Piggybanks')->with(
'mainTitleIcon', 'fa-sort-amount-asc'
)
->with('subTitle', 'Edit piggy bank "' . e($piggybank->name) . '"')->with('subTitleIcon', 'fa-pencil');
}
/**
* @param Piggybank $piggyBank
* @param Piggybank $piggybank
*
* @return $this
*/
public function add(Piggybank $piggybank)
{
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$amount = min($leftOnAccount, $leftToSave);
return View::make('piggybanks.add', compact('piggybank'))->with('maxAmount', $amount);
}
/**
* @param Piggybank $piggybank
*
* @return \Illuminate\Http\RedirectResponse
* @throws Firefly\Exception\FireflyException
*/
public function modMoney(Piggybank $piggyBank)
public function postAdd(Piggybank $piggybank)
{
$amount = round(floatval(Input::get('amount')), 2);
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
if ($amount <= $maxAmount) {
$repetition = $piggybank->currentRelevantRep();
$repetition->currentamount += $amount;
$repetition->save();
Session::flash('success', 'Added ' . mf($amount, false) . ' to "' . e($piggybank->name) . '".');
} else {
Session::flash('error', 'Could not add ' . mf($amount, false) . ' to "' . e($piggybank->name) . '".');
}
return Redirect::route('piggybanks.index');
}
/**
* @param Piggybank $piggybank
*
* @return \Illuminate\View\View
*/
public function remove(Piggybank $piggybank)
{
return View::make('piggybanks.remove', compact('piggybank'));
}
/**
* @param Piggybank $piggybank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postRemove(Piggybank $piggybank)
{
$amount = floatval(Input::get('amount'));
switch (Input::get('what')) {
default:
throw new FireflyException('No such action');
break;
case 'add':
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
if (round($amount, 2) <= round(min($maxAdd, $piggyBank->targetamount), 2)) {
Session::flash('success', 'Amount updated!');
$this->_repository->modifyAmount($piggyBank, $amount);
Event::fire('piggybanks.modifyAmountAdd', [$piggyBank, $amount]);
} else {
Session::flash('warning', 'Could not!');
}
break;
case 'remove':
$rep = $piggyBank->currentRelevantRep();
$maxRemove = $rep->currentamount;
if (round($amount, 2) <= round($maxRemove, 2)) {
Session::flash('success', 'Amount updated!');
$this->_repository->modifyAmount($piggyBank, ($amount * -1));
Event::fire('piggybanks.modifyAmountRemove', [$piggyBank, ($amount * -1)]);
} else {
Session::flash('warning', 'Could not!');
}
break;
}
if($piggyBank->repeats == 1) {
$route = 'piggybanks.index.repeated';
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
if ($amount <= $savedSoFar) {
$repetition = $piggybank->currentRelevantRep();
$repetition->currentamount -= $amount;
$repetition->save();
Session::flash('success', 'Removed ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".');
} else {
$route = 'piggybanks.index.piggybanks';
Session::flash('error', 'Could not remove ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".');
}
return Redirect::route($route);
return Redirect::route('piggybanks.index');
}
/**
* @return $this
*/
public function piggybanks()
public function index()
{
$countRepeating = $this->_repository->countRepeating();
$countNonRepeating = $this->_repository->countNonrepeating();
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$piggybanks = $this->_repository->get();
// get the accounts with each piggy bank and check their balance; Fireflyy might needs to
// show the user a correction.
/** @var Collection $piggybanks */
$piggybanks = $repos->get();
$accounts = [];
/** @var \Piggybank $piggybank */
/** @var Piggybank $piggybank */
foreach ($piggybanks as $piggybank) {
$piggybank->savedSoFar = floatval($piggybank->currentRelevantRep()->currentamount);
$piggybank->percentage = intval($piggybank->savedSoFar / $piggybank->targetamount * 100);
$piggybank->leftToSave = $piggybank->targetamount - $piggybank->savedSoFar;
/*
* Fill account information:
*/
$account = $piggybank->account;
$id = $account->id;
if (!isset($accounts[$id])) {
$account->leftOnAccount = $this->_repository->leftOnAccount($account);
$accounts[$id] = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)];
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [
'name' => $account->name,
'balance' => $account->balance(),
'leftForPiggybanks' => $repos->leftOnAccount($account),
'sumOfSaved' => $piggybank->savedSoFar,
'sumOfTargets' => floatval($piggybank->targetamount),
'leftToSave' => $piggybank->leftToSave
];
} else {
$accounts[$account->id]['sumOfSaved'] += $piggybank->savedSoFar;
$accounts[$account->id]['sumOfTargets'] += floatval($piggybank->targetamount);
$accounts[$account->id]['leftToSave'] += $piggybank->leftToSave;
}
}
View::share('title', 'Piggy banks');
View::share('subTitle', 'Save for big expenses');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.index')->with('piggybanks', $piggybanks)
->with('countRepeating', $countRepeating)
->with('countNonRepeating', $countNonRepeating)
->with('accounts', $accounts);
return View::make('piggybanks.index', compact('piggybanks', 'accounts'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc');
}
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function removeMoney(Piggybank $piggyBank)
{
$what = 'remove';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$maxRemove = $piggyBank->currentRelevantRep()->currentamount;
return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with(
'maxRemove', $maxRemove
)->with('piggybank', $piggyBank);
}
/**
* @return $this
*/
public function repeated()
{
$countRepeating = $this->_repository->countRepeating();
$countNonRepeating = $this->_repository->countNonrepeating();
$piggybanks = $this->_repository->get();
// get the accounts with each piggy bank and check their balance; Fireflyy might needs to
// show the user a correction.
$accounts = [];
/** @var \Piggybank $piggybank */
foreach ($piggybanks as $piggybank) {
$account = $piggybank->account;
$id = $account->id;
if (!isset($accounts[$id])) {
$account->leftOnAccount = $this->_repository->leftOnAccount($account);
$accounts[$id] = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)];
}
}
View::share('title', 'Repeated expenses');
View::share('subTitle', 'Save for returning bills');
View::share('mainTitleIcon', 'fa-rotate-left');
return View::make('piggybanks.index')->with('piggybanks', $piggybanks)
->with('countRepeating', $countRepeating)
->with('countNonRepeating', $countNonRepeating)
->with('accounts', $accounts);
}
/**
*
*/
public function show(Piggybank $piggyBank)
{
$leftOnAccount = $this->_repository->leftOnAccount($piggyBank->account);
$balance = $piggyBank->account->balance();
View::share('subTitle', $piggyBank->name);
if ($piggyBank->repeats == 1) {
// repeated expense.
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
} else {
// piggy bank.
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
return View::make('piggybanks.show')->with('piggyBank', $piggyBank)->with('leftOnAccount', $leftOnAccount)
->with('balance', $balance);
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function storePiggybank()
{
$data = Input::all();
unset($data['_token']);
// extend the data array with the settings needed to create a piggy bank:
$data['repeats'] = 0;
$data['rep_times'] = 1;
$data['rep_every'] = 1;
$data['order'] = 0;
$piggyBank = $this->_repository->store($data);
if (!is_null($piggyBank->id)) {
Session::flash('success', 'New piggy bank "' . $piggyBank->name . '" created!');
Event::fire('piggybanks.store', [$piggyBank]);
return Redirect::route('piggybanks.index.piggybanks');
} else {
Session::flash('error', 'Could not save piggy bank: ' . $piggyBank->errors()->first());
return Redirect::route('piggybanks.create.piggybank')->withInput()->withErrors($piggyBank->errors());
}
throw new NotImplementedException;
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
*
*/
public function storeRepeated()
public function store()
{
$data = Input::all();
$data['repeats'] = 0;
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data = Input::all();
unset($data['_token']);
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggybanks.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New piggy bank stored!');
// extend the data array with the settings needed to create a repeated:
$data['repeats'] = 1;
$data['order'] = 0;
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('piggybanks.create');
} else {
return Redirect::route('piggybanks.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
$piggyBank = $this->_repository->store($data);
if ($piggyBank->id) {
Session::flash('success', 'New piggy bank "' . $piggyBank->name . '" created!');
Event::fire('piggybanks.store', [$piggyBank]);
return Redirect::route('piggybanks.index.repeated');
} else {
Session::flash('error', 'Could not save piggy bank: ' . $piggyBank->errors()->first());
return Redirect::route('piggybanks.create.repeated')->withInput()->withErrors($piggyBank->errors());
return Redirect::route('piggybanks.create')->withInput();
break;
}
}
/**
@@ -378,25 +281,41 @@ class PiggybankController extends BaseController
*/
public function update(Piggybank $piggyBank)
{
$piggyBank = $this->_repository->update($piggyBank, Input::all());
if ($piggyBank->validate()) {
if ($piggyBank->repeats == 1) {
$route = 'piggybanks.index.repeated';
$message = 'Repeated expense';
} else {
$route = 'piggybanks.index.piggybanks';
$message = 'Piggy bank';
}
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data = Input::except('_token');
Session::flash('success', $message . ' "' . $piggyBank->name . '" updated.');
Event::fire('piggybanks.update', [$piggyBank]);
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'create_another':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($piggyBank, $data);
Session::flash('success', 'Piggy bank updated!');
return Redirect::route($route);
} else {
Session::flash('error', 'Could not update piggy bank: ' . $piggyBank->errors()->first());
return Redirect::route('piggybanks.edit', $piggyBank->id)->withErrors($piggyBank->errors())->withInput();
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('piggybanks.edit', $piggyBank->id);
} else {
return Redirect::route('piggybanks.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput();
break;
}

View File

@@ -22,9 +22,9 @@ class ProfileController extends BaseController
*/
public function index()
{
View::share('title','Profile');
View::share('subTitle',Auth::user()->email);
View::share('mainTitleIcon','fa-user');
View::share('title', 'Profile');
View::share('subTitle', Auth::user()->email);
View::share('mainTitleIcon', 'fa-user');
return View::make('profile.index');
}
@@ -33,9 +33,9 @@ class ProfileController extends BaseController
*/
public function changePassword()
{
View::share('title',Auth::user()->email);
View::share('subTitle','Change your password');
View::share('mainTitleIcon','fa-user');
View::share('title', Auth::user()->email);
View::share('subTitle', 'Change your password');
View::share('mainTitleIcon', 'fa-user');
return View::make('profile.change-password');
}

View File

@@ -21,7 +21,7 @@ class RecurringController extends BaseController
public function __construct(RTR $repository, RI $helper)
{
$this->_repository = $repository;
$this->_helper = $helper;
$this->_helper = $helper;
View::share('title', 'Recurring transactions');
View::share('mainTitleIcon', 'fa-rotate-right');
@@ -102,6 +102,31 @@ class RecurringController extends BaseController
}
/**
* @param RecurringTransaction $recurringTransaction
* @return mixed
*/
public function rescan(RecurringTransaction $recurringTransaction)
{
if (intval($recurringTransaction->active) == 0) {
Session::flash('warning', 'Inactive recurring transactions cannot be scanned.');
return Redirect::back();
}
// do something!
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $repo */
$repo = App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$set = $repo->get();
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
Event::fire('recurring.rescan', [$recurringTransaction, $journal]);
}
Session::flash('success', 'Rescanned everything.');
return Redirect::back();
}
public function store()
{
$data = Input::except(['_token', 'post_submit_action']);
@@ -170,14 +195,14 @@ class RecurringController extends BaseController
Session::flash('error', 'Could not update recurring transaction: ' . $messageBag->first());
return Redirect::route('transactions.edit', $recurringTransaction->id)->withInput()
->withErrors($messageBag);
->withErrors($messageBag);
}
break;
case 'validate_only':
$data = Input::all();
$data['id'] = $recurringTransaction->id;
$data = Input::all();
$data['id'] = $recurringTransaction->id;
$messageBags = $this->_helper->validate($data);
Session::flash('warnings', $messageBags['warnings']);

View File

@@ -1,97 +1,10 @@
<?php
use Carbon\Carbon;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
/**
* Class ReminderController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class ReminderController extends BaseController
{
protected $_repository;
/**
* @param RRI $repository
*/
public function __construct(RRI $repository)
{
$this->_repository = $repository;
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\JsonResponse
*/
public function dismiss(\Reminder $reminder)
{
$reminder = $this->_repository->deactivate($reminder);
return Response::json($reminder->id);
}
/**
* Returns the reminders currently active for the modal dialog.
*/
public function modalDialog()
{
$today = new Carbon;
$reminders = $this->_repository->getPiggybankReminders();
/** @var \Reminder $reminder */
foreach ($reminders as $index => $reminder) {
if (\Session::has('dismissal-' . $reminder->id)) {
$time = \Session::get('dismissal-' . $reminder->id);
if ($time >= $today) {
unset($reminders[$index]);
}
}
}
return View::make('reminders.popup')->with('reminders', $reminders);
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\JsonResponse
*/
public function postpone(\Reminder $reminder)
{
$now = new Carbon;
$now->addDay();
Session::put('dismissal-' . $reminder->id, $now);
return Response::json($reminder->id);
}
/**
* @param Reminder $reminder
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function redirect(\Reminder $reminder)
{
if ($reminder instanceof PiggybankReminder) {
// fields to prefill:
$parameters = [
'account_to_id' => $reminder->piggybank->account->id,
'amount' => round($reminder->amountToSave(), 2),
'description' => 'Money for ' . $reminder->piggybank->name,
'piggybank_id' => $reminder->piggybank->id,
'reminder_id' => $reminder->id
];
return Redirect::to(
route('transactions.create', ['what' => 'transfer']) . '?' . http_build_query($parameters)
);
}
return View::make('error')->with('message', 'No such reminder.');
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
use Carbon\Carbon;
/**
* Class ReportController
@@ -12,7 +13,55 @@ class ReportController extends BaseController
*/
public function index()
{
return View::make('reports.index')->with('title','Reports')->with('mainTitleIcon','fa-line-chart');
/** @var \FireflyIII\Database\TransactionJournal $journals */
$journals = App::make('FireflyIII\Database\TransactionJournal');
$journal = $journals->first();
$date = $journal->date;
$years = [];
while ($date <= Carbon::now()) {
$years[] = $date->format('Y');
$date->addYear();
}
return View::make('reports.index', compact('years'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart');
}
/**
* @param $year
*/
public function year($year)
{
try {
$date = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
// get some sums going
$summary = [];
$end = clone $date;
$end->endOfYear();
while ($date < $end) {
$summary[] = [
'month' => $date->format('F'),
'income' => $tj->getSumOfIncomesByMonth($date),
'expense' => $tj->getSumOfExpensesByMonth($date),
];
$date->addMonth();
}
// draw some charts etc.
return View::make('reports.year', compact('summary'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart')->with('subTitle', $year)->with(
'subTitleIcon', 'fa-bar-chart'
)->with('year', $year);
}
}

View File

@@ -33,6 +33,7 @@ class CreatePiggybanksTable extends Migration
$table->smallInteger('rep_times')->unsigned()->nullable();
$table->enum('reminder', ['day', 'week', 'month', 'year'])->nullable();
$table->smallInteger('reminder_skip')->unsigned();
$table->boolean('remind_me');
$table->integer('order')->unsigned();
// connect account to piggybank.

View File

@@ -27,26 +27,11 @@ class CreateRemindersTable extends Migration
'reminders', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('class', 40);
$table->integer('piggybank_id')->unsigned()->nullable();
$table->integer('recurring_transaction_id')->unsigned()->nullable();
$table->integer('user_id')->unsigned();
$table->date('startdate');
$table->date('enddate')->nullable();
$table->boolean('active');
// connect reminders to piggy banks.
$table->foreign('piggybank_id')
->references('id')->on('piggybanks')
->onDelete('set null');
// connect reminders to recurring transactions.
$table->foreign('recurring_transaction_id')
->references('id')->on('recurring_transactions')
->onDelete('set null');
// connect reminders to users
$table->foreign('user_id')
->references('id')->on('users')

View File

@@ -10,6 +10,7 @@ App::before(
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->getDateRange();
$toolkit->checkImportJobs();
Event::fire('recurring.verify');
}
}

View File

@@ -10,9 +10,10 @@ class Form
{
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -29,18 +30,42 @@ class Form
return self::ffInput('checkbox', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffAmount($name, $value = null, array $options = [])
{
$options['step'] = 'any';
$options['min'] = '0.01';
$options['min'] = '0.01';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffBalance($name, $value = null, array $options = [])
{
$options['step'] = 'any';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -50,9 +75,10 @@ class Form
}
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -63,10 +89,11 @@ class Form
}
/**
* @param $name
* @param $name
* @param array $list
* @param null $selected
* @param null $selected
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -76,9 +103,10 @@ class Form
}
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -88,16 +116,25 @@ class Form
}
public static function label($name)
/**
* @param $name
* @param $options
*
* @return string
*/
public static function label($name, $options)
{
if (isset($options['label'])) {
return $options['label'];
}
$labels = [
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account'
'account_to_id' => 'Account to',
'account_id' => 'Asset account'
];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
@@ -151,28 +188,29 @@ class Form
case 'create':
$return = '<div class="form-group"><label for="return_to_form" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action','create_another', $previousValue == 'create_another');
$return .= \Form::radio('post_submit_action', 'create_another', $previousValue == 'create_another');
$return .= 'After storing, return here to create another one.</label></div></div></div>';
break;
case 'update':
$return = '<div class="form-group"><label for="return_to_edit" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action','return_to_edit', $previousValue == 'return_to_edit');
$return .= \Form::radio('post_submit_action', 'return_to_edit', $previousValue == 'return_to_edit');
$return .= 'After updating, return here.</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type);
break;
}
return $store.$validate.$return;
return $store . $validate . $return;
}
/**
* @param $type
* @param $name
* @param null $value
* @param $type
* @param $name
* @param null $value
* @param array $options
* @param array $list
*
* @return string
* @throws FireflyException
*/
@@ -181,10 +219,10 @@ class Form
/*
* add some defaults to this method:
*/
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['autocomplete'] = 'off';
$label = self::label($name);
$label = self::label($name, $options);
/*
* Make label and placeholder look nice.
*/
@@ -193,9 +231,9 @@ class Form
/*
* Get prefilled value:
*/
if(\Session::has('prefilled')) {
if (\Session::has('prefilled')) {
$prefilled = \Session::get('prefilled');
$value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value;
$value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value;
}
/*

View File

@@ -12,14 +12,17 @@ class Account implements AccountInterface
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()
->accountIs($account)
->leftJoin('transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id')
->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']);
}
@@ -36,7 +39,7 @@ class Account implements AccountInterface
* For now, Firefly simply warns the user of this.
*
* @param \Account $account
* @param $perPage
* @param $perPage
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
@@ -110,17 +113,25 @@ class Account implements AccountInterface
// statistics (transactions)
$trIn = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
$trOut = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
$trIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trDiff = $trIn + $trOut;
// statistics (transfers)
$trfIn = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount'));
$trfOut = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount'));
$trfIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfDiff = $trfIn + $trfOut;
$stats['period'] = [

View File

@@ -1,11 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 27/09/14
* Time: 07:39
*/
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
@@ -355,7 +348,7 @@ class Json implements JsonInterface
* Loop set and create entries to return.
*/
foreach ($set as $entry) {
$data['data'][] = [
$set = [
'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)],
'match' => explode(' ', $entry->match),
@@ -370,6 +363,11 @@ class Json implements JsonInterface
'delete' => route('recurring.delete', $entry->id)
]
];
if (intval($entry->skip) > 0) {
$set['repeat_freq'] = $entry->repeat_freq . ' (skip ' . $entry->skip . ')';
}
$data['data'][] = $set;
}
return $data;

View File

@@ -84,22 +84,23 @@ class Toolkit implements ToolkitInterface
/**
*
*/
public function checkImportJobs() {
public function checkImportJobs()
{
/*
* Get all jobs.
*/
/** @var \Importmap $importJob */
$importJob = \Importmap::where('user_id',\Auth::user()->id)
->where('totaljobs','>',\DB::Raw('`jobsdone`'))
->orderBy('created_at','DESC')
->first();
if(!is_null($importJob)) {
$diff = intval($importJob->totaljobs) - intval($importJob->jobsdone);
$date = new Carbon;
$importJob = \Importmap::where('user_id', \Auth::user()->id)
->where('totaljobs', '>', \DB::Raw('`jobsdone`'))
->orderBy('created_at', 'DESC')
->first();
if (!is_null($importJob)) {
$diff = intval($importJob->totaljobs) - intval($importJob->jobsdone);
$date = new Carbon;
$today = new Carbon;
$date->addSeconds($diff);
\Session::put('job_pct',$importJob->pct());
\Session::put('job_text',$date->diffForHumans());
\Session::put('job_pct', $importJob->pct());
\Session::put('job_text', $date->diffForHumans());
} else {
\Session::forget('job_pct');
\Session::forget('job_text');
@@ -134,7 +135,6 @@ class Toolkit implements ToolkitInterface
*/
protected function _updateStartDate($range, Carbon $start)
{
$today = new Carbon;
switch ($range) {
case '1D':
$start->startOfDay();
@@ -149,12 +149,15 @@ class Toolkit implements ToolkitInterface
$start->firstOfQuarter();
break;
case '6M':
if (intval($today->format('m')) >= 7) {
if (intval($start->format('m')) >= 7) {
$start->startOfYear()->addMonths(6);
} else {
$start->startOfYear();
}
break;
case '1Y':
$start->startOfYear();
break;
}
return $start;
@@ -170,34 +173,34 @@ class Toolkit implements ToolkitInterface
*/
protected function _updateEndDate($range, Carbon $start)
{
$end = clone $start;
switch ($range) {
default:
throw new FireflyException('_updateEndDate cannot handle $range ' . $range);
break;
case '1D':
$end = clone $start;
$end->endOfDay();
break;
case '1W':
$end = clone $start;
$end->endOfWeek();
break;
case '1M':
$end = clone $start;
$end->endOfMonth();
break;
case '3M':
$end = clone $start;
$end->lastOfQuarter();
break;
case '6M':
$end = clone $start;
if (intval($start->format('m')) >= 7) {
$end->endOfYear();
} else {
$end->startOfYear()->addMonths(6);
}
break;
default:
throw new FireflyException('Nothing happened with $end!');
case '1Y':
$end->endOfYear();
break;
}
return $end;
@@ -209,15 +212,39 @@ class Toolkit implements ToolkitInterface
default:
throw new FireflyException('No _periodName() for range "' . $range . '"');
break;
case '1D':
return $date->format('jS F Y');
break;
case '1W':
return 'week ' . $date->format('W, Y');
break;
case '1M':
return $date->format('F Y');
break;
case '3M':
$month = intval($date->format('m'));
return 'Q' . ceil(($month / 12) * 4) . ' ' . $date->format('Y');
break;
case '6M':
$month = intval($date->format('m'));
$half = ceil(($month / 12) * 2);
$halfName = $half == 1 ? 'first' : 'second';
return $halfName . ' half of ' . $date->format('d-m-Y');
break;
case '1Y':
return $date->format('Y');
break;
}
}
protected function _previous($range, Carbon $date)
{
switch ($range) {
default:
throw new FireflyException('Cannot do _previous() on ' . $range);
break;
case '1D':
$date->startOfDay()->subDay();
break;
@@ -231,12 +258,17 @@ class Toolkit implements ToolkitInterface
$date->firstOfQuarter()->subMonths(3)->firstOfQuarter();
break;
case '6M':
if (intval($date->format('m')) >= 7) {
$date->startOfYear();
} else {
$month = intval($date->format('m'));
if ($month <= 6) {
$date->startOfYear()->subMonths(6);
} else {
$date->startOfYear();
}
break;
case '1Y':
$date->startOfYear()->subYear();
break;
}
return $date;
}
@@ -254,7 +286,7 @@ class Toolkit implements ToolkitInterface
$date->endOfMonth()->addDay()->startOfMonth();
break;
case '3M':
$date->lastOfQuarter();
$date->lastOfQuarter()->addDay();
break;
case '6M':
if (intval($date->format('m')) >= 7) {
@@ -263,6 +295,12 @@ class Toolkit implements ToolkitInterface
$date->startOfYear()->addMonths(6);
}
break;
case '1Y':
$date->startOfYear()->addYear();
break;
default:
throw new FireflyException('Cannot do _next() on ' . $range);
break;
}
return $date;
}
@@ -311,7 +349,7 @@ class Toolkit implements ToolkitInterface
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
* @param null $titleField
* @param null $titleField
*
* @return mixed
*/
@@ -343,4 +381,127 @@ class Toolkit implements ToolkitInterface
}
return $selectList;
}
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5)
{
if (strlen($start) != 6) {
throw new FireflyException('Start, ' . e($start) . ' should be a six character HTML colour.');
}
if (strlen($end) != 6) {
throw new FireflyException('End, ' . e($end) . ' should be a six character HTML colour.');
}
if ($steps < 1) {
throw new FireflyException('Steps must be > 1');
}
$start = '#' . $start;
$end = '#' . $end;
/*
* Split html colours.
*/
list($rs, $gs, $bs) = sscanf($start, "#%02x%02x%02x");
list($re, $ge, $be) = sscanf($end, "#%02x%02x%02x");
$stepr = ($re - $rs) / $steps;
$stepg = ($ge - $gs) / $steps;
$stepb = ($be - $bs) / $steps;
$return = [];
for ($i = 0; $i <= $steps; $i++) {
$cr = $rs + ($stepr * $i);
$cg = $gs + ($stepg * $i);
$cb = $bs + ($stepb * $i);
$return[] = $this->rgb2html($cr, $cg, $cb);
}
return $return;
}
protected function rgb2html($r, $g = -1, $b = -1)
{
$r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r));
$g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g));
$b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b));
$color = (strlen($r) < 2 ? '0' : '') . $r;
$color .= (strlen($g) < 2 ? '0' : '') . $g;
$color .= (strlen($b) < 2 ? '0' : '') . $b;
return '#' . $color;
}
/**
* @param Carbon $currentEnd
* @param $repeatFreq
* @throws FireflyException
*/
public function endOfPeriod(Carbon $currentEnd, $repeatFreq)
{
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->addDay();
break;
case 'weekly':
$currentEnd->addWeek()->subDay();
break;
case 'monthly':
$currentEnd->addMonth()->subDay();
break;
case 'quarterly':
$currentEnd->addMonths(3)->subDay();
break;
case 'half-year':
$currentEnd->addMonths(6)->subDay();
break;
case 'yearly':
$currentEnd->addYear()->subDay();
break;
}
}
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
* @throws FireflyException
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip)
{
$add = ($skip + 1);
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$date->addDays($add);
break;
case 'weekly':
$date->addWeeks($add);
break;
case 'monthly':
$date->addMonths($add);
break;
case 'quarterly':
$months = $add * 3;
$date->addMonths($months);
break;
case 'half-year':
$months = $add * 6;
$date->addMonths($months);
break;
case 'yearly':
$date->addYears($add);
break;
}
return $date;
}
}

View File

@@ -2,6 +2,7 @@
namespace Firefly\Helper\Toolkit;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
@@ -21,7 +22,7 @@ interface ToolkitInterface
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
* @param null $titleField
* @param null $titleField
*
* @return mixed
*/
@@ -33,4 +34,19 @@ interface ToolkitInterface
public function checkImportJobs();
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5);
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip);
}

View File

@@ -175,12 +175,6 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo
return $messageBag;
}
if ($recurringTransaction->date < Carbon::now()) {
$messageBag->add('date', 'Must be in the future.');
return $messageBag;
}
if ($recurringTransaction->validate()) {
$recurringTransaction->save();
} else {

View File

@@ -31,17 +31,4 @@ class EloquentReminderRepository implements ReminderRepositoryInterface
{
$this->_user = \Auth::user();
}
/**
* @param \Reminder $reminder
*
* @return mixed|void
*/
public function deactivate(\Reminder $reminder)
{
$reminder->active = 0;
$reminder->save();
return $reminder;
}
}

View File

@@ -10,14 +10,6 @@ namespace Firefly\Storage\Reminder;
interface ReminderRepositoryInterface
{
/**
* @param \Reminder $reminder
*
* @return mixed
*/
public function deactivate(\Reminder $reminder);
/**
* @param \User $user
* @return mixed

View File

@@ -5,6 +5,7 @@ namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
@@ -24,6 +25,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$this->_user = \Auth::user();
}
/**
* Get them ALL
*
* @return Collection
*/
public function get() {
return $this->_user->transactionjournals()->with('transactions')->get();
}
/**
* @param Job $job
* @param array $payload

View File

@@ -4,6 +4,7 @@ namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
/**
* Interface TransactionJournalRepositoryInterface
@@ -20,6 +21,13 @@ interface TransactionJournalRepositoryInterface
*/
public function importTransaction(Job $job, array $payload);
/**
* Get them ALL
*
* @return Collection
*/
public function get();
/**
* @param Job $job
* @param array $payload

View File

@@ -23,44 +23,19 @@ class EloquentJournalTrigger
/*
* Grab all recurring events.
*/
$set = $journal->user()->first()->recurringtransactions()->get();
$set = $journal->user()->first()->recurringtransactions()->get();
$result = [];
/*
* Prep vars
*/
$description = strtolower($journal->description);
$result = [0 => 0];
/** @var \RecurringTransaction $recurring */
foreach ($set as $recurring) {
$matches = explode(' ', $recurring->match);
/*
* Count the number of matches.
*/
$count = 0;
foreach ($matches as $word) {
if (!(strpos($description, $word) === false)) {
$count++;
\Log::debug('Recurring transaction #' . $recurring->id . ': word "' . $word . '" found in "' . $description . '".');
}
}
$result[$recurring->id] = $count;
\Event::fire('recurring.rescan', [$recurring, $journal]);
}
/*
* The one with the highest value is the winrar!
*/
$index = array_search(max($result), $result);
/*
* Find the recurring transaction:
*/
if (count($result[$index]) > 0) {
$winner = $journal->user()->first()->recurringtransactions()->find($index);
if ($winner) {
$journal->recurringTransaction()->associate($winner);
$journal->save();
}
}
return true;
}

View File

@@ -126,9 +126,6 @@ class EloquentLimitTrigger
// remove and recreate limit repetitions.
// if limit is not repeating, simply update the repetition to match the limit,
// even though deleting everything is easier.
foreach ($limit->limitrepetitions()->get() as $l) {
$l->delete();
}
$limit->createRepetition($limit->startdate);
return true;

View File

@@ -107,159 +107,6 @@ class EloquentPiggybankTrigger
return true;
}
/**
* Whenever a repetition is made, the decision is there to make reminders for it. Or not.
* Some combinations are "invalid" or impossible and will never trigger reminders. Others do.
*
* The numbers below refer to a small list I made in a text-file (it no longer exists) which contained the eight
* binary combinations that can be made of three properties each piggy bank has (among others):
*
* - Whether or not it has a start date.
* - Whether or not it has an end date.
* - Whether or not the piggy bank repeats itself.
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param \PiggybankRepetition $repetition
*
* @return null
*/
public function createdRepetition(\PiggybankRepetition $repetition)
{
\Log::debug('TRIGGER on createdRepetition() for repetition #' . $repetition->id);
$piggyBank = $repetition->piggybank;
// first, exclude all combinations that will not generate (valid) reminders
// no reminders needed (duh)
if (is_null(($piggyBank->reminder))) {
\Log::debug('No reminders because no reminder needed.');
return null;
}
// no start, no target, no repeat (#1):
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 0) {
\Log::debug('No reminders because no start, no target, no repeat (#1)');
return null;
}
// no start, but repeats (#5):
if (is_null($piggyBank->startdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, but repeats (#5)');
return null;
}
// no start, no end, but repeats (#6)
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, no end, but repeats (#6)');
return null;
}
// no end, but repeats (#7)
if (is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no end, but repeats (#7)');
return null;
}
\Log::debug('Will continue...');
/*
* #2, #3, #4 and #8 are valid combo's.
*
* We add two years to the end when the repetition has no target date; we "pretend" there is a target date.
*
*/
if (is_null($repetition->targetdate)) {
$end = new Carbon;
$end->addYears(2);
} else {
$end = $repetition->targetdate;
}
/*
* If there is no start date, the start dat becomes right now.
*/
if (is_null($repetition->startdate)) {
$start = new Carbon;
} else {
$start = $repetition->startdate;
}
/*
* Firefly checks every period X between $start and $end and if necessary creates a reminder. Firefly
* only creates reminders if the $current date is after today. Piggy banks may have their start in the past.
*
* This loop will jump a month when the reminder is set monthly, a week when it's set weekly, etcetera.
*/
$current = $start;
$today = new Carbon;
$today->startOfDay();
while ($current <= $end) {
\Log::debug('Looping reminder dates; now at ' . $current);
/*
* Piggy bank reminders start X days before the actual date of the event.
*/
$reminderStart = clone $current;
switch ($piggyBank->reminder) {
case 'day':
$reminderStart->subDay();
break;
case 'week':
$reminderStart->subDays(4);
break;
case 'month':
$reminderStart->subDays(21);
break;
case 'year':
$reminderStart->subMonths(9);
break;
}
/*
* If the date is past today we create a reminder, otherwise we don't. The end date is the date
* the reminder is due; after that it is invalid.
*/
if ($current >= $today) {
$reminder = new \PiggybankReminder;
$reminder->piggybank()->associate($piggyBank);
$reminder->user()->associate(\Auth::user());
$reminder->startdate = $reminderStart;
$reminder->enddate = $current;
$reminder->active = 1;
\Log::debug('Will create a reminder. Is it valid?');
\Log::debug($reminder->validate());
try {
$reminder->save();
} catch (QueryException $e) {
\Log::error('Could not save reminder: ' . $e->getMessage());
}
} else {
\Log::debug('Current is before today, will not make a reminder.');
}
/*
* Here Firefly jumps ahead to the next reminder period.
*/
switch ($piggyBank->reminder) {
case 'day':
$current->addDays($piggyBank->reminder_skip);
break;
case 'week':
$current->addWeeks($piggyBank->reminder_skip);
break;
case 'month':
$current->addMonths($piggyBank->reminder_skip);
break;
case 'year':
$current->addYears($piggyBank->reminder_skip);
break;
}
}
}
/**
* @param \Piggybank $piggyBank
*
@@ -267,13 +114,6 @@ class EloquentPiggybankTrigger
*/
public function destroy(\Piggybank $piggyBank)
{
$reminders = $piggyBank->piggybankreminders()->get();
/** @var \PiggybankReminder $reminder */
foreach ($reminders as $reminder) {
$reminder->delete();
}
return true;
}
@@ -353,12 +193,9 @@ class EloquentPiggybankTrigger
'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@updateRelatedTransfer'
);
$events->listen(
'piggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies'
'piggybanks.storepiggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies'
);
$events->listen(
'piggybanks.repetition', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createdRepetition'
);
}
/**

View File

@@ -3,6 +3,7 @@
namespace Firefly\Trigger\Recurring;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Events\Dispatcher;
/**
@@ -28,8 +29,67 @@ class EloquentRecurringTrigger
}
public function createReminders()
/**
* @param \RecurringTransaction $recurring
* @param \TransactionJournal $journal
*/
public function rescan(\RecurringTransaction $recurring, \TransactionJournal $journal)
{
/*
* Match words.
*/
$wordMatch = false;
$matches = explode(' ', $recurring->match);
$description = strtolower($journal->description);
/*
* Attach expense account to description for more narrow matching.
*/
$transactions = $journal->transactions()->get();
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
/** @var \Account $account */
$account = $transaction->account()->first();
/** @var \AccountType $type */
$type = $account->accountType()->first();
if ($type->type == 'Expense account' || $type->type == 'Beneficiary account') {
$description .= ' ' . strtolower($account->name);
}
}
$count = 0;
foreach ($matches as $word) {
if (!(strpos($description, strtolower($word)) === false)) {
$count++;
}
}
if ($count >= count($matches)) {
$wordMatch = true;
}
/*
* Match amount.
*/
$amountMatch = false;
if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));
$min = floatval($recurring->amount_min);
$max = floatval($recurring->amount_max);
if ($amount >= $min && $amount <= $max) {
$amountMatch = true;
}
}
/*
* If both, update!
*/
if ($wordMatch && $amountMatch) {
$journal->recurringTransaction()->associate($recurring);
$journal->save();
}
}
/**
@@ -39,10 +99,7 @@ class EloquentRecurringTrigger
*/
public function subscribe(Dispatcher $events)
{
// $events->listen('recurring.destroy', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@destroy');
// $events->listen('recurring.store', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@store');
// $events->listen('recurring.update', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@update');
// $events->listen('recurring.check', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@createReminders');
$events->listen('recurring.rescan', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@rescan');
}
/**

View File

@@ -0,0 +1,469 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use FireflyIII\Database\Ifaces\AccountInterface;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use Illuminate\Support\Collection;
/**
* Class Account
*
* @package FireflyIII\Database
*/
class Account implements CUD, CommonDatabaseCalls, AccountInterface
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
$model->name = $data['name'];
$model->active = isset($data['active']) ? intval($data['active']) : 0;
$model->save();
if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) {
$openingBalance = $this->openingBalanceTransaction($model);
$openingBalance->date = new Carbon($data['openingbalancedate']);
$openingBalance->save();
$amount = floatval($data['openingbalance']);
/** @var \Transaction $transaction */
foreach ($openingBalance->transactions as $transaction) {
if ($transaction->account_id == $model->id) {
$transaction->amount = $amount;
} else {
$transaction->amount = $amount * -1;
}
$transaction->save();
}
}
return true;
}
/**
* Get all asset accounts. Optional JSON based parameters.
*
* @param array $parameters
*
* @return Collection
*/
public function getAssetAccounts(array $parameters = [])
{
return $this->getAccountsByType(['Default account', 'Asset account'], $parameters);
}
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account)
{
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$accountType = $acctType->findByWhat('initial');
return $this->getUser()->accounts()->where('account_type_id', $accountType->id)->where('name', 'LIKE', $account->name . '%')->first();
}
/**
* @param array $types
* @param array $parameters
*
* @return Collection
*/
public function getAccountsByType(array $types, array $parameters = [])
{
/*
* Basic query:
*/
$query = $this->getUser()->accounts()->accountTypeIn($types);
/*
* Without an opening balance, the rest of these queries will fail.
*/
$query->leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id');
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
/*
* Not used, but useful for the balance within a certain month / year.
*/
$balanceOnDate = isset($parameters['date']) ? $parameters['date'] : Carbon::now();
$query->where(
function ($q) use ($balanceOnDate) {
$q->where('transaction_journals.date', '<=', $balanceOnDate->format('Y-m-d'));
$q->orWhereNull('transaction_journals.date');
}
);
$query->groupBy('accounts.id');
/*
* If present, process parameters for sorting:
*/
$query->orderBy('name', 'ASC');
/*
* If present, process parameters for searching.
*/
if (isset($parameters['search'])) {
$query->where('name', 'LIKE', '%' . e($parameters['search']['value'] . '%'));
}
/*
* If present, start at $start:
*/
if (isset($parameters['start'])) {
$query->skip(intval($parameters['start']));
}
if (isset($parameters['length'])) {
$query->take(intval($parameters['length']));
}
return $query->get(['accounts.*', \DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
}
/**
* @return int
*/
public function countAssetAccounts()
{
return $this->countAccountsByType(['Default account', 'Asset account']);
}
/**
* @return int
*/
public function countExpenseAccounts()
{
return $this->countAccountsByType(['Expense account', 'Beneficiary account']);
}
/**
* @param array $types
*
* @return int
*/
public function countAccountsByType(array $types)
{
return $this->getUser()->accounts()->accountTypeIn($types)->count();
}
/**
* @param array $parameters
*
* @return Collection
*/
public function getExpenseAccounts(array $parameters = [])
{
return $this->getAccountsByType(['Expense account', 'Beneficiary account'], $parameters);
}
/**
* Get all default accounts.
*
* @return Collection
*/
public function getDefaultAccounts()
{
// TODO: Implement getDefaultAccounts() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts()
{
return $this->countAccountsByType(['Revenue account']);
}
/**
* Get all revenue accounts.
*
* @param array $parameters
*
* @return Collection
*/
public function getRevenueAccounts(array $parameters = [])
{
return $this->getAccountsByType(['Revenue account'], $parameters);
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
$model->delete();
return true;
}
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()
->accountIs($account)
->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']);
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
die('No impl');
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
/*
* Name validation:
*/
if (!isset($model['name'])) {
$errors->add('name', 'Name is mandatory');
}
if (isset($model['name']) && strlen($model['name']) == 0) {
$errors->add('name', 'Name is too short');
}
if (isset($model['name']) && strlen($model['name']) > 100) {
$errors->add('name', 'Name is too long');
}
$validator = \Validator::make([$model], \Account::$rules);
if ($validator->invalid()) {
$errors->merge($errors);
}
/*
* type validation.
*/
if (!isset($model['what'])) {
$errors->add('name', 'Internal error: need to know type of account!');
}
/*
* Opening balance and opening balance date.
*/
if (isset($model['what']) && $model['what'] == 'asset') {
if (isset($model['openingbalance']) && strlen($model['openingbalance']) > 0 && !is_numeric($model['openingbalance'])) {
$errors->add('openingbalance', 'This is not a number.');
}
if (isset($model['openingbalancedate']) && strlen($model['openingbalancedate']) > 0) {
try {
new Carbon($model['openingbalancedate']);
} catch (\Exception $e) {
$errors->add('openingbalancedate', 'This date is invalid.');
}
}
}
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
if (!$errors->has('openingbalance')) {
$successes->add('openingbalance', 'OK');
}
if (!$errors->has('openingbalancedate')) {
$successes->add('openingbalancedate', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
/*
* Find account type.
*/
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$accountType = $acctType->findByWhat($data['what']);
$data['user_id'] = $this->getUser()->id;
$data['account_type_id'] = $accountType->id;
$data['active'] = isset($data['active']) && $data['active'] === '1' ? 1 : 0;
$data = array_except($data, array('_token', 'what'));
$account = new \Account($data);
if (!$account->validate()) {
var_dump($account->errors()->all());
exit;
}
$account->save();
if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) {
$this->storeInitialBalance($account, $data);
}
/* Tell transaction journal to store a new one.*/
return $account;
}
/**
* @param \Account $account
* @param array $data
*
* @return bool
*/
public function storeInitialBalance(\Account $account, array $data)
{
$opposingData = [
'name' => $account->name . ' Initial Balance',
'active' => 0,
'what' => 'initial'
];
$opposingAccount = $this->store($opposingData);
/*
* Create a journal from opposing to account or vice versa.
*/
$balance = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = \App::make('FireflyIII\Database\TransactionJournal');
if ($balance < 0) {
// first transaction draws money from the new account to the opposing
$from = $account;
$to = $opposingAccount;
} else {
// first transaction puts money into account
$from = $opposingAccount;
$to = $account;
}
// data for transaction journal:
$balance = $balance < 0 ? $balance * -1 : $balance;
$opening = [
'what' => 'opening',
'currency' => 'EUR',
'amount' => $balance,
'from' => $from,
'to' => $to,
'date' => $date,
'description' => 'Opening balance for new account ' . $account->name,
];
$validation = $tj->validate($opening);
if ($validation['errors']->count() == 0) {
$tj->store($opening);
return true;
} else {
var_dump($validation['errors']);
exit;
}
return false;
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
return $this->getUser()->accounts()->whereIn('id', $ids)->get();
}
}

View File

@@ -0,0 +1,139 @@
<?php
namespace FireflyIII\Database;
use Firefly\Exception\FireflyException;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
use FireflyIII\Database\Ifaces\AccountTypeInterface;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
/**
* Class AccountType
*
* @package FireflyIII\Database
*/
class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls
{
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue
*
* @param $what
*
* @return \AccountType|null
* @throws FireflyException
*/
public function findByWhat($what)
{
switch ($what) {
case 'expense':
return \AccountType::whereType('Expense account')->first();
break;
case 'asset':
return \AccountType::whereType('Asset account')->first();
break;
case 'revenue':
return \AccountType::whereType('Revenue account')->first();
break;
case 'initial':
return \AccountType::whereType('Initial balance account')->first();
break;
default:
throw new FireflyException('Cannot find account type described as "' . e($what) . '".');
break;
}
return null;
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
}

View File

@@ -0,0 +1,234 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use Illuminate\Support\Collection;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\BudgetInterface;
/**
* Class Budget
*
* @package FireflyIII\Database
*/
class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return \LimitRepetition|null
*/
public function repetitionOnStartingOnDate(\Budget $budget, Carbon $date)
{
return \LimitRepetition::
leftJoin('limits', 'limit_repetitions.limit_id', '=', 'limits.id')->leftJoin(
'components', 'limits.component_id', '=', 'components.id'
)->where('limit_repetitions.startdate', $date->format('Y-m-d'))->where(
'components.id', $budget->id
)->first(['limit_repetitions.*']);
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
$model->delete();
return true;
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if(isset($model['name'])) {
if(strlen($model['name']) < 1) {
$errors->add('name', 'Name is too short');
}
if(strlen($model['name']) > 200) {
$errors->add('name', 'Name is too long');
}
} else {
$errors->add('name', 'Name is mandatory');
}
$validator = \Validator::make($model, \Component::$rules);
if ($validator->invalid()) {
$errors->merge($validator->errors());
}
if(!$errors->has('name')) {
$successes->add('name','OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
$data['user_id'] = $this->getUser()->id;
$budget = new \Budget($data);
$budget->class = 'Budget';
if (!$budget->validate()) {
var_dump($budget->errors()->all());
exit;
}
$budget->save();
return $budget;
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
$budgets = $this->getUser()->budgets()->get();
return $budgets;
}
/**
* @param \Budget $budget
* @param Carbon $date
* @return float
*/
public function spentInMonth(\Budget $budget, Carbon $date) {
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1;
return $sum;
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function transactionsWithoutBudgetInDateRange(Carbon $start, Carbon $end)
{
// Add expenses that have no budget:
return \Auth::user()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget');
}
)->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->get();
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
$model->name = $data['name'];
if (!$model->validate()) {
var_dump($model->errors()->all());
exit;
}
$model->save();
return true;
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use Illuminate\Support\Collection;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\CategoryInterface;
/**
* Class Category
*
* @package FireflyIII\Database
*/
class Category implements CUD, CommonDatabaseCalls, CategoryInterface
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Illuminate\Support\Collection;
/**
* Interface AccountInterface
*
* @package FireflyIII\Database
*/
interface AccountInterface
{
/**
* Get all asset accounts. The parameters are optional and are provided by the DataTables plugin.
*
* @param array $parameters
*
* @return Collection
*/
public function getAssetAccounts(array $parameters = []);
/**
* Counts the number of total asset accounts. Useful for DataTables.
*
* @return int
*/
public function countAssetAccounts();
/**
* Counts the number of total expense accounts. Useful for DataTables.
*
* @return int
*/
public function countExpenseAccounts();
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts();
/**
* @param array $parameters
*
* @return Collection
*/
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account);
public function getExpenseAccounts(array $parameters = []);
/**
* Get all revenue accounts.
*
* @param array $parameters
*
* @return Collection
*/
public function getRevenueAccounts(array $parameters = []);
/**
* Get all accounts of the selected types. Is also capable of handling DataTables' parameters.
*
* @param array $types
* @param array $parameters
*
* @return Collection
*/
public function getAccountsByType(array $types, array $parameters = []);
/**
* Counts the number of accounts found with the included types.
*
* @param array $types
*
* @return int
*/
public function countAccountsByType(array $types);
/**
* Get all default accounts.
*
* @return Collection
*/
public function getDefaultAccounts();
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account);
/**
* @param \Account $account
* @param array $data
*
* @return bool
*/
public function storeInitialBalance(\Account $account, array $data);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace FireflyIII\Database\Ifaces;
/**
* Interface AccountTypeInterface
*
* @package FireflyIII\Database
*/
interface AccountTypeInterface
{
}

View File

@@ -0,0 +1,31 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface BudgetInterface
*
* @package FireflyIII\Database
*/
interface BudgetInterface
{
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return \LimitRepetition|null
*/
public function repetitionOnStartingOnDate(\Budget $budget, Carbon $date);
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function transactionsWithoutBudgetInDateRange(Carbon $start, Carbon $end);
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
/**
* Interface CUD
* @package FireflyIII\Database
*/
interface CUD
{
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model);
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model);
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model);
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data);
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data);
}

View File

@@ -0,0 +1,16 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface CategoryInterface
*
* @package FireflyIII\Database
*/
interface CategoryInterface
{
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
/**
* Interface CommonDatabaseCalls
*
* @package FireflyIII\Database
*/
interface CommonDatabaseCalls
{
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id);
/**
* Returns all objects.
*
* @return Collection
*/
public function get();
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids);
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what);
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FireflyIII\Database\Ifaces;
/**
* Interface PiggybankInterface
*
* @package FireflyIII\Database\Ifaces
*/
interface PiggybankInterface
{
/**
* @param \Account $account
*
* @return float
*/
public function leftOnAccount(\Account $account);
}

View File

@@ -0,0 +1,22 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Carbon\Carbon;
/**
* Interface RecurringInterface
*
* @package FireflyIII\Database
*/
interface RecurringInterface
{
/**
* @param \RecurringTransaction $recurring
* @param Carbon $current
* @param Carbon $currentEnd
*
* @return \TransactionJournal|null
*/
public function getJournalForRecurringInRange(\RecurringTransaction $recurring, Carbon $start, Carbon $end);
}

View File

@@ -0,0 +1,20 @@
<?php
namespace FireflyIII\Database\Ifaces;
/**
* Interface TransactionTypeInterface
*
* @package FireflyIII\Database
*/
interface TransactionCurrencyInterface
{
/**
* @param string $code
*
* @return \TransactionCurrency|null
*/
public function findByCode($code);
}

View File

@@ -0,0 +1,14 @@
<?php
namespace FireflyIII\Database\Ifaces;
/**
* Interface TransactionInterface
*
* @package FireflyIII\Database
*/
interface TransactionInterface
{
}

View File

@@ -0,0 +1,42 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface TransactionJournalInterface
*
* @package FireflyIII\Database
*/
interface TransactionJournalInterface
{
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getInDateRange(Carbon $start, Carbon $end);
/**
* Get the very first transaction journal.
* @return mixed
*/
public function first();
/**
* @param Carbon $date
*
* @return float
*/
public function getSumOfIncomesByMonth(Carbon $date);
/**
* @param Carbon $date
*
* @return float
*/
public function getSumOfExpensesByMonth(Carbon $date);
}

View File

@@ -0,0 +1,14 @@
<?php
namespace FireflyIII\Database\Ifaces;
/**
* Interface TransactionTypeInterface
*
* @package FireflyIII\Database
*/
interface TransactionTypeInterface
{
}

View File

@@ -0,0 +1,250 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use Illuminate\Support\Collection;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\PiggybankInterface;
/**
* Class Piggybank
*
* @package FireflyIII\Database
*/
class Piggybank implements CUD, CommonDatabaseCalls, PiggybankInterface
{
use SwitchUser;
/**
* @param \Account $account
*
* @return float
*/
public function leftOnAccount(\Account $account)
{
$balance = $account->balance();
/** @var \Piggybank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;
}
return $balance;
}
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
$model->delete();
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
/*
* Name validation:
*/
if (!isset($model['name'])) {
$errors->add('name', 'Name is mandatory');
}
if (isset($model['name']) && strlen($model['name']) == 0) {
$errors->add('name', 'Name is too short');
}
if (isset($model['name']) && strlen($model['name']) > 100) {
$errors->add('name', 'Name is too long');
}
if (intval($model['account_id']) == 0) {
$errors->add('account_id', 'Account is mandatory');
}
if ($model['targetdate'] == '' && isset($model['remind_me']) && intval($model['remind_me']) == 1) {
$errors->add('targetdate', 'Target date is mandatory when setting reminders.');
}
if ($model['targetdate'] != '') {
try {
new Carbon($model['targetdate']);
} catch (\Exception $e) {
$errors->add('date', 'Invalid date.');
}
}
if (floatval($model['targetamount']) < 0.01) {
$errors->add('targetamount', 'Amount should be above 0.01.');
}
if (!in_array(ucfirst($model['reminder']), \Config::get('firefly.piggybank_periods'))) {
$errors->add('reminder', 'Invalid reminder period (' . $model['reminder'] . ')');
}
// check period.
if (!$errors->has('reminder') && !$errors->has('targetdate') && isset($model['remind_me']) && intval($model['remind_me']) == 1) {
$today = new Carbon;
$target = new Carbon($model['targetdate']);
switch ($model['reminder']) {
case 'week':
$today->addWeek();
break;
case 'month':
$today->addMonth();
break;
case 'year':
$today->addYear();
break;
}
if ($today > $target) {
$errors->add('reminder', 'Target date is too close to today to set reminders.');
}
}
$validator = \Validator::make($model, \Piggybank::$rules);
if ($validator->invalid()) {
$errors->merge($errors);
}
// add ok messages.
$list = ['name', 'account_id', 'targetamount', 'targetdate', 'remind_me', 'reminder'];
foreach ($list as $entry) {
if (!$errors->has($entry) && !$warnings->has($entry)) {
$successes->add($entry, 'OK');
}
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
$data['rep_every'] = isset($data['rep_every']) ? $data['rep_every'] : 0;
$data['reminder_skip'] = isset($data['reminder_skip']) ? $data['reminder_skip'] : 0;
$data['order'] = isset($data['order']) ? $data['order'] : 0;
$data['remind_me'] = isset($data['remind_me']) ? intval($data['remind_me']) : 0;
$data['startdate'] = isset($data['startdate']) ? $data['startdate'] : Carbon::now()->format('Y-m-d');
$data['targetdate'] = isset($data['targetdate']) && $data['targetdate'] != '' ? $data['targetdate'] : null;
$piggybank = new \Piggybank($data);
if (!$piggybank->validate()) {
var_dump($piggybank->errors()->all());
exit;
}
$piggybank->save();
\Event::fire('piggybanks.store', [$piggybank]);
$piggybank->save();
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
return $this->getUser()->piggybanks()->where('repeats', 0)->get();
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
/** @var \Piggybank $model */
$model->name = $data['name'];
$model->account_id = intval($data['account_id']);
$model->targetamount = floatval($data['targetamount']);
$model->targetdate = isset($data['targetdate']) && $data['targetdate'] != '' ? $data['targetdate'] : null;
$model->rep_every = isset($data['rep_every']) ? $data['rep_every'] : 0;
$model->reminder_skip = isset($data['reminder_skip']) ? $data['reminder_skip'] : 0;
$model->order = isset($data['order']) ? $data['order'] : 0;
$model->remind_me = isset($data['remind_me']) ? intval($data['remind_me']) : 0;
if(!$model->validate()) {
var_dump($model->errors());
exit();
}
$model->save();
return true;
}
}

View File

@@ -0,0 +1,143 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\RecurringInterface;
/**
* Class Recurring
*
* @package FireflyIII\Database
*/
class Recurring implements CUD, CommonDatabaseCalls, RecurringInterface
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param \RecurringTransaction $recurring
* @param Carbon $start
* @param Carbon $end
*
* @return \TransactionJournal|null
*/
public function getJournalForRecurringInRange(\RecurringTransaction $recurring, Carbon $start, Carbon $end)
{
return $this->getUser()->transactionjournals()->where('recurring_transaction_id', $recurring->id)->after($start)->before($end)->first();
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
return $this->getUser()->recurringtransactions()->get();
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FireflyIII\Database;
/**
* Class SwitchUser
*
* @package FireflyIII\Database
*/
trait SwitchUser
{
protected $_user;
public function getUser()
{
return $this->_user;
}
/**
* @param $user
*/
public function setUser($user)
{
$this->_user = $user;
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace FireflyIII\Database;
use Firefly\Exception\FireflyException;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\TransactionInterface;
/**
* Class Transaction
*
* @package FireflyIII\Database
*/
class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls
{
use SwitchUser;
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (!isset($model['account_id']) && !isset($model['account'])) {
$errors->add('account', 'No account present');
}
if (isset($model['account']) && !($model['account'] instanceof \Account)) {
$errors->add('account', 'No valid account present');
}
if (isset($model['account_id']) && intval($model['account_id']) < 0) {
$errors->add('account', 'No valid account_id present');
}
if (isset($model['piggybank_id']) && intval($model['piggybank_id']) < 0) {
$errors->add('piggybank', 'No valid piggybank_id present');
}
if (!isset($model['transaction_journal_id']) && !isset($model['transaction_journal'])) {
$errors->add('transaction_journal', 'No TJ present');
}
if (isset($model['transaction_journal']) && !($model['transaction_journal'] instanceof \TransactionJournal)) {
$errors->add('transaction_journal', 'No valid transaction_journal present');
}
if (isset($model['transaction_journal_id']) && intval($model['transaction_journal_id']) < 0) {
$errors->add('account', 'No valid transaction_journal_id present');
}
if (isset($model['description']) && strlen($model['description']) > 255) {
$errors->add('account', 'Description too long');
}
if (!isset($model['amount'])) {
$errors->add('amount', 'No amount present.');
}
if (isset($model['amount']) && floatval($model['amount']) == 0) {
$errors->add('amount', 'Invalid amount.');
}
if (!$errors->has('account')) {
$successes->add('account', 'OK');
}
if (!$errors->has('')) {
$successes->add('piggybank', 'OK');
}
if (!$errors->has('transaction_journal')) {
$successes->add('transaction_journal', 'OK');
}
if (!$errors->has('amount')) {
$successes->add('amount', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
$transaction = new \Transaction;
$transaction->account()->associate($data['account']);
$transaction->transactionJournal()->associate($data['transaction_journal']);
$transaction->amount = floatval($data['amount']);
if (isset($data['piggybank'])) {
$transaction->piggybank()->associate($data['piggybank']);
}
if (isset($data['description'])) {
$transaction->description = $data['description'];
}
if ($transaction->validate()) {
$transaction->save();
} else {
throw new FireflyException($transaction->errors()->first());
}
return $transaction;
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace FireflyIII\Database;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\TransactionCurrencyInterface;
/**
* Class TransactionType
*
* @package FireflyIII\Database
*/
class TransactionCurrency implements TransactionCurrencyInterface, CUD, CommonDatabaseCalls
{
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement get() method.
}
/**
* @param string $code
*
* @return \TransactionCurrency|null
*/
public function findByCode($code)
{
return \TransactionCurrency::whereCode($code)->first();
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
}

View File

@@ -0,0 +1,334 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\TransactionJournalInterface;
/**
* Class TransactionJournal
*
* @package FireflyIII\Database
*/
class TransactionJournal implements TransactionJournalInterface, CUD, CommonDatabaseCalls
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param Carbon $date
*
* @return float
*/
public function getSumOfIncomesByMonth(Carbon $date)
{
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = \DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('amount', '>', 0)
->where('transaction_types.type', '=', 'Deposit')
->where('transaction_journals.date', '>=', $date->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('transactions.amount');
$sum = floatval($sum);
return $sum;
}
/**
* @param Carbon $date
*
* @return float
*/
public function getSumOfExpensesByMonth(Carbon $date)
{
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = \DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('amount', '>', 0)
->where('transaction_types.type', '=', 'Withdrawal')
->where('transaction_journals.date', '>=', $date->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('transactions.amount');
$sum = floatval($sum);
return $sum;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getInDateRange(Carbon $start, Carbon $end)
{
return $this->getuser()->transactionjournals()->withRelevantData()->before($end)->after($start)->get();
}
/**
* @return TransactionJournal
*/
public function first()
{
return $this->getUser()->transactionjournals()->orderBy('date', 'ASC')->first();
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (!isset($model['what'])) {
$errors->add('description', 'Internal error: need to know type of transaction!');
}
if (isset($model['recurring_transaction_id']) && intval($model['recurring_transaction_id']) < 0) {
$errors->add('recurring_transaction_id', 'Recurring transaction is invalid.');
}
if (!isset($model['description'])) {
$errors->add('description', 'This field is mandatory.');
}
if (isset($model['description']) && strlen($model['description']) == 0) {
$errors->add('description', 'This field is mandatory.');
}
if (isset($model['description']) && strlen($model['description']) > 255) {
$errors->add('description', 'Description is too long.');
}
if (!isset($model['currency'])) {
$errors->add('description', 'Internal error: currency is mandatory!');
}
if (isset($model['date']) && !($model['date'] instanceof Carbon) && strlen($model['date']) > 0) {
try {
new Carbon($model['date']);
} catch (\Exception $e) {
$errors->add('date', 'This date is invalid.');
}
}
if (!isset($model['date'])) {
$errors->add('date', 'This date is invalid.');
}
if (isset($model['to_id']) && intval($model['to_id']) < 0) {
$errors->add('account_to', 'Invalid to-account');
}
if (isset($model['from_id']) && intval($model['from_id']) < 0) {
$errors->add('account_from', 'Invalid from-account');
}
if (isset($model['to']) && !($model['to'] instanceof \Account)) {
$errors->add('account_to', 'Invalid to-account');
}
if (isset($model['from']) && !($model['from'] instanceof \Account)) {
$errors->add('account_from', 'Invalid from-account');
}
if (!isset($model['amount']) || (isset($model['amount']) && floatval($model['amount']) < 0)) {
$errors->add('amount', 'Invalid amount');
}
if (!isset($model['from']) && !isset($model['to'])) {
$errors->add('account_to', 'No accounts found!');
}
$validator = \Validator::make([$model], \Transaction::$rules);
if ($validator->invalid()) {
$errors->merge($errors);
}
/*
* Add "OK"
*/
if (!$errors->has('description')) {
$successes->add('description', 'OK');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
/** @var \FireflyIII\Database\TransactionType $typeRepository */
$typeRepository = \App::make('FireflyIII\Database\TransactionType');
/** @var \FireflyIII\Database\TransactionCurrency $currencyRepository */
$currencyRepository = \App::make('FireflyIII\Database\TransactionCurrency');
/** @var \FireflyIII\Database\Transaction $transactionRepository */
$transactionRepository = \App::make('FireflyIII\Database\Transaction');
$journalType = $typeRepository->findByWhat($data['what']);
$currency = $currencyRepository->findByCode($data['currency']);
$journal = new \TransactionJournal;
$journal->transactionType()->associate($journalType);
$journal->transactionCurrency()->associate($currency);
$journal->user()->associate($this->getUser());
$journal->description = $data['description'];
$journal->date = $data['date'];
$journal->completed = 0;
//$journal->user_id = $this->getUser()->id;
/*
* This must be enough to store the journal:
*/
if (!$journal->validate()) {
\Log::error($journal->errors()->all());
throw new FireflyException('store() transactionjournal failed, but it should not!');
}
$journal->save();
/*
* Then store both transactions.
*/
$first = [
'account' => $data['from'],
'transaction_journal' => $journal,
'amount' => ($data['amount'] * -1),
];
$validate = $transactionRepository->validate($first);
if ($validate['errors']->count() == 0) {
$transactionRepository->store($first);
} else {
throw new FireflyException($validate['errors']->first());
}
$second = [
'account' => $data['to'],
'transaction_journal' => $journal,
'amount' => floatval($data['amount']),
];
$validate = $transactionRepository->validate($second);
if ($validate['errors']->count() == 0) {
$transactionRepository->store($second);
} else {
throw new FireflyException($validate['errors']->first());
}
$journal->completed = 1;
$journal->save();
return $journal;
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
return $this->getUser()->transactionjournals()->find($id);
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
return $this->getUser()->transactionjournals()->get();
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace FireflyIII\Database;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\TransactionTypeInterface;
/**
* Class TransactionType
*
* @package FireflyIII\Database
*/
class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCalls
{
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
switch ($what) {
case 'opening':
return \TransactionType::whereType('Opening balance')->first();
break;
default:
throw new FireflyException('Cannot find transaction type described as "' . e($what) . '".');
break;
}
return null;
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
}
/**
* @param Ardent $model
* @param array $data
*
* @return bool
*/
public function update(Ardent $model, array $data)
{
// TODO: Implement update() method.
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace FireflyIII\Exception;
/**
* Class NotImplementedException
*
* @package FireflyIII\Exception
*/
class NotImplementedException extends \Exception
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace FireflyIII\Shared\Json;
/**
* Class Account
*
* @package FireflyIII\Shared\Json
*/
class Account {
}

View File

@@ -0,0 +1,86 @@
<?php
namespace FireflyIII\Shared\Json;
/**
* Class Json
* @package FireflyIII\Shared\Json
*/
class Json
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters()
{
/*
* Process all parameters!
*/
if (intval(\Input::get('length')) < 0) {
$length = 10000; // we get them all if no length is defined.
} else {
$length = intval(\Input::get('length'));
}
$parameters = [
'start' => intval(\Input::get('start')),
'length' => $length,
'draw' => intval(\Input::get('draw')),
];
/*
* Columns:
*/
if (!is_null(\Input::get('columns')) && is_array(\Input::get('columns'))) {
foreach (\Input::get('columns') as $column) {
$parameters['columns'][] = [
'data' => $column['data'],
'name' => $column['name'],
'searchable' => $column['searchable'] == 'true' ? true : false,
'orderable' => $column['orderable'] == 'true' ? true : false,
'search' => [
'value' => $column['search']['value'],
'regex' => $column['search']['regex'] == 'true' ? true : false,
]
];
}
}
/*
* Sorting.
*/
$parameters['orderOnAccount'] = false;
if (!is_null(\Input::get('order')) && is_array(\Input::get('order'))) {
foreach (\Input::get('order') as $order) {
$columnIndex = intval($order['column']);
$columnName = $parameters['columns'][$columnIndex]['name'];
$parameters['order'][] = [
'name' => $columnName,
'dir' => strtoupper($order['dir'])
];
if ($columnName == 'to' || $columnName == 'from') {
$parameters['orderOnAccount'] = true;
}
}
}
/*
* Search parameters:
*/
$parameters['search'] = [
'value' => '',
'regex' => false
];
if (!is_null(\Input::get('search')) && is_array(\Input::get('search'))) {
$search = \Input::get('search');
$parameters['search'] = [
'value' => $search['value'],
'regex' => $search['regex'] == 'true' ? true : false
];
}
return $parameters;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace FireflyIII\Shared\Preferences;
/**
* Class PreferencesHelper
*
* @package Firefly\Helper\Preferences
*/
class Preferences implements PreferencesInterface
{
/**
* @param $name
* @param null $default
*
* @return null|\Preference
*/
public function get($name, $default = null)
{
$pref = \Preference::where('user_id', \Auth::user()->id)->where('name', $name)->first();
if (is_null($default) && is_null($pref)) {
// return NULL
return null;
}
if (!is_null($pref)) {
return $pref;
}
if (!is_null($default) && is_null($pref)) {
// create preference, return that:
return $this->set($name, $default);
}
return null;
}
/**
* @param $name
* @param $value
*
* @return \Preference
*/
public function set($name, $value)
{
$pref = \Preference::where('user_id', \Auth::user()->id)->where('name', $name)->first();
if (is_null($pref)) {
$pref = new \Preference;
$pref->name = $name;
$pref->user()->associate(\Auth::user());
}
$pref->data = $value;
$pref->save();
return $pref;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace FireflyIII\Shared\Preferences;
/**
* Interface PreferencesHelperInterface
*
* @package Firefly\Helper\Preferences
*/
interface PreferencesInterface
{
/**
* @param $name
* @param $value
*
* @return null|\Preference
*/
public function set($name, $value);
/**
* @param $name
* @param null $default
*
* @return \Preference
*/
public function get($name, $default = null);
}

View File

@@ -0,0 +1,86 @@
<?php
namespace FireflyIII\Shared\Toolkit;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
/**
* Class Date
*
* @package FireflyIII\Shared\Toolkit
*/
class Date
{
/**
* @param Carbon $currentEnd
* @param $repeatFreq
*
* @throws FireflyException
*/
public function endOfPeriod(Carbon $currentEnd, $repeatFreq)
{
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->addDay();
break;
case 'weekly':
$currentEnd->addWeek()->subDay();
break;
case 'monthly':
$currentEnd->addMonth()->subDay();
break;
case 'quarterly':
$currentEnd->addMonths(3)->subDay();
break;
case 'half-year':
$currentEnd->addMonths(6)->subDay();
break;
case 'yearly':
$currentEnd->addYear()->subDay();
break;
}
}
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
*
* @return Carbon
* @throws FireflyException
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip)
{
$add = ($skip + 1);
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do addPeriod for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$date->addDays($add);
break;
case 'weekly':
$date->addWeeks($add);
break;
case 'monthly':
$date->addMonths($add);
break;
case 'quarterly':
$months = $add * 3;
$date->addMonths($months);
break;
case 'half-year':
$months = $add * 6;
$date->addMonths($months);
break;
case 'yearly':
$date->addYears($add);
break;
}
return $date;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace FireflyIII\Shared\Toolkit;
use Illuminate\Support\Collection;
/**
* Class Form
*
* @package FireflyIII\Shared\Toolkit
*/
class Form {
/**
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
* @param null $titleField
*
* @return mixed
*/
public function makeSelectList(Collection $set, $titleField = null)
{
$selectList = [];
/** @var Model $entry */
foreach ($set as $entry) {
$id = intval($entry->id);
$title = null;
if (is_null($titleField)) {
// try 'title' field.
if (isset($entry->title)) {
$title = $entry->title;
}
// try 'name' field
if (is_null($title)) {
$title = $entry->name;
}
// try 'description' field
if (is_null($title)) {
$title = $entry->description;
}
} else {
$title = $entry->$titleField;
}
$selectList[$id] = $title;
}
return $selectList;
}
}

View File

@@ -89,9 +89,9 @@ class Limit extends Ardent
break;
}
$end->subDay();
$count = $this->limitrepetitions()->where('startdate', $start->format('Y-m-d'))->where(
'enddate', $start->format('Y-m-d')
)->count();
$count = $this->limitrepetitions()->where('startdate', $start->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))->count();
\Log::debug('All: '.$this->limitrepetitions()->count().' (#'.$this->id.')');
\Log::debug('Found ' . $count.' limit-reps for limit #' . $this->id.' with start '.$start->format('Y-m-d') .' and end ' . $end->format('Y-m-d'));
if ($count == 0) {
@@ -115,6 +115,12 @@ class Limit extends Ardent
if (isset($repetition->id)) {
\Event::fire('limits.repetition', [$repetition]);
}
} else if($count == 1) {
// update this one:
$repetition = $this->limitrepetitions()->where('startdate', $start->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))->first();
$repetition->amount = $this->amount;
$repetition->save();
}
}

View File

@@ -5,14 +5,14 @@ use LaravelBook\Ardent\Ardent as Ardent;
/**
* LimitRepetition
*
* @property integer $id
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $limit_id
* @property integer $limit_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property float $amount
* @property-read \Limit $limit
* @property float $amount
* @property-read \Limit $limit
* @method static \Illuminate\Database\Query\Builder|\LimitRepetition whereId($value)
* @method static \Illuminate\Database\Query\Builder|\LimitRepetition whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\LimitRepetition whereUpdatedAt($value)
@@ -39,29 +39,27 @@ class LimitRepetition extends Ardent
return ['created_at', 'updated_at', 'startdate', 'enddate'];
}
public function spentInRepetition() {
$sum = \DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->leftJoin('limits', 'limits.component_id', '=', 'components.id')
->leftJoin('limit_repetitions', 'limit_repetitions.limit_id', '=', 'limits.id')
->where('transaction_journals.date', '>=', $this->startdate->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->enddate->format('Y-m-d'))
->where('transactions.amount', '>', 0)
->where('limit_repetitions.id', '=', $this->id)->sum('transactions.amount');
return floatval($sum);
}
/**
* How much money is left in this?
*/
public function leftInRepetition()
{
$left = floatval($this->amount);
return floatval($this->amount - $this->spentInRepetition());
// budget:
$budget = $this->limit->budget;
/** @var \Firefly\Storage\Limit\EloquentLimitRepository $limits */
$limits = App::make('Firefly\Storage\Limit\EloquentLimitRepository');
$set = $limits->getTJByBudgetAndDateRange($budget, $this->startdate, $this->enddate);
foreach ($set as $journal) {
foreach ($journal->transactions as $t) {
if ($t->amount < 0) {
$left += floatval($t->amount);
}
}
}
return $left;
}
/**
@@ -85,8 +83,10 @@ class LimitRepetition extends Ardent
}
switch ($this->repeat_freq) {
default:
throw new \Firefly\Exception\FireflyException('No date formats for frequency "' . $this->repeat_freq
. '"!');
throw new \Firefly\Exception\FireflyException(
'No date formats for frequency "' . $this->repeat_freq
. '"!'
);
break;
case 'daily':
return $this->startdate->format('Ymd') . '-5';
@@ -119,8 +119,10 @@ class LimitRepetition extends Ardent
}
switch ($this->repeat_freq) {
default:
throw new \Firefly\Exception\FireflyException('No date formats for frequency "' . $this->repeat_freq
. '"!');
throw new \Firefly\Exception\FireflyException(
'No date formats for frequency "' . $this->repeat_freq
. '"!'
);
break;
case 'daily':
return $this->startdate->format('j F Y');

View File

@@ -56,6 +56,7 @@ class Piggybank extends Ardent
'rep_times' => 'min:1|max:100', // how many times do you want to save this amount? eg. 3 times
'reminder' => 'in:day,week,month,year', // want a reminder to put money in this?
'reminder_skip' => 'required|min:0|max:100', // every week? every 2 months?
'remind_me' => 'required|boolean',
'order' => 'required:min:1', // not yet used.
];
public $fillable
@@ -71,6 +72,7 @@ class Piggybank extends Ardent
'rep_times',
'reminder',
'reminder_skip',
'remind_me',
'order'
];
@@ -90,7 +92,6 @@ class Piggybank extends Ardent
$rep->targetdate = $target;
$rep->currentamount = 0;
$rep->save();
\Event::fire('piggybanks.repetition', [$rep]);
return $rep;
@@ -104,68 +105,6 @@ class Piggybank extends Ardent
return ['created_at', 'updated_at', 'targetdate', 'startdate'];
}
/**
* Firefly shouldn't create piggybank repetions that completely
* lie in the future, so we should be able to safely grab the "latest"
* one and use that to calculate when the user will be reminded.
*/
public function nextReminderDate()
{
if (is_null($this->reminder)) {
return null;
}
/** @var \PiggybankRepetition $rep */
$rep = $this->currentRelevantRep();
$today = new Carbon;
if ($rep && is_null($rep->startdate)) {
switch ($this->reminder) {
case 'day':
return $today;
break;
case 'week':
return $today->endOfWeek();
break;
case 'month':
return $today->endOfMonth();
break;
case 'year':
return $today->endOfYear();
break;
}
return null;
}
if ($rep && !is_null($rep->startdate)) {
// start with the start date
// when its bigger than today, return it:
$start = clone $rep->startdate;
while ($start <= $today) {
switch ($this->reminder) {
default:
return null;
break;
case 'day':
$start->addDay();
break;
case 'week':
$start->addWeek();
break;
case 'month':
$start->addMonth();
break;
case 'year':
$start->addYear();
break;
}
}
return $start;
}
return new Carbon;
}
/**
* Grabs the PiggyBankRepetition that's currently relevant / active
*
@@ -222,14 +161,6 @@ class Piggybank extends Ardent
return $this->hasMany('PiggybankEvent');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggybankreminders()
{
return $this->hasMany('PiggybankReminder');
}
/**
* Same but for specific date.
*

View File

@@ -1,105 +0,0 @@
<?php
use Carbon\Carbon;
/**
* PiggybankReminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $class
* @property integer $piggybank_id
* @property integer $recurring_transaction_id
* @property integer $user_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property boolean $active
* @property-read \Piggybank $piggybank
* @property-read \RecurringTransaction $recurringTransaction
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder wherePiggybankId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereRecurringTransactionId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereActive($value)
* @method static \Reminder validOn($date)
* @method static \Reminder validOnOrAfter($date)
*/
class PiggybankReminder extends Reminder
{
protected $isSubclass = true;
/**
* This method will render a string telling you something about what to save or something.
* @return string
*/
public function render()
{
/** @var \Piggybank $piggyBank */
$piggyBank = $this->piggybank;
$fullText
= 'In order to save enough money for <a href="' . route('piggybanks.show', $piggyBank->id) . '">"' . e(
$piggyBank->name
) . '"</a> you';
$fullText .= ' should save at least ' . mf($this->amountToSave(), false) . ' this ' . $piggyBank->reminder
. ', before ' . $this->enddate->format('M jS, Y');
return $fullText;
}
/**
* @return float
* @throws Firefly\Exception\FireflyException
*/
public function amountToSave()
{
/** @var \Piggybank $piggyBank */
$piggyBank = $this->piggybank;
/** @var \PiggybankRepetition $repetition */
$repetition = $piggyBank->currentRelevantRep();
// if the target date of the repetition is zero, we use the created_at date of the repetition
// and add two years; it's the same routine used elsewhere.
if (is_null($repetition->targetdate)) {
$targetdate = clone $repetition->created_at;
$targetdate->addYears(2);
} else {
$targetdate = $repetition->targetdate;
}
$today = new Carbon;
$diff = $today->diff($targetdate);
$left = $piggyBank->targetamount - $repetition->currentamount;
// to prevent devide by zero:
$piggyBank->reminder_skip = $piggyBank->reminder_skip < 1 ? 1 : $piggyBank->reminder_skip;
$toSave = 0;
switch ($piggyBank->reminder) {
case 'day':
$toSave = $left;// / ($diff->days / $piggyBank->reminder_skip);
break;
case 'week':
$weeks = ceil($diff->days / 7);
$toSave = $left / ($weeks / $piggyBank->reminder_skip);
break;
case 'month':
$toSave = $left / ($diff->m / $piggyBank->reminder_skip);
break;
case 'year':
throw new \Firefly\Exception\FireflyException('No impl year reminder/ PiggyBankReminder Render');
break;
}
return floatval($toSave);
}
}

View File

@@ -62,15 +62,6 @@ class RecurringTransaction extends Ardent
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function reminders()
{
return $this->hasMany('RecurringTransactionReminder');
}
/**
* @return Carbon
*/

View File

@@ -1,42 +0,0 @@
<?php
/**
* RecurringTransactionReminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $class
* @property integer $piggybank_id
* @property integer $recurring_transaction_id
* @property integer $user_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property boolean $active
* @property-read \Piggybank $piggybank
* @property-read \RecurringTransaction $recurringTransaction
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder wherePiggybankId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereRecurringTransactionId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereActive($value)
* @method static \Reminder validOn($date)
* @method static \Reminder validOnOrAfter($date)
*/
class RecurringTransactionReminder extends Reminder
{
protected $isSubclass = true;
public function render()
{
return '123';
}
}

View File

@@ -2,6 +2,8 @@
use Carbon\Carbon;
use Firefly\Database\SingleTableInheritanceEntity;
use LaravelBook\Ardent\Ardent;
/**
* Reminder
@@ -10,33 +12,24 @@ use Firefly\Database\SingleTableInheritanceEntity;
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $class
* @property integer $piggybank_id
* @property integer $recurring_transaction_id
* @property integer $user_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property boolean $active
* @property-read \Piggybank $piggybank
* @property-read \RecurringTransaction $recurringTransaction
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\Reminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder wherePiggybankId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereRecurringTransactionId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereActive($value)
* @method static \Reminder validOn($date)
* @method static \Reminder validOnOrAfter($date)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereActive($value)
*/
class Reminder extends SingleTableInheritanceEntity
class Reminder extends Ardent
{
protected $table = 'reminders';
protected $subclassField = 'class';
/**
@@ -47,49 +40,6 @@ class Reminder extends SingleTableInheritanceEntity
return ['created_at', 'updated_at', 'startdate', 'enddate'];
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function piggybank()
{
return $this->belongsTo('Piggybank');
}
public function recurringTransaction()
{
return $this->belongsTo('RecurringTransaction');
}
public function render()
{
return '';
}
public function scopeValidOn($query, Carbon $date)
{
return $query->where('startdate', '<=', $date->format('Y-m-d'))->where('enddate', '>=', $date->format('Y-m-d'))
->where('active', 1);
}
public function scopeValidOnOrAfter($query, Carbon $date)
{
return $query->where(
function ($q) use ($date) {
$q->where('startdate', '<=', $date->format('Y-m-d'))->where(
'enddate', '>=', $date->format('Y-m-d')
);
$q->orWhere(
function ($q) use ($date) {
$q->where('startdate', '>=', $date);
$q->where('enddate', '>=', $date);
}
);
}
)->where('active', 1);
}
/**
* User
*

View File

@@ -4,218 +4,6 @@ use Carbon\Carbon;
use LaravelBook\Ardent\Ardent;
use LaravelBook\Ardent\Builder;
/**
* TransactionJournal
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property integer $transaction_type_id
* @property integer $transaction_currency_id
* @property string $description
* @property boolean $completed
* @property \Carbon\Carbon $date
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\Component[] $components
* @property-read \TransactionCurrency $transactionCurrency
* @property-read \TransactionType $transactionType
* @property-read \Illuminate\Database\Eloquent\Collection|\Transaction[] $transactions
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereId($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereTransactionTypeId($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereTransactionCurrencyId($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCompleted($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDate($value)
* @method static \TransactionJournal account($account)
* @method static \TransactionJournal after($date)
* @method static \TransactionJournal before($date)
* @method static \TransactionJournal defaultSorting()
* @method static \TransactionJournal moreThan($amount)
* @method static \TransactionJournal lessThan($amount)
* @method static \TransactionJournal onDate($date)
* @method static \TransactionJournal transactionTypes($types)
* @method static \TransactionJournal withRelevantData()
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property integer $recurring_transaction_id
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \RecurringTransaction $recurringTransaction
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereRecurringTransactionId($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @method static \TransactionJournal accountIs($account)
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
*/
class TransactionJournal extends Ardent
{
@@ -235,7 +23,7 @@ class TransactionJournal extends Ardent
public function budgets()
{
return $this->belongsToMany(
'Budget', 'component_transaction_journal', 'transaction_journal_id', 'component_id'
'Budget', 'component_transaction_journal', 'transaction_journal_id', 'component_id'
);
}
@@ -245,7 +33,7 @@ class TransactionJournal extends Ardent
public function categories()
{
return $this->belongsToMany(
'Category', 'component_transaction_journal', 'transaction_journal_id', 'component_id'
'Category', 'component_transaction_journal', 'transaction_journal_id', 'component_id'
);
}
@@ -257,6 +45,20 @@ class TransactionJournal extends Ardent
return $this->belongsToMany('Component');
}
/**
* @return float
*/
public function getAmount()
{
foreach ($this->transactions as $t) {
if (floatval($t->amount) > 0) {
return floatval($t->amount);
}
}
return -0.01;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
@@ -288,7 +90,7 @@ class TransactionJournal extends Ardent
/**
* @param $query
* @param Carbon $date
* @param Carbon $date
*
* @return mixed
*/
@@ -299,7 +101,7 @@ class TransactionJournal extends Ardent
/**
* @param $query
* @param Carbon $date
* @param Carbon $date
*
* @return mixed
*/
@@ -316,8 +118,10 @@ class TransactionJournal extends Ardent
public function scopeMoreThan(Builder $query, $amount)
{
if (is_null($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id');
$query->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id'
);
$this->joinedTransactions = true;
}
@@ -327,8 +131,10 @@ class TransactionJournal extends Ardent
public function scopeLessThan(Builder $query, $amount)
{
if (is_null($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id');
$query->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id'
);
$this->joinedTransactions = true;
}
@@ -349,8 +155,10 @@ class TransactionJournal extends Ardent
public function scopeTransactionTypes(Builder $query, array $types)
{
if (is_null($this->joinedTransactionTypes)) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id');
$query->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
);
$this->joinedTransactionTypes = true;
}
$query->whereIn('transaction_types.type', $types);
@@ -365,11 +173,11 @@ class TransactionJournal extends Ardent
public function scopeWithRelevantData(Builder $query)
{
$query->with(
['transactions' => function ($q) {
$q->orderBy('amount', 'ASC');
}, 'transactiontype', 'components' => function ($q) {
$q->orderBy('class');
}, 'transactions.account.accounttype','recurringTransaction']
['transactions' => function ($q) {
$q->orderBy('amount', 'ASC');
}, 'transactiontype', 'components' => function ($q) {
$q->orderBy('class');
}, 'transactions.account.accounttype', 'recurringTransaction']
);
}

View File

@@ -78,22 +78,6 @@ class User extends Ardent implements UserInterface, RemindableInterface
return $this->hasMany('Budget');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function reminders()
{
return $this->hasMany('Reminder');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggybankreminders()
{
return $this->hasMany('PiggybankReminder');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@@ -3,172 +3,183 @@
//use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
// models:
Route::bind('account', function($value, $route)
{
if(Auth::check()) {
Route::bind(
'account', function ($value, $route) {
if (Auth::check()) {
$account = Account::
leftJoin('account_types','account_types.id','=','accounts.account_type_id')->
where('account_types.editable',1)->
where('accounts.id', $value)->
where('user_id',Auth::user()->id)->
first(['accounts.*']);
if($account) {
leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')->where('account_types.editable', 1)->where('accounts.id', $value)
->where('user_id', Auth::user()->id)->first(['accounts.*']);
if ($account) {
return $account;
}
}
App::abort(404);
});
Route::bind('accountname', function($value, $route)
{
if(Auth::check()) {
return Account::
leftJoin('account_types','account_types.id','=','accounts.account_type_id')->
where('account_types.editable',1)->
where('name', $value)->
where('user_id',Auth::user()->id)->first();
}
return null;
});
Route::bind('recurring', function($value, $route)
{
if(Auth::check()) {
return RecurringTransaction::
where('id', $value)->
where('user_id',Auth::user()->id)->first();
}
return null;
});
Route::bind('budget', function($value, $route)
{
if(Auth::check()) {
return Budget::
where('id', $value)->
where('user_id',Auth::user()->id)->first();
}
return null;
});
Route::bind('reminder', function($value, $route)
{
if(Auth::check()) {
return Reminder::
where('id', $value)->
where('user_id',Auth::user()->id)->first();
}
return null;
});
Route::bind('category', function($value, $route)
{
if(Auth::check()) {
return Category::
where('id', $value)->
where('user_id',Auth::user()->id)->first();
}
return null;
});
);
Route::bind('tj', function($value, $route)
{
if(Auth::check()) {
Route::bind(
'accountname', function ($value, $route) {
if (Auth::check()) {
return Account::
leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')->where('account_types.editable', 1)->where('name', $value)->where(
'user_id', Auth::user()->id
)->first();
}
return null;
}
);
Route::bind(
'recurring', function ($value, $route) {
if (Auth::check()) {
return RecurringTransaction::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'budget', function ($value, $route) {
if (Auth::check()) {
return Budget::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'reminder', function ($value, $route) {
if (Auth::check()) {
return Reminder::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'category', function ($value, $route) {
if (Auth::check()) {
return Category::
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'tj', function ($value, $route) {
if (Auth::check()) {
return TransactionJournal::
where('id', $value)->
where('user_id',Auth::user()->id)->first();
where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
});
}
);
Route::bind('limit', function($value, $route)
{
if(Auth::check()) {
Route::bind(
'limit', function ($value, $route) {
if (Auth::check()) {
return Limit::
where('limits.id', $value)->
leftJoin('components','components.id','=','limits.component_id')->
where('components.class','Budget')->
where('components.user_id',Auth::user()->id)->first(['limits.*']);
where('limits.id', $value)->leftJoin('components', 'components.id', '=', 'limits.component_id')->where('components.class', 'Budget')->where(
'components.user_id', Auth::user()->id
)->first(['limits.*']);
}
return null;
});
}
);
Route::bind('limitrepetition', function($value, $route)
{
if(Auth::check()) {
Route::bind(
'limitrepetition', function ($value, $route) {
if (Auth::check()) {
return LimitRepetition::
where('limit_repetitions.id', $value)->
leftjoin('limits','limits.id','=','limit_repetitions.limit_id')->
leftJoin('components','components.id','=','limits.component_id')->
where('components.class','Budget')->
where('components.user_id',Auth::user()->id)->first(['limit_repetitions.*']);
where('limit_repetitions.id', $value)->leftjoin('limits', 'limits.id', '=', 'limit_repetitions.limit_id')->leftJoin(
'components', 'components.id', '=', 'limits.component_id'
)->where('components.class', 'Budget')->where('components.user_id', Auth::user()->id)->first(['limit_repetitions.*']);
}
return null;
});
}
);
Route::bind('piggybank', function($value, $route)
{
if(Auth::check()) {
Route::bind(
'piggybank', function ($value, $route) {
if (Auth::check()) {
return Piggybank::
where('piggybanks.id', $value)->
leftJoin('accounts','accounts.id','=','piggybanks.account_id')->
where('accounts.user_id',Auth::user()->id)->first(['piggybanks.*']);
where('piggybanks.id', $value)->leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where('accounts.user_id', Auth::user()->id)
->first(['piggybanks.*']);
}
return null;
});
}
);
// a development route:
Route::get('/dev', ['uses' => 'HomeController@jobDev']);
// protected routes:
Route::group(['before' => 'auth'], function () {
Route::group(
['before' => 'auth'], function () {
// some date routes:
Route::get('/prev',['uses' => 'HomeController@sessionPrev', 'as' => 'sessionPrev']);
Route::get('/next',['uses' => 'HomeController@sessionNext', 'as' => 'sessionNext']);
Route::get('/prev', ['uses' => 'HomeController@sessionPrev', 'as' => 'sessionPrev']);
Route::get('/next', ['uses' => 'HomeController@sessionNext', 'as' => 'sessionNext']);
Route::get('/jump/{range}', ['uses' => 'HomeController@rangeJump', 'as' => 'rangeJump']);
Route::get('/cleanup', ['uses' => 'HomeController@cleanup', 'as' => 'cleanup']);
// account controller:
Route::get('/accounts', ['uses' => 'AccountController@index', 'as' => 'accounts.index']);
Route::get('/accounts/asset', ['uses' => 'AccountController@asset', 'as' => 'accounts.asset']);
Route::get('/accounts/expense', ['uses' => 'AccountController@expense', 'as' => 'accounts.expense']);
Route::get('/accounts/revenue', ['uses' => 'AccountController@revenue', 'as' => 'accounts.revenue']);
Route::get('/accounts/create/{what}', ['uses' => 'AccountController@create', 'as' => 'accounts.create'])->where('what','revenue|asset|expense');
Route::get('/accounts/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']);
Route::get('/accounts/{account}/edit', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']);
Route::get('/accounts/{account}/delete', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']);
Route::get('/accounts/json/{what}', ['uses' => 'AccountController@json', 'as' => 'accounts.json'])->where('what', 'revenue|asset|expense');
Route::get('/accounts/{what}', ['uses' => 'AccountController@index', 'as' => 'accounts.index'])->where('what', 'revenue|asset|expense');
Route::get('/accounts/create/{what}', ['uses' => 'AccountController@create', 'as' => 'accounts.create'])->where('what', 'revenue|asset|expense');
Route::get('/accounts/edit/{account}', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']);
Route::get('/accounts/delete/{account}', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']);
Route::get('/accounts/show/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']);
// budget controller:
Route::get('/budgets/date',['uses' => 'BudgetController@indexByDate','as' => 'budgets.index.date']);
Route::get('/budgets/budget',['uses' => 'BudgetController@indexByBudget','as' => 'budgets.index.budget']);
Route::get('/budgets/create',['uses' => 'BudgetController@create', 'as' => 'budgets.create']);
Route::get('/budgets', ['uses' => 'BudgetController@index', 'as' => 'budgets.index']);
Route::get('/budgets/income', ['uses' => 'BudgetController@updateIncome', 'as' => 'budgets.income']);
Route::get('/budgets/show/{budget}/{limitrepetition?}', ['uses' => 'BudgetController@show', 'as' => 'budgets.show']);
Route::get('/budgets/nobudget/{period}',['uses' => 'BudgetController@nobudget', 'as' => 'budgets.nobudget']);
#Route::get('/budgets/date', ['uses' => 'BudgetController@indexByDate', 'as' => 'budgets.index.date']);
#Route::get('/budgets/budget', ['uses' => 'BudgetController@indexByBudget', 'as' => 'budgets.index.budget']);
Route::get('/budgets/create', ['uses' => 'BudgetController@create', 'as' => 'budgets.create']);
#Route::get('/budgets/nobudget/{period}', ['uses' => 'BudgetController@nobudget', 'as' => 'budgets.nobudget']);
Route::get('/budgets/show/{budget}/{limitrepetition?}',['uses' => 'BudgetController@show', 'as' => 'budgets.show']);
Route::get('/budgets/edit/{budget}',['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']);
Route::get('/budgets/delete/{budget}',['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']);
Route::get('/budgets/edit/{budget}', ['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']);
Route::get('/budgets/delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']);
// category controller:
Route::get('/categories',['uses' => 'CategoryController@index','as' => 'categories.index']);
Route::get('/categories/create',['uses' => 'CategoryController@create','as' => 'categories.create']);
Route::get('/categories/show/{category}',['uses' => 'CategoryController@show','as' => 'categories.show']);
Route::get('/categories/edit/{category}',['uses' => 'CategoryController@edit','as' => 'categories.edit']);
Route::get('/categories/delete/{category}',['uses' => 'CategoryController@delete','as' => 'categories.delete']);
Route::get('/categories', ['uses' => 'CategoryController@index', 'as' => 'categories.index']);
Route::get('/categories/create', ['uses' => 'CategoryController@create', 'as' => 'categories.create']);
Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']);
Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']);
// google chart controller
Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']);
Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']);
Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']);
Route::get('/chart/home/recurring', ['uses' => 'GoogleChartController@recurringTransactionsOverview']);
Route::get('/chart/account/{account}', ['uses' => 'GoogleChartController@accountBalanceChart']);
Route::get('/chart/sankey/{account}/out', ['uses' => 'GoogleChartController@accountSankeyOutChart']);
Route::get('/chart/sankey/{account}/in', ['uses' => 'GoogleChartController@accountSankeyInChart']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/reports/budgets/{year}', ['uses' => 'GoogleChartController@budgetsReportChart']);
Route::get('/chart/budgets/{budget}/spending/{year}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
// google table controller
Route::get('/table/account/{account}/transactions', ['uses' => 'GoogleTableController@transactionsByAccount']);
Route::get('/table/accounts/{what}', ['uses' => 'GoogleTableController@accountList']);
Route::get('/table/budget/{budget}/{limitrepetition?}/transactions', ['uses' => 'GoogleTableController@transactionsByBudget']);
// chart controller
Route::get('/chart/home/account/{account?}', ['uses' => 'ChartController@homeAccount', 'as' => 'chart.home']);
Route::get('/chart/home/categories', ['uses' => 'ChartController@homeCategories', 'as' => 'chart.categories']);
Route::get('/chart/home/budgets', ['uses' => 'ChartController@homeBudgets', 'as' => 'chart.budgets']);
Route::get('/chart/home/info/{accountnameA}/{day}/{month}/{year}', ['uses' => 'ChartController@homeAccountInfo', 'as' => 'chart.info']);
Route::get('/chart/categories/show/{category}', ['uses' => 'ChartController@categoryShowChart','as' => 'chart.showcategory']);
Route::get('/chart/categories/show/{category}', ['uses' => 'ChartController@categoryShowChart', 'as' => 'chart.showcategory']);
// (new charts for budgets)
Route::get('/chart/budget/{budget}/default', ['uses' => 'ChartController@budgetDefault', 'as' => 'chart.budget.default']);
Route::get('chart/budget/{budget}/no_envelope', ['uses' => 'ChartController@budgetNoLimits', 'as' => 'chart.budget.nolimit']);
@@ -187,103 +198,114 @@ Route::group(['before' => 'auth'], function () {
Route::get('/json/revenue', ['uses' => 'JsonController@revenue', 'as' => 'json.revenue']);
Route::get('/json/transfers', ['uses' => 'JsonController@transfers', 'as' => 'json.transfers']);
Route::get('/json/recurring', ['uses' => 'JsonController@recurring', 'as' => 'json.recurring']);
Route::get('/json/recurringjournals/{recurring}', ['uses' => 'JsonController@recurringjournals', 'as' => 'json.recurringjournals']);
// limit controller:
Route::get('/budgets/limits/create/{budget?}',['uses' => 'LimitController@create','as' => 'budgets.limits.create']);
Route::get('/budgets/limits/delete/{limit}',['uses' => 'LimitController@delete','as' => 'budgets.limits.delete']);
Route::get('/budgets/limits/edit/{limit}',['uses' => 'LimitController@edit','as' => 'budgets.limits.edit']);
Route::get('/budgets/limits/create/{budget?}', ['uses' => 'LimitController@create', 'as' => 'budgets.limits.create']);
Route::get('/budgets/limits/delete/{limit}', ['uses' => 'LimitController@delete', 'as' => 'budgets.limits.delete']);
Route::get('/budgets/limits/edit/{limit}', ['uses' => 'LimitController@edit', 'as' => 'budgets.limits.edit']);
Route::get('/migrate',['uses' => 'MigrateController@index', 'as' => 'migrate.index']);
Route::get('/migrate', ['uses' => 'MigrateController@index', 'as' => 'migrate.index']);
// piggy bank controller
Route::get('/piggybanks',['uses' => 'PiggybankController@piggybanks','as' => 'piggybanks.index.piggybanks']);
Route::get('/repeated',['uses' => 'PiggybankController@repeated','as' => 'piggybanks.index.repeated']);
Route::get('/piggybanks/create/piggybank', ['uses' => 'PiggybankController@createPiggybank','as' => 'piggybanks.create.piggybank']);
Route::get('/piggybanks/create/repeated', ['uses' => 'PiggybankController@createRepeated','as' => 'piggybanks.create.repeated']);
Route::get('/piggybanks/addMoney/{piggybank}', ['uses' => 'PiggybankController@addMoney','as' => 'piggybanks.amount.add']);
Route::get('/piggybanks/removeMoney/{piggybank}', ['uses' => 'PiggybankController@removeMoney','as' => 'piggybanks.amount.remove']);
Route::get('/piggybanks/show/{piggybank}', ['uses' => 'PiggybankController@show','as' => 'piggybanks.show']);
Route::get('/piggybanks/edit/{piggybank}', ['uses' => 'PiggybankController@edit','as' => 'piggybanks.edit']);
Route::get('/piggybanks/delete/{piggybank}', ['uses' => 'PiggybankController@delete','as' => 'piggybanks.delete']);
Route::post('/piggybanks/updateAmount/{piggybank}',['uses' => 'PiggybankController@updateAmount','as' => 'piggybanks.updateAmount']);
Route::get('/piggybanks', ['uses' => 'PiggybankController@index', 'as' => 'piggybanks.index']);
Route::get('/piggybanks/add/{piggybank}', ['uses' => 'PiggybankController@add']);
Route::get('/piggybanks/remove/{piggybank}', ['uses' => 'PiggybankController@remove']);
Route::get('/piggybanks/edit/{piggybank}', ['uses' => 'PiggybankController@edit', 'as' => 'piggybanks.edit']);
Route::get('/piggybanks/create', ['uses' => 'PiggybankController@create', 'as' => 'piggybanks.create']);
Route::get('/piggybanks/delete/{piggybank}', ['uses' => 'PiggybankController@delete', 'as' => 'piggybanks.delete']);
// Route::get('/repeated',['uses' => 'PiggybankController@repeated','as' => 'piggybanks.index.repeated']);
// Route::get('/piggybanks/create/repeated', ['uses' => 'PiggybankController@createRepeated','as' => 'piggybanks.create.repeated']);
// Route::get('/piggybanks/addMoney/{piggybank}', ['uses' => 'PiggybankController@addMoney','as' => 'piggybanks.amount.add']);
// Route::get('/piggybanks/removeMoney/{piggybank}', ['uses' => 'PiggybankController@removeMoney','as' => 'piggybanks.amount.remove']);
// Route::get('/piggybanks/show/{piggybank}', ['uses' => 'PiggybankController@show','as' => 'piggybanks.show']);
// Route::get('/piggybanks/delete/{piggybank}', ['uses' => 'PiggybankController@delete','as' => 'piggybanks.delete']);
// Route::post('/piggybanks/updateAmount/{piggybank}',['uses' => 'PiggybankController@updateAmount','as' => 'piggybanks.updateAmount']);
// preferences controller
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
//profile controller
Route::get('/profile', ['uses' => 'ProfileController@index', 'as' => 'profile']);
Route::get('/profile/change-password',['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
Route::get('/profile/change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
// recurring transactions controller
Route::get('/recurring',['uses' => 'RecurringController@index', 'as' => 'recurring.index']);
Route::get('/recurring/show/{recurring}',['uses' => 'RecurringController@show', 'as' => 'recurring.show']);
Route::get('/recurring/create',['uses' => 'RecurringController@create', 'as' => 'recurring.create']);
Route::get('/recurring/edit/{recurring}',['uses' => 'RecurringController@edit','as' => 'recurring.edit']);
Route::get('/recurring/delete/{recurring}',['uses' => 'RecurringController@delete','as' => 'recurring.delete']);
// reminder controller
Route::get('/reminders/dialog',['uses' => 'ReminderController@modalDialog']);
Route::post('/reminders/postpone/{reminder}',['uses' => 'ReminderController@postpone']);
Route::post('/reminders/dismiss/{reminder}',['uses' => 'ReminderController@dismiss']);
Route::get('/reminders/redirect/{reminder}',['uses' => 'ReminderController@redirect']);
Route::get('/recurring', ['uses' => 'RecurringController@index', 'as' => 'recurring.index']);
Route::get('/recurring/show/{recurring}', ['uses' => 'RecurringController@show', 'as' => 'recurring.show']);
Route::get('/recurring/rescan/{recurring}', ['uses' => 'RecurringController@rescan', 'as' => 'recurring.rescan']);
Route::get('/recurring/create', ['uses' => 'RecurringController@create', 'as' => 'recurring.create']);
Route::get('/recurring/edit/{recurring}', ['uses' => 'RecurringController@edit', 'as' => 'recurring.edit']);
Route::get('/recurring/delete/{recurring}', ['uses' => 'RecurringController@delete', 'as' => 'recurring.delete']);
// report controller:
Route::get('/reports',['uses' => 'ReportController@index','as' => 'reports.index']);
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']);
// search controller:
Route::get('/search',['uses' => 'SearchController@index','as' => 'search']);
Route::get('/search', ['uses' => 'SearchController@index', 'as' => 'search']);
// transaction controller:
Route::get('/transactions/create/{what}', ['uses' => 'TransactionController@create', 'as' => 'transactions.create'])->where(['what' => 'withdrawal|deposit|transfer']);
Route::get('/transaction/show/{tj}',['uses' => 'TransactionController@show','as' => 'transactions.show']);
Route::get('/transaction/edit/{tj}',['uses' => 'TransactionController@edit','as' => 'transactions.edit']);
Route::get('/transaction/delete/{tj}',['uses' => 'TransactionController@delete','as' => 'transactions.delete']);
Route::get('/transactions/index',['uses' => 'TransactionController@index','as' => 'transactions.index']);
Route::get('/transactions/expenses',['uses' => 'TransactionController@expenses','as' => 'transactions.expenses']);
Route::get('/transactions/revenue',['uses' => 'TransactionController@revenue','as' => 'transactions.revenue']);
Route::get('/transactions/transfers',['uses' => 'TransactionController@transfers','as' => 'transactions.transfers']);
Route::get('/transactions/create/{what}', ['uses' => 'TransactionController@create', 'as' => 'transactions.create'])->where(
['what' => 'withdrawal|deposit|transfer']
);
Route::get('/transaction/show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'transactions.show']);
Route::get('/transaction/edit/{tj}', ['uses' => 'TransactionController@edit', 'as' => 'transactions.edit']);
Route::get('/transaction/delete/{tj}', ['uses' => 'TransactionController@delete', 'as' => 'transactions.delete']);
Route::get('/transactions/index', ['uses' => 'TransactionController@index', 'as' => 'transactions.index']);
Route::get('/transactions/expenses', ['uses' => 'TransactionController@expenses', 'as' => 'transactions.expenses']);
Route::get('/transactions/revenue', ['uses' => 'TransactionController@revenue', 'as' => 'transactions.revenue']);
Route::get('/transactions/transfers', ['uses' => 'TransactionController@transfers', 'as' => 'transactions.transfers']);
Route::get('/transactions/expenses',['uses' => 'TransactionController@expenses','as' => 'transactions.index.withdrawal']);
Route::get('/transactions/revenue',['uses' => 'TransactionController@revenue','as' => 'transactions.index.deposit']);
Route::get('/transactions/transfers',['uses' => 'TransactionController@transfers','as' => 'transactions.index.transfer']);
Route::get('/transactions/expenses', ['uses' => 'TransactionController@expenses', 'as' => 'transactions.index.withdrawal']);
Route::get('/transactions/revenue', ['uses' => 'TransactionController@revenue', 'as' => 'transactions.index.deposit']);
Route::get('/transactions/transfers', ['uses' => 'TransactionController@transfers', 'as' => 'transactions.index.transfer']);
// user controller
Route::get('/logout', ['uses' => 'UserController@logout', 'as' => 'logout']);
Route::post('budgets/amount/{budget}', ['uses' => 'BudgetController@amount']);
}
);
// protected + csrf routes (POST)
Route::group(['before' => 'csrf|auth'], function () {
Route::group(
['before' => 'csrf|auth'], function () {
// account controller:
Route::post('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']);
Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']);
// budget controller:
Route::post('/budgets/store',['uses' => 'BudgetController@store', 'as' => 'budgets.store']);
Route::post('/budgets/income', ['uses' => 'BudgetController@postUpdateIncome', 'as' => 'budgets.postIncome']);
Route::post('/budgets/update/{budget}', ['uses' => 'BudgetController@update', 'as' => 'budgets.update']);
Route::post('/budgets/store', ['uses' => 'BudgetController@store', 'as' => 'budgets.store']);
Route::post('/budgets/destroy/{budget}', ['uses' => 'BudgetController@destroy', 'as' => 'budgets.destroy']);
// category controller
Route::post('/categories/store',['uses' => 'CategoryController@store', 'as' => 'categories.store']);
Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']);
Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']);
Route::post('/categories/destroy/{category}', ['uses' => 'CategoryController@destroy', 'as' => 'categories.destroy']);
// limit controller:
Route::post('/budgets/limits/store/{budget?}', ['uses' => 'LimitController@store', 'as' => 'budgets.limits.store']);
Route::post('/budgets/limits/destroy/{limit}',['uses' => 'LimitController@destroy','as' => 'budgets.limits.destroy']);
Route::post('/budgets/limits/update/{limit}',['uses' => 'LimitController@update','as' => 'budgets.limits.update']);
Route::post('/budgets/limits/destroy/{limit}', ['uses' => 'LimitController@destroy', 'as' => 'budgets.limits.destroy']);
Route::post('/budgets/limits/update/{limit}', ['uses' => 'LimitController@update', 'as' => 'budgets.limits.update']);
Route::post('/migrate/upload',['uses' => 'MigrateController@upload', 'as' => 'migrate.upload']);
Route::post('/migrate/upload', ['uses' => 'MigrateController@upload', 'as' => 'migrate.upload']);
// piggy bank controller
Route::post('/piggybanks/store/piggybank',['uses' => 'PiggybankController@storePiggybank','as' => 'piggybanks.store.piggybank']);
Route::post('/piggybanks/store/repeated',['uses' => 'PiggybankController@storeRepeated','as' => 'piggybanks.store.repeated']);
Route::post('/piggybanks/update/{piggybank}', ['uses' => 'PiggybankController@update','as' => 'piggybanks.update']);
Route::post('/piggybanks/destroy/{piggybank}', ['uses' => 'PiggybankController@destroy','as' => 'piggybanks.destroy']);
Route::post('/piggybanks/mod/{piggybank}', ['uses' => 'PiggybankController@modMoney','as' => 'piggybanks.modMoney']);
Route::post('/piggybanks/store', ['uses' => 'PiggybankController@store', 'as' => 'piggybanks.store']);
#Route::post('/piggybanks/store/repeated', ['uses' => 'PiggybankController@storeRepeated', 'as' => 'piggybanks.store.repeated']);
Route::post('/piggybanks/update/{piggybank}', ['uses' => 'PiggybankController@update', 'as' => 'piggybanks.update']);
Route::post('/piggybanks/destroy/{piggybank}', ['uses' => 'PiggybankController@destroy', 'as' => 'piggybanks.destroy']);
#Route::post('/piggybanks/mod/{piggybank}', ['uses' => 'PiggybankController@modMoney', 'as' => 'piggybanks.modMoney']);
Route::post('/piggybanks/add/{piggybank}', ['uses' => 'PiggybankController@postAdd', 'as' => 'piggybanks.add']);
Route::post('/piggybanks/remove/{piggybank}', ['uses' => 'PiggybankController@postRemove', 'as' => 'piggybanks.remove']);
// preferences controller
@@ -293,20 +315,23 @@ Route::group(['before' => 'csrf|auth'], function () {
Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword']);
// recurring controller
Route::post('/recurring/store',['uses' => 'RecurringController@store', 'as' => 'recurring.store']);
Route::post('/recurring/update/{recurring}',['uses' => 'RecurringController@update','as' => 'recurring.update']);
Route::post('/recurring/destroy/{recurring}',['uses' => 'RecurringController@destroy','as' => 'recurring.destroy']);
Route::post('/recurring/store', ['uses' => 'RecurringController@store', 'as' => 'recurring.store']);
Route::post('/recurring/update/{recurring}', ['uses' => 'RecurringController@update', 'as' => 'recurring.update']);
Route::post('/recurring/destroy/{recurring}', ['uses' => 'RecurringController@destroy', 'as' => 'recurring.destroy']);
// transaction controller:
Route::post('/transactions/store/{what}', ['uses' => 'TransactionController@store', 'as' => 'transactions.store'])->where(['what' => 'withdrawal|deposit|transfer']);
Route::post('/transaction/update/{tj}',['uses' => 'TransactionController@update','as' => 'transactions.update']);
Route::post('/transaction/destroy/{tj}',['uses' => 'TransactionController@destroy','as' => 'transactions.destroy']);
Route::post('/transactions/store/{what}', ['uses' => 'TransactionController@store', 'as' => 'transactions.store'])->where(
['what' => 'withdrawal|deposit|transfer']
);
Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']);
Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']);
}
);
// guest routes:
Route::group(['before' => 'guest'], function () {
Route::group(
['before' => 'guest'], function () {
// user controller
Route::get('/login', ['uses' => 'UserController@login', 'as' => 'login']);
Route::get('/register', ['uses' => 'UserController@register', 'as' => 'register']);
@@ -319,7 +344,8 @@ Route::group(['before' => 'guest'], function () {
);
// guest + csrf routes:
Route::group(['before' => 'csrf|guest'], function () {
Route::group(
['before' => 'csrf|guest'], function () {
// user controller
Route::post('/login', ['uses' => 'UserController@postLogin']);

View File

@@ -72,30 +72,51 @@ App::down(
);
// forms:
\Form::macro('ffText', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffText($name, $value, $options);
});
\Form::macro('ffSelect', function ($name, array $list = [], $selected = null, array $options = []) {
return \Firefly\Form\Form::ffSelect($name, $list, $selected, $options);
});
\Form::macro('ffInteger', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffInteger($name, $value, $options);
});
\Form::macro('ffAmount', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffAmount($name, $value, $options);
});
\Form::macro('ffDate', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffDate($name, $value, $options);
});
\Form::macro('ffTags', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffTags($name, $value, $options);
});
\Form::macro('ffCheckbox',function ($name, $value = 1, $checked = null, $options = []) {
return \Firefly\Form\Form::ffCheckbox($name, $value, $checked, $options);
});
\Form::macro('ffOptionsList',function ($type, $name) {
return \Firefly\Form\Form::ffOptionsList($type, $name);
});
\Form::macro(
'ffText', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffText($name, $value, $options);
}
);
\Form::macro(
'ffSelect', function ($name, array $list = [], $selected = null, array $options = []) {
return \Firefly\Form\Form::ffSelect($name, $list, $selected, $options);
}
);
\Form::macro(
'ffInteger', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffInteger($name, $value, $options);
}
);
\Form::macro(
'ffAmount', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffAmount($name, $value, $options);
}
);
\Form::macro(
'ffBalance', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffBalance($name, $value, $options);
}
);
\Form::macro(
'ffDate', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffDate($name, $value, $options);
}
);
\Form::macro(
'ffTags', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffTags($name, $value, $options);
}
);
\Form::macro(
'ffCheckbox', function ($name, $value = 1, $checked = null, $options = []) {
return \Firefly\Form\Form::ffCheckbox($name, $value, $checked, $options);
}
);
\Form::macro(
'ffOptionsList', function ($type, $name) {
return \Firefly\Form\Form::ffOptionsList($type, $name);
}
);
/*

View File

@@ -1,18 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p>
<a href="{{route('accounts.create','asset')}}" class="btn btn-success btn-large">Create a new asset account</a>
</p>
@if(count($accounts) > 0)
@include('accounts.list')
<p>
<a href="{{route('accounts.create','asset')}}" class="btn btn-success btn-large">Create a new asset account</a>
</p>
@endif
</div>
</div>
@stop

View File

@@ -4,98 +4,50 @@
{{Form::hidden('what',$what)}}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<h4>Mandatory fields</h4>
<div class="form-group">
{{ Form::label('name', 'Account name', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }}
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
@if($what == 'asset')
<span class="help-block">
Use something descriptive such as "checking account" or "My Bank Main Account".
</span>
@endif
@if($what == 'expense')
<span class="help-block">
Use something descriptive such as "Albert Heijn" or "Amazon".
</span>
@endif
@if($what == 'revenue')
<span class="help-block">
Use something descriptive such as "my mom" or "my job".
</span>
@endif
@endif
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> Mandatory fields
</div>
<div class="panel-body">
{{Form::ffText('name')}}
</div>
</div>
<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 class="col-lg-6 col-md-6 col-sm-12">
@if($what == 'asset')
<h4>Optional fields</h4>
<div class="form-group">
{{ Form::label('openingbalance', 'Opening balance', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
{{Form::input('number','openingbalance', Input::old('openingbalance'), ['step' => 'any', 'class' => 'form-control'])}}
</div>
@if($errors->has('openingbalance'))
<p class="text-danger">{{$errors->first('openingbalance')}}</p>
@else
<span class="help-block">What's the current balance of this new account?</span>
@endif
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
@if($what == 'asset')
{{Form::ffBalance('openingbalance')}}
{{Form::ffDate('openingbalancedate', date('Y-m-d'))}}
@endif
{{Form::ffCheckbox('active','1',true)}}
</div>
</div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: date('Y-m-d'), ['class'
=> 'form-control']) }}
@if($errors->has('openingbalancedate'))
<p class="text-danger">{{$errors->first('openingbalancedate')}}</p>
@else
<span class="help-block">When was this the balance of the new account? Since your bank statements may lag behind, update this date to match the date of the last known balance of the account.</span>
@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">
{{Form::ffOptionsList('create','account')}}
</div>
</div>
@endif
</div>
</div>
<div class="row">
<div class="col-lg-6">
<!-- add another after this one? -->
<div class="form-group">
<label for="create" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('create',1,Input::old('create') == '1')}}
Create another (return to this form)
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Create the {{{$what}}} account</button>
</div>
</div>
</div>
</div>
{{Form::close()}}
@stop

View File

@@ -1,44 +1,38 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Remember that deleting something is permanent.
</p>
</div>
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('accounts.destroy',$account->id)])}}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
@if($account->transactions()->count() > 0)
<p class="text-info">
Account "{{{$account->name}}}" still has {{$account->transactions()->count()}} transaction(s) associated to it.
These will be deleted as well.
</p>
@endif
<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?
</p>
<p class="text-danger">
Press "Delete permanently" If you are sure you want to delete "{{{$account->name}}}".
</p>
@if($account->transactions()->count() > 0)
<p class="text-info">
Account "{{{$account->name}}}" still has {{$account->transactions()->count()}} transaction(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>
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<div class="col-sm-8">
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
@if($account->accountType->type == 'Asset account' || $account->accountType->type == 'Default account')
<a href="{{route('accounts.asset')}}" class="btn-default btn">Cancel</a >
@endif
@if($account->accountType->type == 'Expense account' || $account->accountType->type == 'Beneficiary account')
<a href="{{route('accounts.expense')}}" class="btn-default btn">Cancel</a >
@endif
@if($account->accountType->type == 'Revenue account')
<a href="{{route('accounts.revenue')}}" class="btn-default btn">Cancel</a >
@endif
</div>
</div>
</div>

View File

@@ -11,79 +11,46 @@
{{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update',$account->id)])}}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<h4>Mandatory fields</h4>
<div class="form-group">
{{ Form::label('name', 'Account name', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }}
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span
class="help-block">Use something descriptive such as "checking account" or "Albert Heijn".</span>
@endif
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> Mandatory fields
</div>
<div class="panel-body">
{{Form::ffText('name')}}
</div>
</div>
<p>
<button type="submit" class="btn btn-lg btn-success">
Update account
</button>
</p>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
@if($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account')
<h4>Optional fields</h4>
<div class="form-group">
{{ Form::label('openingbalance', 'Opening balance', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
@if(!is_null($openingBalance))
{{Form::input('number','openingbalance', Input::old('openingbalance') ?: $openingBalance->transactions[1]->amount, ['step' => 'any', 'class' => 'form-control'])}}
@else
{{Form::input('number','openingbalance', Input::old('openingbalance'), ['step' => 'any', 'class' => 'form-control'])}}
@endif
</div>
@if($errors->has('openingbalance'))
<p class="text-danger">{{$errors->first('openingbalance')}}</p>
@else
<span class="help-block">What's the current balance of this new account?</span>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
{{Form::ffCheckbox('active','1')}}
@if($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account')
{{Form::ffBalance('openingbalance')}}
{{Form::ffDate('openingbalancedate')}}
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
@if(!is_null($openingBalance))
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: $openingBalance->date->format('Y-m-d'), ['class' => 'form-control']) }}
@else
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: '', ['class' => 'form-control']) }}
@endif
@if($errors->has('openingbalancedate'))
<p class="text-danger">{{$errors->first('openingbalancedate')}}</p>
@else
<span class="help-block">When was this the balance of the new account? Since your bank statements may lag behind, update this date to match the date of the last known balance of the account.</span>
@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">
{{Form::ffOptionsList('update','account')}}
</div>
</div>
@endif
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Update {{{$account->name}}}</button>
</div>
</div>
</div>
</div>
{{Form::close()}}
@stop

View File

@@ -1,28 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Bla bla expense
</p>
<p class="text-info">
Bla bla bla expense
</p>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p>
<a href="{{route('accounts.create','expense')}}" class="btn btn-success">Create a new expense account</a>
</p>
@if(count($accounts) > 0)
@include('accounts.list')
<p>
<a href="{{route('accounts.create','expense')}}" class="btn btn-success">Create a new expense account</a>
</p>
@endif
</div>
</div>
@stop

View File

@@ -2,42 +2,45 @@
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
<small>Accounts</small>
</h1>
<p class="lead">
Accounts are the record holders for transactions and transfers. Money moves from one account to another.
</p>
<p class="text-info">
In a double-entry bookkeeping system almost <em>everything</em> is an account. Your own personal
bank accounts are representated as accounts (naturally), but the stores you buy stuff at are also
represented as accounts. Likewise, if you have a job, your salary is drawn from their account.
</p>
<p>
<a href="{{route('accounts.create')}}" class="btn btn-success">Create a new account</a>
</p>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> {{{$subTitle}}}
<!-- 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('accounts.create',$what)}}"><i class="fa fa-plus fa-fw"></i> New {{$what}} account</a></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<div id="account-list"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
@if(count($accounts['personal']) > 0)
<h3>Your accounts</h3>
<p style="width:50%;" class="text-info">
These are your personal accounts.
</p>
@stop
@section('scripts')
<script type="text/javascript">
var what = '{{{$what}}}';
</script>
@include('accounts.list',['accounts' => $accounts['personal']])
@endif
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
{{HTML::script('assets/javascript/firefly/gcharts.options.js')}}
{{HTML::script('assets/javascript/firefly/gcharts.js')}}
@if(count($accounts['beneficiaries']) > 0)
<h3>Beneficiaries</h3>
<p style="width:50%;" class="text-info">
These are beneficiaries; places where you spend money or people who pay you.
</p>
@include('accounts.list',['accounts' => $accounts['beneficiaries']])
@endif
</div>
</div>
<script src="assets/javascript/firefly/accounts.js"></script>
@stop
@stop
@section('styles')
@endsection

View File

@@ -1,26 +0,0 @@
<table class="table table-bordered table-striped">
<tr>
<th>&nbsp;</th>
<th style="width:30%;">Name</th>
<th>Current balance</th>
<th></th>
</tr>
@foreach($accounts as $account)
<tr>
<td>
@if($account->active == 0)
<span title="This account is inactive." class="glyphicon glyphicon-ban-circle"></span>
@endif
</td>
<td>
<a href="{{route('accounts.show',$account->id)}}" title="Overview for account {{{$account->name}}}">{{{$account->name}}}</a></td>
<td>{{mf($account->balance())}}</td>
<td>
<span class="btn-group-xs btn-group">
<a href="{{route('accounts.edit',$account->id)}}" title="Edit {{{$account->name}}}" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="{{route('accounts.delete',$account->id)}}" title="Edit {{{$account->name}}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</span>
</td>
</tr>
@endforeach
</table>

View File

@@ -1,28 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Bla bla revenue
</p>
<p class="text-info">
Bla bla bla revenue
</p>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p>
<a href="{{route('accounts.create','revenue')}}" class="btn btn-success">Create a new revenue account</a>
</p>
@if(count($accounts) > 0)
@include('accounts.list')
<p>
<a href="{{route('accounts.create','revenue')}}" class="btn btn-success">Create a new revenue account</a>
</p>
@endif
</div>
</div>
@stop

View File

@@ -4,10 +4,10 @@
<div class="col-lg-8 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}}
<i class="fa fa-fw {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}}
</div>
<div class="panel-body">
<div id="chart"></div>
<div id="overview-chart"></div>
</div>
</div>
</div>
@@ -16,88 +16,26 @@
@include('partials.date_nav')
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
Summary
Out
</div>
<div class="panel-body">
<table class="table table-striped table-condensed">
<tr>
<th></th>
<th>Expense / income</th>
<th>Transfers</th>
</tr>
<tr>
<td>Out</td>
<td>
{{mf($show['statistics']['period']['out'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=expenses"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
<td>
{{mf($show['statistics']['period']['t_out'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=out"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
</tr>
<tr>
<td>In</td>
<td>
{{mf($show['statistics']['period']['in'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=income"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
<td>
{{mf($show['statistics']['period']['t_in'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=in"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
</tr>
<tr>
<td>Difference</td>
<td>{{mf($show['statistics']['period']['diff'])}}</td>
<td>{{mf($show['statistics']['period']['t_diff'])}}</td>
</tr>
</table>
<div id="account-out-sankey"></div>
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
Related
In
</div>
<div class="panel-body">
<table class="table table-striped table-condensed">
@if(count($show['statistics']['accounts']) > 0)
<tr>
<td style="width:30%;">Related accounts</td>
<td>
@foreach($show['statistics']['accounts'] as $acct)
<a href="{{route('accounts.show',$acct->id)}}" class="btn btn-default btn-xs">{{{$acct->name}}}</a>
@endforeach
</td>
</tr>
@endif
@if(isset($show['statistics']['Category']) && count($show['statistics']['Category']) > 0)
<tr>
<td>Related categories</td>
<td>
@foreach($show['statistics']['Category'] as $cat)
<a href="{{route('categories.show',$cat->id)}}" class="btn btn-default btn-xs">{{{$cat->name}}}</a>
@endforeach
</td>
</tr>
@endif
@if(isset($show['statistics']['Budget']) && count($show['statistics']['Budget']) > 0)
<tr>
<td>Related budgets</td>
<td>
@foreach($show['statistics']['Budget'] as $bud)
<a href="{{route('budgets.show',$bud->id)}}?useSession=true" class="btn btn-default btn-xs">{{{$bud->name}}}</a>
@endforeach
</td>
</tr>
@endif
</table>
<div id="account-in-sankey"></div>
</div>
</div>
</div>
@@ -105,23 +43,26 @@
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h4>Transactions <small> For selected account and period</small></h4>
@include('paginated.transactions',['journals' => $show['journals'],'sum' => true])
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-repeat fa-fw"></i> Transactions
</div>
<div class="panel-body">
<div id="account-transactions"></div>
</div>
</div>
</div>
@stop
@section('styles')
{{HTML::style('assets/stylesheets/highslide/highslide.css')}}
@stop
@stop
@section('scripts')
<script type="text/javascript">
var accountID = {{$account->id}};
var accountID = {{{$account->id}}};
</script>
{{HTML::script('assets/javascript/highcharts/highcharts.js')}}
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
{{HTML::script('assets/javascript/firefly/gcharts.options.js')}}
{{HTML::script('assets/javascript/firefly/gcharts.js')}}
{{HTML::script('assets/javascript/firefly/accounts.js')}}
@stop

View File

@@ -1,118 +1,39 @@
@extends('layouts.default')
@section('content')
<div class="row">
<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="text-info">
Firefly uses the <a href="http://en.wikipedia.org/wiki/Envelope_System" class="text-success">envelope system</a>. Every budget
is an envelope in which you put money every [period]. Expenses allocated to each budget are paid from this
(virtual) envelope.
</p>
<p class="text-info">
When the envelope is empty, you must stop spending on the budget. If the envelope still has some money left at the
end of the [period], congratulations! You have saved money!
</p>
</div>
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('budgets.store')])}}
{{Form::hidden('from',e(Input::get('from')))}}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<h4>Mandatory fields</h4>
<div class="form-group">
<label for="name" class="col-sm-4 control-label">Name</label>
<div class="col-sm-8">
<input type="text" name="name" class="form-control" id="name" value="{{Input::old('name')}}" placeholder="Name">
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span class="help-block">For example: groceries, bills</span>
@endif
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-exclamation"></i> Mandatory fields
</div>
<div class="panel-body">
{{Form::ffText('name')}}
</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 class="col-lg-6 col-md-12 col-sm-6">
<h4>Optional fields</h4>
<div class="form-group">
<label for="amount" class="col-sm-4 control-label">Max. amount</label>
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
<input type="number" min="0.01" step="any" name="amount" class="form-control" id="amount" value="{{Input::old('amount')}}">
<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>
@if($errors->has('amount'))
<p class="text-danger">{{$errors->first('amount')}}</p>
@else
<span class="help-block">What's the most you're willing to spend in this budget? This amount is "put" in the virtual
envelope.</span>
@endif
</div>
</div>
<div class="form-group">
<label for="period" class="col-sm-4 control-label">Spending period</label>
<div class="col-sm-8">
{{Form::select('repeat_freq',$periods,Input::old('repeat_freq') ?: 'monthly',['class' => 'form-control'])}}
@if($errors->has('repeat_freq'))
<p class="text-danger">{{$errors->first('repeat_freq')}}</p>
@else
<span class="help-block">How long will the envelope last? A week, a month, or even longer?</span>
@endif
</div>
</div>
<div class="form-group">
<label for="period" class="col-sm-4 control-label">Repeat</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
<input type="checkbox" value="1" name="repeats">
Repeat
</label>
<div class="panel-body">
{{Form::ffOptionsList('create','budget')}}
</div>
@if($errors->has('repeats'))
<p class="text-danger">{{$errors->first('repeats')}}</p>
@else
<span class="help-block">If you want, Firefly can automatically recreate the "envelope" and fill it again
when the timespan above has expired. Be careful with this option though. It makes it easier
to <a href="http://en.wikipedia.org/wiki/Personal_budget#Concepts">fall back to old habits</a>.
Instead, you should recreate the envelope yourself each [period].</span>
@endif
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<!-- add another after this one? -->
<div class="form-group">
<label for="create" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('create',1,Input::old('create') == '1')}}
Create another (return to this form)
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Create the budget</button>
</div>
</div>
</div>
</div>
{{Form::close()}}

View File

@@ -1,42 +1,31 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Remember that deleting something is permanent.
</p>
</div>
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('budgets.destroy',$budget->id)])}}
{{Form::hidden('from',e(Input::get('from')))}}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
@if($budget->transactionjournals()->count() > 0)
<p class="text-info">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red">
<div class="panel-heading">
Delete budget "{{{$budget->name}}}"
</div>
<div class="panel-body">
<p>
Are you sure?
</p>
Account "{{{$budget->name}}}" still has {{$budget->transactionjournals()->count()}} transaction(s) associated to it.
These will NOT be deleted but will lose their connection to the budget.
</p>
@endif
<p class="text-danger">
Press "Delete permanently" If you are sure you want to delete "{{{$budget->name}}}".
</p>
<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>
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<div class="col-sm-8">
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
@if(Input::get('from') == 'date')
<a href="{{route('budgets.index')}}" class="btn-default btn">Cancel</a>
@else
<a href="{{route('budgets.index.budget')}}" class="btn-default btn">Cancel</a>
@endif
</div>
</div>
</div>

View File

@@ -7,40 +7,34 @@
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('budgets.update',$budget->id)])}}
{{Form::hidden('from',e(Input::get('from')))}}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<h4>Mandatory fields</h4>
<div class="form-group">
<label for="name" class="col-sm-4 control-label">Name</label>
<div class="col-sm-8">
<input type="text" name="name" class="form-control" id="name" value="{{Input::old('name') ?: $budget->name}}" placeholder="Name">
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span class="help-block">For example: groceries, bills</span>
@endif
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-fw fa-exclamation"></i> Mandatory fields
</div>
<div class="panel-body">
{{Form::ffText('name')}}
</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="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Update the budget</button>
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
{{Form::ffOptionsList('update','budget')}}
</div>
</div>
</div>
</div>
{{Form::close()}}

View File

@@ -0,0 +1,21 @@
<form style="display: inline;" action="{{route('budgets.postIncome')}}" method="POST">
{{Form::token()}}
<div class="modal-dialog">
<div class="modal-content">
<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>
<h4 class="modal-title" id="myModalLabel">Update (expected) income for {{$date->format('F Y')}}</h4>
</div>
<div class="modal-body">
<div class="input-group">
<div class="input-group-addon"></div>
<input step="any" class="form-control" id="amount" value="{{$amount->data}}" autocomplete="off" name="amount" type="number">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Update</button>
</div>
</div>
</div>
</form>

View File

@@ -0,0 +1,190 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-9 col-sm-8 col-md-8">
<div class="panel panel-default">
<div class="panel-heading">
{{\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">{{mf(300)}}</span></small>
</div>
<div class="col-lg-6 col-md-4 col-sm-3" style="text-align:right;">
<small>Income {{\Session::get('start')->format('F Y')}}:
<a href="#" class="updateIncome"><span id="totalAmount" data-value="{{$budgetAmount->data}}">{{mf($budgetAmount->data)}}</span></a></small>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="progress">
<div class="progress-bar" 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: {{mf($spent)}}</small>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="progress">
@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" 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">
<!-- time based navigation -->
@include('partials.date_nav')
</div>
</div>
<div class="row">
@foreach($budgets as $budget)
<div class="col-lg-3 col-sm-4 col-md-6" style="height:180px;">
<div class="panel panel-default">
<div class="panel-heading">
@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="900" min="0" value="{{$budget->currentRep->amount}}" />
@else
<input type="range" data-id="{{$budget->id}}" data-spent="{{$budget->spent}}" id="budget-range-{{$budget->id}}" max="900" 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->limit > $budget->spent)
<span class="text-success">&euro;</span> <input type="number" min="0" max="900" data-id="{{$budget->id}}" step="1" value="{{$budget->limit}}" style="width:50px;color:#3c763d;" />
@else
<span class="text-danger">&euro;</span> <input type="number" min="0" max="900" data-id="{{$budget->id}}" step="1" value="{{$budget->limit}}" style="width:50px;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;">&euro;</span> <input data-id="{{$budget->id}}" type="number" min="0" max="900" 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: {{mf($budget->spent)}}</span>
</p>
</div>
</div>
</div>
@endforeach
<div class="col-lg-3 col-sm-4 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
Create budget
</div>
<div class="panel-body">
<a href="{{route('budgets.create')}}" class="btn btn-success">Create new budget</a>
</div>
</div>
</div>
@foreach($budgets as $budget)
{{--
<div class="row">
<div class="col-lg-9 col-sm-8 col-md-8">
<div class="row">
<div class="col-lg-3 col-md-4 col-sm-4">
{{$budget->name}}
</div>
<div class="col-lg-7 col-md-4 col-sm-4">
</div>
<div class="col-lg-2 col-md-4 col-sm-3">
<span id="budget-range-display-{{$budget->id}}" data-id="{{$budget->id}}"></span>
</div>
</div>
<div class="row">
<div class="col-lg-7 col-lg-offset-3 col-md-4 col-md-offset-4 col-sm-4 col-sm-offset-4">
@if($budget->pct > 0)
<!-- display a progress bar. -->
<div class="progress" id="budget-progress-{{$budget->id}}" data-spent="{{$budget->spent}}" data-amount="{{$budget->limit}}">
</div>
@else
<!-- do NOT display a progress bar. -->
<div style="display:none;" class="progress" id="budget-progress-{{$budget->id}}" data-spent="{{$budget->spent}}" data-amount="{{$budget->limit}}">
</div>
@endif
<!--
@if($budget->currentRep)
@if($budget->currentRep->amount <= $budget->spent)
Overspent on budget (budgeted: {{$budget->currentRep->amount}}, spent: {{$budget->spent}}).
@else
NOT overspent on budget (budgeted: {{$budget->currentRep->amount}}, spent: {{$budget->spent}}).
@endif
@else
No limit.
@endif
-->
</div>
</div>
</div>
</div>
--}}
@endforeach
<!-- DIALOG -->
<div class="modal fade" id="monthlyBudgetModal">
</div><!-- /.modal -->
@stop
@section('scripts')
{{HTML::script('assets/javascript/firefly/budgets.js')}}
@stop

View File

@@ -1,113 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<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="text-info">
Budgets are groups of expenses that reappear every [period]*. Examples could be "groceries", "bills" or
"drinks with friends". The table below lists all of the budgets you have, if any.
<a href="http://dictionary.reference.com/browse/budget">By definition</a>, budgets are an estimated amount
of money, ie. 400,-. Such an estimation can change over time but <em>should</em> always be set. Budgets
without an actual budget are fairly pointless.
</p>
<p class="text-info">
Use this page to create or change budgets and the estimated amount of money you think is wise. Pages further ahead
will explain what an "envelope" is in the context of budgeting.
</p>
<p class="text-info">
* <small>Every month, week, year, etc.</small>
</p>
<div class="btn-group">
<a class="btn btn-default" href ="{{route('budgets.create')}}?from=budget"><span class="glyphicon glyphicon-plus-sign"></span> Create a new budget</a>
<a class="btn btn-default" href ="{{route('budgets.limits.create')}}?from=budget"><span class="glyphicon glyphicon-plus-sign"></span> Create a new envelope</a>
</div>
</p>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<table class="table table-bordered table-striped">
<tr>
<th>Budget</th>
<th>Current envelope(s)</th>
<th>Update budget</th>
</tr>
@foreach($budgets as $budget)
<tr>
<td>
<a href="{{route('budgets.show',$budget->id)}}">{{{$budget->name}}}</a>
</td>
<td>
<div class="row">
<div class="col-sm-2">
<small>Envelope</small>
</div>
<div class="col-sm-2">
<small>Left</small>
</div>
</div>
@foreach($budget->limits as $limit)
@foreach($limit->limitrepetitions as $index => $rep)
<div class="row">
<div class="col-sm-2">
<span class="label label-primary">
<span class="glyphicon glyphicon-envelope"></span>
{{mf($rep->amount,false)}}</span>
</div>
<div class="col-sm-2">
@if($rep->leftInRepetition() < 0)
<span class="label label-danger">
<span class="glyphicon glyphicon-envelope"></span>
{{mf($rep->leftInRepetition(),false)}}</span>
@else
<span class="label label-success">
<span class="glyphicon glyphicon-envelope"></span>
{{mf($rep->leftInRepetition(),false)}}</span>
@endif
</div>
<div class="col-sm-3">
<small>
<a href="{{route('budgets.show',$budget->id,$rep->id)}}">
{{$rep->periodShow()}}
</a>
</small>
</div>
@if($limit->repeats == 1)
<div class="col-sm-2">
<span class="label label-warning">auto repeats</span>
</div>
@endif
<div class="col-sm-2 @if($limit->repeats == 0) col-sm-offset-2 @endif">
<div class="btn-group btn-group-xs">
<a href="{{route('budgets.limits.edit',$limit->id)}}?from=budget" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span></a>
@if($limit->repeats == 0 || ($limit->repeats == 1 && $index == 0))
<a href="{{route('budgets.limits.delete',$limit->id)}}?from=budget" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
@endif
</div>
</div>
</div>
@endforeach
@endforeach
<p style="margin-top:5px;">
<a href="{{route('budgets.limits.create',$budget->id)}}?from=budget" class="btn btn-default btn-xs"><span
class="glyphicon-plus-sign glyphicon"></span> Add another envelope</a>
</p>
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{{route('budgets.edit',$budget->id)}}?from=budget" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="{{route('budgets.delete',$budget->id)}}?from=budget" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</div>
</td>
</tr>
@endforeach
</table>
</div>
</div>
@stop

View File

@@ -1,87 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<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="text-info">
Budgets are groups of expenses that reappear every [period]*. Examples could be "groceries", "bills" or
"drinks with friends". The table below lists all of the budgets you have, if any.
<a href="http://dictionary.reference.com/browse/budget">By definition</a>, budgets are an estimated amount
of money, ie. 400,-. Such an estimation can change over time but <em>should</em> always be set. Budgets
without an actual budget are fairly pointless.
</p>
<p class="text-info">
Use this page to create or change budgets and the estimated amount of money you think is wise. Pages further ahead
will explain what an "envelope" is in the context of budgeting.
</p>
<p class="text-info">
* <small>Every month, week, year, etc.</small>
</p>
<div class="btn-group">
<a class="btn btn-default" href ="{{route('budgets.create')}}?from=date"><span class="glyphicon glyphicon-plus-sign"></span> Create a new budget</a>
<a class="btn btn-default" href ="{{route('budgets.limits.create')}}?from=date"><span class="glyphicon glyphicon-plus-sign"></span> Create a new envelope</a>
</div>
</div>
</div>
<!-- count = zero! -->
@foreach($budgets as $date => $entry)
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h3><a href="{{route('transactions.index')}}?startdate={{$entry['start']->format('Y-m-d')}}&amp;enddate={{$entry['end']->format('Y-m-d')}}">{{$entry['date']}}</a>
<a class="btn btn-default btn-xs" href ="{{route('budgets.limits.create')}}?startdate={{$entry['start']->format('Y-m-d')}}"><span class="glyphicon glyphicon-plus-sign"></span> Create a new envelope for {{$entry['date']}}</a>
</h3>
<table class="table table-bordered table-striped">
<tr>
<th colspan="2" style="width:45%;">Budget</th>
<th style="width:15%;">Envelope</th>
<th style="width:15%;">Left</th>
<th>&nbsp;</th>
</tr>
@foreach($entry['limitrepetitions'] as $index => $repetition)
<tr>
<td>
<div class="btn-group">
<a title="Edit budget {{{$repetition->limit->budget->name}}}" href="{{route('budgets.edit',$repetition->limit->budget->id)}}?from=date" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-pencil"></span></a>
<a title="Delete budget {{{$repetition->limit->budget->name}}}" href="{{route('budgets.delete',$repetition->limit->budget->id)}}?from=date" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</div>
</td>
<td>
<a href="{{route('budgets.show',[$repetition->limit->budget->id,$repetition->id])}}">
{{{$repetition->limit->budget->name}}}
</a>
</td>
<td>
<span class="label label-primary">
<span class="glyphicon glyphicon-envelope"></span> {{mf($repetition->amount,false)}}</span>
</td>
<td>
@if($repetition->left < 0)
<span class="label label-danger">
<span class="glyphicon glyphicon-envelope"></span>
{{mf($repetition->left,false)}}</span>
@else
<span class="label label-success">
<span class="glyphicon glyphicon-envelope"></span>
{{mf($repetition->left,false)}}</span>
@endif
</td>
<td>
<div class="btn-group">
<a title="Edit envelope for {{{$repetition->limit->budget->name}}} in {{$entry['date']}}" href="{{route('budgets.limits.edit',$repetition->limit->id)}}?from=date" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-pencil"></span></a>
<a title="Delete envelope for {{{$repetition->limit->budget->name}}} in {{$entry['date']}}" href="{{route('budgets.limits.delete',$repetition->limit->id)}}?from=date" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</div>
@if($repetition->limit->repeats == 1)
<span class="label label-warning">auto repeats</span>
@endif
</td>
</tr>
@endforeach
</table>
</div>
</div>
@endforeach
@stop

View File

@@ -1,30 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
@if($view == 'session')
<!-- warning for session date -->
<p class="bg-primary" style="padding:15px;">
This view is filtered to only show transactions between {{Session::get('start')->format('d M Y')}}
and {{Session::get('end')->format('d M Y')}}.
</p>
@endif
</div>
</div>
@if($transactions->count() > 0)
<div class="row">
<div class="col-lg-12">
@include('lists.transactions',['journals' => $transactions,'sum' => true])
</div>
</div>
@else
<div class="row">
<div class="col-lg-12">
<h4>{{$repetition['date']}}
</h4>
<p><em>No transactions</em></p>
</div>
</div>
@endif
@stop

View File

@@ -1,120 +1,92 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">Budgets can help you cut back on spending.</p>
<div class="col-lg-9 col-md-9 col-sm-7">
<div class="panel panel-default">
<div class="panel-heading">
Some stuff?
</div>
<div class="panel-body">
<div id="budgetOverview"></div>
</div>
</div>
@if($view == 1)
<!-- warning for selected limit -->
<p class="bg-primary" style="padding:15px;">
This view is filtered to show only the envelope from
{{{$repetitions[0]['limitrepetition']->periodShow()}}},
which contains {{mf($repetitions[0]['limit']->amount,false)}}.
</p>
<div class="panel panel-default">
<div class="panel-heading">
Transactions
</div>
<div class="panel-body">
<div id="transactions"></div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-5">
@foreach($limits as $limit)
@foreach($limit->limitrepetitions as $rep)
<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: {{mf($rep->amount)}}
</div>
<div class="col-lg-6 col-md-6 col-sm-6">
Spent: {{mf($rep->spentInRepetition())}}
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<?php
$overspent = $rep->spentInRepetition() > $rep->amount;
?>
@if($overspent)
<?php
$pct = $rep->amount / $rep->spentInRepetition()*100;
?>
<div class="progress progress-striped">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{ceil($pct)}}" aria-valuemin="0" aria-valuemax="100" style="width: {{ceil($pct)}}%;"></div>
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{100-ceil($pct)}}" aria-valuemin="0" aria-valuemax="100" style="width: {{100-ceil($pct)}}%;"></div>
</div>
@else
<?php
$pct = $rep->spentInRepetition() / $rep->amount*100;
?>
<div class="progress progress-striped">
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{ceil($pct)}}" aria-valuemin="0" aria-valuemax="100" style="width: {{ceil($pct)}}%;">
</div>
</div>
@endif
</div>
</div>
</div>
</div>
@endforeach
@endforeach
@endif
@if($view == 2)
<!-- warning for non-caught only -->
<p class="bg-primary" style="padding:15px;">
This view is filtered to show transactions not in an envelope only.
</p>
@endif
@if($view == 3)
<!-- warning for session date -->
<p class="bg-primary" style="padding:15px;">
This view is filtered to only show transactions between {{Session::get('start')->format('d M Y')}}
and {{Session::get('end')->format('d M Y')}}.
</p>
@endif
@if($view != 4)
<p class="bg-info" style="padding:15px;">
<a class="btn btn-default btn-sm" href="{{route('budgets.show',$budget->id)}}">Reset the filter</a>
</p>
@endif
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div id="chart"></div>
@if($view == 1)
<div id="instr" data-type="envelope" data-envelope="{{$repetitions[0]['limitrepetition']->id}}"></div>
@endif
@if($view == 2)
<div id="instr" data-type="no_envelope" data-budget="{{$budget->id}}"></div>
@endif
@if($view == 3)
<div id="instr" data-type="session" data-budget="{{$budget->id}}"></div>
@endif
@if($view == 4)
<div id="instr" data-type="default" data-budget="{{$budget->id}}"></div>
@endif
</div>
</div>
@foreach($repetitions as $repetition)
@if(isset($repetition['journals']) && count($repetition['journals']) > 0)
<div class="row">
<div class="col-lg-12">
@if($repetition['paginated'] == true)
<h4>
<a href="{{route('budgets.show',$budget->id)}}?noenvelope=true">
{{$repetition['date']}}</a> <small>paginated</small></h4>
@else
<h4>
<a href="{{route('budgets.show',$budget->id,$repetition['limitrepetition']->id)}}">
{{$repetition['date']}}
</a>
</h4>
<small>{{mf($repetition['limit']->amount,false)}}
(left: {{mf($repetition['limitrepetition']->leftInRepetition(),false)}})</small>
@endif
</h4>
@if($repetition['paginated'] == true)
@include('paginated.transactions',['journals' => $repetition['journals'],'highlight' => $highlight])
@else
@include('lists.transactions',['journals' => $repetition['journals'],'sum' => true,'highlight' => $highlight])
@endif
</div>
</div>
@else
<div class="row">
<div class="col-lg-12">
<h4>{{$repetition['date']}}
</h4>
<p><em>No transactions</em></p>
</div>
</div>
@endif
@endforeach
@stop
@section('scripts')
{{HTML::script('assets/javascript/highcharts/highcharts.js')}}
@if($view == 1)
{{HTML::script('assets/javascript/firefly/budgets/limit.js')}}
@endif
<script type="text/javascript">
var budgetID = {{$budget->id}};
@if(!is_null($repetition))
var repetitionID = {{$repetition->id}};
var year = {{$repetition->startdate->format('Y')}};
@else
var year = {{Session::get('start')->format('Y')}};
@endif
@if($view == 2)
{{HTML::script('assets/javascript/firefly/budgets/nolimit.js')}}
@endif
</script>
@if($view == 3)
{{HTML::script('assets/javascript/firefly/budgets/session.js')}}
@endif
@if($view == 4)
{{HTML::script('assets/javascript/firefly/budgets/default.js')}}
@endif
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
{{HTML::script('assets/javascript/firefly/gcharts.options.js')}}
{{HTML::script('assets/javascript/firefly/gcharts.js')}}
{{HTML::script('assets/javascript/firefly/budgets.js')}}
@stop

View File

@@ -8,7 +8,7 @@
Expenses grouped in categories do not have to reoccur every month or every week, like budgets.
</p>
</div>
</div>
</div><!-- TODO cleanup to match new theme & form -->
{{Form::open(['class' => 'form-horizontal','url' => route('categories.store')])}}

View File

@@ -6,7 +6,7 @@
Remember that deleting something is permanent.
</p>
</div>
</div>
</div><!-- TODO cleanup to match new theme & form -->
{{Form::open(['class' => 'form-horizontal','url' => route('categories.destroy',$category->id)])}}
<div class="row">

View File

@@ -4,7 +4,7 @@
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">Use categories to group your expenses</p>
</div>
</div>
</div><!-- TODO cleanup to match new theme & form -->
{{Form::open(['class' => 'form-horizontal','url' => route('categories.update',$category->id)])}}

View File

@@ -11,7 +11,7 @@
<a href="{{route('categories.create')}}" class="btn btn-success"><span class="glyphicon glyphicon-plus"></span> Create a new category</a>
</p>
</div>
</div>
</div><!-- TODO cleanup to match new theme & form -->
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">

View File

@@ -15,13 +15,13 @@
transactions for the currently selected period.
</p>
</div>
</div>
</div><!-- TODO cleanup to match new theme & form -->
@include('partials.date_nav')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div id="chart"><p class="small text-center">(Some chart here)</p></div>
<div id="chart"><img src="http://placehold.it/650x300" title="Placeholder" alt="" /></div>
</div>
</div>
@@ -40,6 +40,5 @@
<script type="text/javascript">
var categoryID = {{$category->id}};
</script>
{{HTML::script('assets/javascript/highcharts/highcharts.js')}}
{{HTML::script('assets/javascript/firefly/categories.js')}}
@stop

View File

@@ -9,4 +9,4 @@
<td>{{mf($entry['amount']*-1)}}</td>
</tr>
@endforeach
</table>
</table><!-- TODO remove me -->

View File

@@ -12,3 +12,4 @@
</div>
</body>
</html>
<!-- TODO remove me -->

View File

@@ -20,7 +20,7 @@
</p>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<h2><a href="{{route('accounts.create')}}">Start from scratch</a></h2>
<h2><a href="{{route('accounts.create','asset')}}">Start from scratch</a></h2>
<p>
Use this option if you are new to Firefly (III).
@@ -29,83 +29,108 @@
@else
<!-- ACCOUNTS -->
<div class="row">
<div class="col-lg-8 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-credit-card fa-fw"></i> <a href="#">Your accounts</a>
</div>
<div class="panel-body">
<div id="accounts-chart" style="height:300px;"></div>
</div>
<div class="row">
<div class="col-lg-8 col-md-12 col-sm-12">
<!-- ACCOUNTS -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-credit-card fa-fw"></i> <a href="#">Your accounts</a>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-tasks fa-fw"></i> <a href="{{route('budgets.index.date')}}">Budgets and spending</a>
</div>
<div class="panel-body">
<div id="budgets"></div>
</div>
<div class="panel-body">
<div id="accounts-chart"></div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bar-chart fa-fw"></i> <a href="{{route('categories.index')}}">Categories</a>
</div>
<div class="panel-body">
<div id="categories"></div>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 col-sm-12">
<!-- time based navigation -->
@include('partials.date_nav')
<!-- TRANSACTIONS -->
@foreach($transactions as $data)
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-money fa-fw"></i>
<a href="{{route('accounts.show',$data[1]->id)}}">{{{$data[1]->name}}}</a>
<!-- 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('transactions.create','withdrawal')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-left fa-fw"></i> New withdrawal</a></li>
<li><a href="{{route('transactions.create','deposit')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-right fa-fw"></i> New deposit</a></li>
<li><a href="{{route('transactions.create','transfer')}}?account_from_id={{{$data[1]->id}}}"><i class="fa fa-arrows-h fa-fw"></i> New transfer</a></li>
</ul>
</div>
<!-- BUDGETS -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-tasks fa-fw"></i> <a href="{{route('budgets.index')}}">Budgets and spending</a>
</div>
<div class="panel-body">
<div id="budgets-chart"></div>
</div>
</div>
<!-- CATEGORIES -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bar-chart fa-fw"></i> <a href="{{route('categories.index')}}">Categories</a>
</div>
<div class="panel-body">
<div id="categories-chart"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-line-chart"></i> Savings
</div>
<div class="panel-body">
(todo)
</div>
</div>
<div class="panel-body">
@include('transactions.journals-small-index',['transactions' => $data[0],'account' => $data[1]])
</div>
</div>
@endforeach
</div>
</div>
<div class="col-lg-4 col-md-6 col-sm-12">
<!-- time based navigation -->
@include('partials.date_nav')
@endif
<!-- REMINDERS -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-line-chart"></i> Recurring transactions
</div>
<div class="panel-body">
<div id="recurring-chart"></div>
</div>
</div>
@stop
@section('scripts')
{{HTML::script('assets/javascript/highcharts/highcharts.js')}}
{{HTML::script('assets/javascript/firefly/index.js')}}
@stop
@section('styles')
{{HTML::style('assets/stylesheets/highslide/highslide.css')}}
@stop
<!-- TRANSACTIONS -->
@foreach($transactions as $data)
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-money fa-fw"></i>
<a href="{{route('accounts.show',$data[1]->id)}}">{{{$data[1]->name}}}</a>
<!-- 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('transactions.create','withdrawal')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-left fa-fw"></i> New withdrawal</a></li>
<li><a href="{{route('transactions.create','deposit')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-right fa-fw"></i> New deposit</a></li>
<li><a href="{{route('transactions.create','transfer')}}?account_from_id={{{$data[1]->id}}}"><i class="fa fa-arrows-h fa-fw"></i> New transfer</a></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
@include('transactions.journals-small-index',['transactions' => $data[0],'account' => $data[1]])
</div>
</div>
@endforeach
</div>
</div>
@endif
@stop
@section('scripts')
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
{{HTML::script('assets/javascript/firefly/gcharts.options.js')}}
{{HTML::script('assets/javascript/firefly/gcharts.js')}}
{{HTML::script('assets/javascript/firefly/index.js')}}
@stop
@section('styles')
@stop

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