diff --git a/.coveralls.yml b/.coveralls.yml index eb8e63b57e..482521d6ec 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1 +1,2 @@ src_dir: . +coverage_clover: tests/_output/coverage.xml \ No newline at end of file diff --git a/.gitignore b/.gitignore index a6133503cd..7c2baa148d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ index.html* app/storage/firefly-export* .vagrant firefly-iii-import-*.json + +tests/_output/* +testing.sqlite +c3.php diff --git a/.travis.yml b/.travis.yml index 48aa3ad7af..835ec484b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,14 @@ language: php php: - 5.5 - - 5.4 + - 5.6 - hhvm install: - composer install -after_script: - - php vendor/bin/coveralls +script: + - php vendor/bin/codecept run +after_script: + - php vendor/bin/coveralls \ No newline at end of file diff --git a/README.md b/README.md index 2085e155c5..6d44b33cf8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -firefly-iii +Firefly III =========== [![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii) diff --git a/app/breadcrumbs.php b/app/breadcrumbs.php index 0aff5d9441..9772319edf 100644 --- a/app/breadcrumbs.php +++ b/app/breadcrumbs.php @@ -1,7 +1,363 @@ push('Home', route('index')); -}); \ No newline at end of file +Breadcrumbs::register( + 'home', + function (Generator $breadcrumbs) { + + $breadcrumbs->push('Home', route('index')); + } +); + +// accounts +Breadcrumbs::register( + 'accounts.index', function (Generator $breadcrumbs, $what) { + $breadcrumbs->parent('home'); + $breadcrumbs->push(ucfirst($what) . ' accounts', route('accounts.index', $what)); + } +); +Breadcrumbs::register( + 'accounts.show', function (Generator $breadcrumbs, \Account $account) { + switch ($account->accountType->type) { + default: + throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"'); + break; + case 'Default account': + case 'Asset account': + $what = 'asset'; + break; + case 'Expense account': + case 'Beneficiary account': + $what = 'expense'; + break; + case 'Revenue account': + $what = 'revenue'; + break; + } + $breadcrumbs->parent('accounts.index', $what); + $breadcrumbs->push($account->name, route('accounts.show', $account->id)); + } +); +Breadcrumbs::register( + 'accounts.delete', function (Generator $breadcrumbs, \Account $account) { + $breadcrumbs->parent('accounts.show', $account); + $breadcrumbs->push('Delete ' . $account->name, route('accounts.delete', $account->id)); + } +); + +Breadcrumbs::register( + 'accounts.edit', function (Generator $breadcrumbs, \Account $account) { + $breadcrumbs->parent('accounts.show', $account); + $breadcrumbs->push('Edit ' . $account->name, route('accounts.edit', $account->id)); + } +); + +// budgets. +Breadcrumbs::register( + 'budgets.index', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Budgets', route('budgets.index')); + } +); +Breadcrumbs::register( + 'budgets.create', function (Generator $breadcrumbs) { + $breadcrumbs->parent('budgets.index'); + $breadcrumbs->push('Create new budget', route('budgets.create')); + } +); + +Breadcrumbs::register( + 'budgets.edit', function (Generator $breadcrumbs, Budget $budget) { + $breadcrumbs->parent('budgets.show', $budget); + $breadcrumbs->push('Edit ' . $budget->name, route('budgets.edit', $budget->id)); + } +); +Breadcrumbs::register( + 'budgets.delete', function (Generator $breadcrumbs, Budget $budget) { + $breadcrumbs->parent('budgets.show', $budget); + $breadcrumbs->push('Delete ' . $budget->name, route('budgets.delete', $budget->id)); + } +); + +Breadcrumbs::register( + 'budgets.show', function (Generator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) { + $breadcrumbs->parent('budgets.index'); + $breadcrumbs->push($budget->name, route('budgets.show', $budget->id)); + if (!is_null($repetition)) { + $breadcrumbs->push( + DateKit::periodShow($repetition->startdate, $repetition->limit->repeat_freq), route('budgets.show', $budget->id, $repetition->id) + ); + } + } +); + +// categories +Breadcrumbs::register( + 'categories.index', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Categories', route('categories.index')); + } +); +Breadcrumbs::register( + 'categories.create', function (Generator $breadcrumbs) { + $breadcrumbs->parent('categories.index'); + $breadcrumbs->push('Create new category', route('categories.create')); + } +); + +Breadcrumbs::register( + 'categories.edit', function (Generator $breadcrumbs, Category $category) { + $breadcrumbs->parent('categories.show', $category); + $breadcrumbs->push('Edit ' . $category->name, route('categories.edit', $category->id)); + } +); +Breadcrumbs::register( + 'categories.delete', function (Generator $breadcrumbs, Category $category) { + $breadcrumbs->parent('categories.show', $category); + $breadcrumbs->push('Delete ' . $category->name, route('categories.delete', $category->id)); + } +); + +Breadcrumbs::register( + 'categories.show', function (Generator $breadcrumbs, Category $category) { + $breadcrumbs->parent('categories.index'); + $breadcrumbs->push($category->name, route('categories.show', $category->id)); + + } +); + + +// piggy banks +Breadcrumbs::register( + 'piggybanks.index', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Piggy banks', route('piggybanks.index')); + } +); +Breadcrumbs::register( + 'piggybanks.create', function (Generator $breadcrumbs) { + $breadcrumbs->parent('piggybanks.index'); + $breadcrumbs->push('Create new piggy bank', route('piggybanks.create')); + } +); + +Breadcrumbs::register( + 'piggybanks.edit', function (Generator $breadcrumbs, Piggybank $piggybank) { + $breadcrumbs->parent('piggybanks.show', $piggybank); + $breadcrumbs->push('Edit ' . $piggybank->name, route('piggybanks.edit', $piggybank->id)); + } +); +Breadcrumbs::register( + 'piggybanks.delete', function (Generator $breadcrumbs, Piggybank $piggybank) { + $breadcrumbs->parent('piggybanks.show', $piggybank); + $breadcrumbs->push('Delete ' . $piggybank->name, route('piggybanks.delete', $piggybank->id)); + } +); + +Breadcrumbs::register( + 'piggybanks.show', function (Generator $breadcrumbs, Piggybank $piggybank) { + $breadcrumbs->parent('piggybanks.index'); + $breadcrumbs->push($piggybank->name, route('piggybanks.show', $piggybank->id)); + + } +); + +// preferences +Breadcrumbs::register( + 'preferences', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Preferences', route('preferences')); + + } +); + +// profile +Breadcrumbs::register( + 'profile', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Profile', route('profile')); + + } +); +Breadcrumbs::register( + 'change-password', function (Generator $breadcrumbs) { + $breadcrumbs->parent('profile'); + $breadcrumbs->push('Change your password', route('change-password')); + + } +); + +// recurring transactions +Breadcrumbs::register( + 'recurring.index', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Recurring transactions', route('recurring.index')); + } +); +Breadcrumbs::register( + 'recurring.create', function (Generator $breadcrumbs) { + $breadcrumbs->parent('recurring.index'); + $breadcrumbs->push('Create new recurring transaction', route('recurring.create')); + } +); + +Breadcrumbs::register( + 'recurring.edit', function (Generator $breadcrumbs, RecurringTransaction $recurring) { + $breadcrumbs->parent('recurring.show', $recurring); + $breadcrumbs->push('Edit ' . $recurring->name, route('recurring.edit', $recurring->id)); + } +); +Breadcrumbs::register( + 'recurring.delete', function (Generator $breadcrumbs, RecurringTransaction $recurring) { + $breadcrumbs->parent('recurring.show', $recurring); + $breadcrumbs->push('Delete ' . $recurring->name, route('recurring.delete', $recurring->id)); + } +); + +Breadcrumbs::register( + 'recurring.show', function (Generator $breadcrumbs, RecurringTransaction $recurring) { + $breadcrumbs->parent('recurring.index'); + $breadcrumbs->push($recurring->name, route('recurring.show', $recurring->id)); + + } +); + +// reminders +Breadcrumbs::register( + 'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id)); + + } +); + +// repeated expenses +Breadcrumbs::register( + 'repeated.index', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Repeated expenses', route('repeated.index')); + } +); +Breadcrumbs::register( + 'repeated.create', function (Generator $breadcrumbs) { + $breadcrumbs->parent('repeated.index'); + $breadcrumbs->push('Create new repeated expense', route('repeated.create')); + } +); + +Breadcrumbs::register( + 'repeated.edit', function (Generator $breadcrumbs, Piggybank $piggybank) { + $breadcrumbs->parent('repeated.show', $piggybank); + $breadcrumbs->push('Edit ' . $piggybank->name, route('repeated.edit', $piggybank->id)); + } +); +Breadcrumbs::register( + 'repeated.delete', function (Generator $breadcrumbs, Piggybank $piggybank) { + $breadcrumbs->parent('repeated.show', $piggybank); + $breadcrumbs->push('Delete ' . $piggybank->name, route('repeated.delete', $piggybank->id)); + } +); + +Breadcrumbs::register( + 'repeated.show', function (Generator $breadcrumbs, Piggybank $piggybank) { + $breadcrumbs->parent('repeated.index'); + $breadcrumbs->push($piggybank->name, route('repeated.show', $piggybank->id)); + + } +); + +// reports +Breadcrumbs::register( + 'reports.index', function (Generator $breadcrumbs) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Reports', route('reports.index')); + } +); + +Breadcrumbs::register( + 'reports.year', function (Generator $breadcrumbs, Carbon $date) { + $breadcrumbs->parent('reports.index'); + $breadcrumbs->push($date->format('Y'), route('reports.year', $date->format('Y'))); + } +); +Breadcrumbs::register( + 'reports.budgets', function (Generator $breadcrumbs, Carbon $date) { + $breadcrumbs->parent('reports.index'); + $breadcrumbs->push('Budgets in ' . $date->format('F Y'), route('reports.budgets', $date->format('Y'))); + } +); +Breadcrumbs::register( + 'reports.unbalanced', function (Generator $breadcrumbs, Carbon $date) { + $breadcrumbs->parent('reports.index'); + $breadcrumbs->push('Unbalanced transactions in ' . $date->format('F Y'), route('reports.unbalanced', $date->format('Y'))); + } +); +// search +Breadcrumbs::register( + 'search', function (Generator $breadcrumbs, $query) { + $breadcrumbs->parent('home'); + $breadcrumbs->push('Search for "' . e($query) . '"', route('search')); + } +); + +// transactions +Breadcrumbs::register( + 'transactions.index', function (Generator $breadcrumbs, $what) { + $breadcrumbs->parent('home'); + + switch ($what) { + case 'expenses': + case 'withdrawal': + $subTitle = 'Expenses'; + break; + case 'revenue': + case 'deposit': + $subTitle = 'Revenue, income and deposits'; + break; + case 'transfer': + case 'transfers': + $subTitle = 'Transfers'; + break; + case 'opening balance': + $subTitle = 'Opening balances'; + break; + default: + throw new FireflyException('Cannot handle $what "'.e($what).'" in bread crumbs'); + } + + $breadcrumbs->push($subTitle, route('transactions.index', $what)); + } +); +Breadcrumbs::register( + 'transactions.create', function (Generator $breadcrumbs, $what) { + $breadcrumbs->parent('transactions.index', $what); + $breadcrumbs->push('Create new ' . $what, route('transactions.create', $what)); + } +); + +Breadcrumbs::register( + 'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) { + $breadcrumbs->parent('transactions.show', $journal); + $breadcrumbs->push('Edit ' . $journal->description, route('transactions.edit', $journal ->id)); + } +); +Breadcrumbs::register( + 'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) { + $breadcrumbs->parent('transactions.show', $journal); + $breadcrumbs->push('Delete ' . $journal->description, route('transactions.delete', $journal->id)); + } +); + +Breadcrumbs::register( + 'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) { + + $breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type)); + $breadcrumbs->push($journal->description, route('transactions.show', $journal->id)); + + } +); \ No newline at end of file diff --git a/app/commands/Cleanup.php b/app/commands/Cleanup.php new file mode 100644 index 0000000000..2e5c889068 --- /dev/null +++ b/app/commands/Cleanup.php @@ -0,0 +1,77 @@ +info('Start!'); + Artisan::call('clear-compiled'); + $this->info('Cleared compiled...'); + Artisan::call('ide-helper:generate'); + $this->info('IDE helper, done...'); + Artisan::call('ide-helper:models', ['write']); + $this->info('IDE models, done...'); + Artisan::call('optimize'); + $this->info('Optimized...'); + Artisan::call('dump-autoload'); + $this->info('Done!'); + } + + /** + * Get the console command arguments. + * + * @return array + */ + protected function getArguments() + { + return [ + // ['example', InputArgument::REQUIRED, 'An example argument.'], + ]; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + // ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null], + ]; + } + +} diff --git a/app/config/.gitignore b/app/config/.gitignore index 3415788027..8205aab29b 100644 --- a/app/config/.gitignore +++ b/app/config/.gitignore @@ -1,3 +1,4 @@ local/ laptop/ -vagrant/ \ No newline at end of file +vagrant/ +production/ \ No newline at end of file diff --git a/app/config/app.php b/app/config/app.php index b997979dd7..507426a367 100644 --- a/app/config/app.php +++ b/app/config/app.php @@ -39,9 +39,7 @@ return [ 'Illuminate\Workbench\WorkbenchServiceProvider', 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', 'Barryvdh\Debugbar\ServiceProvider', - 'Firefly\Storage\StorageServiceProvider', - 'Firefly\Helper\HelperServiceProvider', - 'Firefly\Validation\ValidationServiceProvider', + 'FireflyIII\FF3ServiceProvider', 'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'Grumpydictator\Gchart\GchartServiceProvider', ], @@ -86,8 +84,7 @@ return [ 'URL' => 'Illuminate\Support\Facades\URL', 'Validator' => 'Illuminate\Support\Facades\Validator', 'View' => 'Illuminate\Support\Facades\View', - 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', - 'Twig' => 'TwigBridge\Facade\Twig', + 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade' ], diff --git a/app/config/firefly.php b/app/config/firefly.php index bd7db200f6..e259b2c0fa 100644 --- a/app/config/firefly.php +++ b/app/config/firefly.php @@ -1,13 +1,13 @@ ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'], 'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], 'piggybank_periods' => [ - 'week' => 'Week', - 'month' => 'Month', - 'year' => 'Year' + 'week' => 'Week', + 'month' => 'Month', + 'quarter' => 'Quarter', + 'year' => 'Year' ], 'periods_to_text' => [ 'weekly' => 'A week', @@ -17,6 +17,11 @@ return [ 'yearly' => 'A year', ], + 'accountRoles' => [ + 'default' => 'Default expense account', + 'sharedExpense' => 'Shared expense account' + ], + 'range_to_text' => [ '1D' => 'day', '1W' => 'week', diff --git a/app/config/homestead/app.php b/app/config/homestead/app.php new file mode 100644 index 0000000000..b6d461000d --- /dev/null +++ b/app/config/homestead/app.php @@ -0,0 +1,15 @@ + true, + 'log_level' => 'debug', +]; \ No newline at end of file diff --git a/app/config/homestead/cache.php b/app/config/homestead/cache.php new file mode 100644 index 0000000000..8fa089055b --- /dev/null +++ b/app/config/homestead/cache.php @@ -0,0 +1,12 @@ + 'file', + 'path' => storage_path() . '/cache', + 'connection' => null, + 'table' => 'cache', + 'memcached' => [ + ['host' => '127.0.0.1', 'port' => 11211, 'weight' => 100], + ], + 'prefix' => 'laravel', +]; diff --git a/app/config/homestead/database.php b/app/config/homestead/database.php new file mode 100644 index 0000000000..de7f714bdf --- /dev/null +++ b/app/config/homestead/database.php @@ -0,0 +1,47 @@ + [ + + 'mysql' => [ + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'homestead', + 'username' => 'homestead', + 'password' => 'secret', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'homestead', + 'username' => 'homestead', + 'password' => 'secret', + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + ], + + ], + +]; \ No newline at end of file diff --git a/app/config/homestead/mail.php b/app/config/homestead/mail.php new file mode 100644 index 0000000000..79a8bf2536 --- /dev/null +++ b/app/config/homestead/mail.php @@ -0,0 +1,13 @@ + 'smtp', + 'host' => 'smtp.gmail.com', + 'port' => 587, + 'from' => ['address' => '@gmail.com', 'name' => 'Firefly III'], + 'encryption' => 'tls', + 'username' => '@gmail.com', + 'password' => '', + 'sendmail' => '/usr/sbin/sendmail -bs', + 'pretend' => false, +]; diff --git a/app/config/packages/barryvdh/laravel-debugbar/config.php b/app/config/packages/barryvdh/laravel-debugbar/config.php index b4ba5d5c62..3baf4b799a 100644 --- a/app/config/packages/barryvdh/laravel-debugbar/config.php +++ b/app/config/packages/barryvdh/laravel-debugbar/config.php @@ -2,14 +2,14 @@ use Illuminate\Support\Facades\Config; -return array( +return [ - 'enabled' => Config::get('app.debug'), + 'enabled' => Config::get('app.debug'), - 'storage' => array( + 'storage' => [ 'enabled' => true, - 'path' => storage_path() . '/debugbar', - ), + 'path' => storage_path() . '/debugbar', + ], /* |-------------------------------------------------------------------------- @@ -37,8 +37,8 @@ return array( | */ - 'capture_ajax' => true, - + 'capture_ajax' => true, + /* |-------------------------------------------------------------------------- | Capture Console Commands @@ -59,7 +59,7 @@ return array( | */ - 'collectors' => array( + 'collectors' => [ 'phpinfo' => true, // Php version 'messages' => true, // Messages 'time' => true, // Time Datalogger @@ -78,7 +78,7 @@ return array( 'files' => false, // Show the included files 'config' => false, // Display config settings 'auth' => false, // Display Laravel authentication status - ), + ], /* |-------------------------------------------------------------------------- @@ -89,27 +89,27 @@ return array( | */ - 'options' => array( - 'auth' => array( + 'options' => [ + 'auth' => [ 'show_name' => false, // Also show the users name/email in the debugbar - ), - 'db' => array( - 'with_params' => true, // Render SQL with the parameters substituted - 'timeline' => false, // Add the queries to the timeline - ), - 'mail' => array( + ], + 'db' => [ + 'with_params' => true, // Render SQL with the parameters substituted + 'timeline' => false, // Add the queries to the timeline + ], + 'mail' => [ 'full_log' => false - ), - 'views' => array( + ], + 'views' => [ 'data' => false, //Note: Can slow down the application, because the data can be quite large.. - ), - 'route' => array( + ], + 'route' => [ 'label' => true // show complete route on bar - ), - 'logs' => array( + ], + 'logs' => [ 'file' => null - ), - ), + ], + ], /* |-------------------------------------------------------------------------- @@ -122,6 +122,6 @@ return array( | */ - 'inject' => true, + 'inject' => true, -); +]; diff --git a/app/config/packages/barryvdh/laravel-ide-helper/config.php b/app/config/packages/barryvdh/laravel-ide-helper/config.php index 89b3dada2f..a49a469e62 100644 --- a/app/config/packages/barryvdh/laravel-ide-helper/config.php +++ b/app/config/packages/barryvdh/laravel-ide-helper/config.php @@ -1,6 +1,6 @@ '_ide_helper.php', + 'filename' => '_ide_helper.php', /* |-------------------------------------------------------------------------- @@ -25,9 +25,9 @@ return array( 'include_helpers' => false, - 'helper_files' => array( - base_path().'/vendor/laravel/framework/src/Illuminate/Support/helpers.php', - ), + 'helper_files' => [ + base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php', + ], /* |-------------------------------------------------------------------------- @@ -39,9 +39,9 @@ return array( | */ - 'model_locations' => array( + 'model_locations' => [ 'app/models', - ), + ], /* @@ -53,14 +53,14 @@ return array( | */ - 'extra' => array( - 'Artisan' => array('Illuminate\Foundation\Artisan'), - 'Eloquent' => array('Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'), - 'Session' => array('Illuminate\Session\Store'), - ), + 'extra' => [ + 'Artisan' => ['Illuminate\Foundation\Artisan'], + 'Eloquent' => ['Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'], + 'Session' => ['Illuminate\Session\Store'], + ], - 'magic' => array( - 'Log' => array( + 'magic' => [ + 'Log' => [ 'debug' => 'Monolog\Logger::addDebug', 'info' => 'Monolog\Logger::addInfo', 'notice' => 'Monolog\Logger::addNotice', @@ -69,7 +69,8 @@ return array( 'critical' => 'Monolog\Logger::addCritical', 'alert' => 'Monolog\Logger::addAlert', 'emergency' => 'Monolog\Logger::addEmergency', - ) - ) + ] + ] -); + +]; diff --git a/app/config/packages/davejamesmiller/laravel-breadcrumbs/config.php b/app/config/packages/davejamesmiller/laravel-breadcrumbs/config.php index a46482b491..e58b40faa9 100644 --- a/app/config/packages/davejamesmiller/laravel-breadcrumbs/config.php +++ b/app/config/packages/davejamesmiller/laravel-breadcrumbs/config.php @@ -1,5 +1,5 @@ 'laravel-breadcrumbs::bootstrap3', -); +]; diff --git a/app/config/testing/app.php b/app/config/testing/app.php new file mode 100644 index 0000000000..e729eb5ab7 --- /dev/null +++ b/app/config/testing/app.php @@ -0,0 +1,2 @@ + 'debug',]; \ No newline at end of file diff --git a/app/config/testing/cache.php b/app/config/testing/cache.php index 66a8a39a86..729ae3a825 100644 --- a/app/config/testing/cache.php +++ b/app/config/testing/cache.php @@ -1,6 +1,6 @@ 'array', -); +]; diff --git a/app/config/testing/database.php b/app/config/testing/database.php index 8448619ce0..dce7200371 100644 --- a/app/config/testing/database.php +++ b/app/config/testing/database.php @@ -4,8 +4,9 @@ return [ 'connections' => [ 'sqlite' => [ 'driver' => 'sqlite', - 'database' => ':memory:', + 'database' => 'tests/_data/testing.sqlite', 'prefix' => '' - ], + ] + ] ]; \ No newline at end of file diff --git a/app/config/testing/mail.php b/app/config/testing/mail.php new file mode 100644 index 0000000000..d2df78f4cd --- /dev/null +++ b/app/config/testing/mail.php @@ -0,0 +1,13 @@ + 'smtp', + 'host' => '', + 'port' => 587, + 'from' => ['address' => '', 'name' => 'Firefly III'], + 'encryption' => 'tls', + 'username' => '', + 'password' => '', + 'sendmail' => '/usr/sbin/sendmail -bs', + 'pretend' => true, +]; diff --git a/app/config/testing/session.php b/app/config/testing/session.php index 0364b63dcc..46bf726a27 100644 --- a/app/config/testing/session.php +++ b/app/config/testing/session.php @@ -1,6 +1,6 @@ 'array', -); +]; diff --git a/app/config/view.php b/app/config/view.php index e0529fb669..f3b156d455 100644 --- a/app/config/view.php +++ b/app/config/view.php @@ -1,6 +1,6 @@ array(__DIR__ . '/../views'), + 'paths' => [__DIR__ . '/../views'], 'pagination' => 'pagination::slider-3', ]; diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index da88631243..73d8ec9338 100644 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -1,6 +1,6 @@ with('what', $what) - ->with(compact('subTitleIcon')) - ->with(compact('subTitle')); - } - - - /** - * @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 */ public function create($what) @@ -130,11 +36,9 @@ class AccountController extends BaseController $subTitleIcon = 'fa-download'; break; } + $subTitle = 'Create a new ' . $what . ' account'; - return View::make('accounts.create') - ->with('subTitle', 'Create a new ' . $what . ' account') - ->with('what', $what) - ->with(compact('subTitleIcon')); + return View::make('accounts.create', compact('subTitleIcon', 'what', 'subTitle')); } /** @@ -144,10 +48,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 . '"' - ); + $subTitle = 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'; + + return View::make('accounts.delete', compact('account', 'subTitle')); } /** @@ -159,69 +62,27 @@ class AccountController extends BaseController { $type = $account->accountType->type; + $name = $account->name; /** @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.'); + $return = 'asset'; switch ($type) { - case 'Asset account': - case 'Default account': - return Redirect::route('accounts.index', 'asset'); - break; case 'Expense account': case 'Beneficiary account': - return Redirect::route('accounts.index', 'expense'); + $return = 'expense'; break; case 'Revenue account': - return Redirect::route('accounts.index', 'revenue'); + $return = 'revenue'; break; } + Session::flash('success', 'The ' . $return . ' account "' . e($name) . '" was deleted.'); + return Redirect::route('accounts.index', $return); } /** @@ -231,11 +92,13 @@ class AccountController extends BaseController */ public function edit(Account $account) { + $prefilled = []; switch ($account->accountType->type) { case 'Asset account': case 'Default account': - $subTitleIcon = 'fa-money'; + $subTitleIcon = 'fa-money'; + $prefilled['account_role'] = $account->getMeta('accountRole'); break; case 'Expense account': case 'Beneficiary account': @@ -254,46 +117,112 @@ class AccountController extends BaseController 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); } + Session::flash('prefilled', $prefilled); - return View::make('accounts.edit') - ->with('account', $account) - ->with('openingBalance', $openingBalance) - ->with(compact('subTitleIcon')) - ->with('subTitle', 'Edit ' . strtolower( + return View::make('accounts.edit', compact('account', 'openingBalance', 'subTitleIcon'))->with( + 'subTitle', 'Edit ' . strtolower( $account->accountType->type ) . ' "' . $account->name . '"' ); } + /** + * @param string $what + * + * @return View + * @throws FireflyException + */ + public function index($what = 'default') + { + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + + switch ($what) { + default: + throw new FireflyException('Cannot handle account type "' . e($what) . '".'); + break; + case 'asset': + $subTitleIcon = 'fa-money'; + $subTitle = 'Asset accounts'; + $accounts = $acct->getAssetAccounts(); + break; + case 'expense': + $subTitleIcon = 'fa-shopping-cart'; + $subTitle = 'Expense accounts'; + $accounts = $acct->getExpenseAccounts(); + break; + case 'revenue': + $subTitleIcon = 'fa-download'; + $subTitle = 'Revenue accounts'; + $accounts = $acct->getRevenueAccounts(); + break; + } + + $accounts->each( + function (Account $account) { + if (Cache::has('account.' . $account->id . '.lastActivityDate')) { + $account->lastActionDate = Cache::get('account.' . $account->id . '.lastActivityDate'); + } else { + $transaction = $account->transactions()->orderBy('updated_at', 'DESC')->first(); + if (is_null($transaction)) { + $account->lastActionDate = null; + Cache::forever('account.' . $account->id . '.lastActivityDate', 0); + } else { + $account->lastActionDate = $transaction->updated_at; + Cache::forever('account.' . $account->id . '.lastActivityDate', $transaction->updated_at); + } + } + + } + ); + + return View::make('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts')); + } + /** * @param Account $account + * @param string $view * * @return $this */ - public function show(Account $account) + public function show(Account $account, $view = 'session') { switch ($account->accountType->type) { case 'Asset account': case 'Default account': $subTitleIcon = 'fa-money'; + $what = 'asset'; break; case 'Expense account': case 'Beneficiary account': $subTitleIcon = 'fa-shopping-cart'; + $what = 'expense'; break; case 'Revenue account': $subTitleIcon = 'fa-download'; + $what = 'revenue'; + break; + } + + // get a paginated view of all transactions for this account: + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + switch ($view) { + default: + case 'session': + $journals = $acct->getTransactionJournals($account, 50); + break; + case 'all': + $journals = $acct->getAllTransactionJournals($account, 50); + break; } - //$data = $this->_accounts->show($account, 40); - return View::make('accounts.show') - ->with('account', $account) - ->with('subTitle', 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"') - ->with(compact('subTitleIcon')); + return View::make('accounts.show', compact('account', 'what', 'view', 'subTitleIcon', 'journals'))->with('account', $account)->with( + 'subTitle', 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' + ); } /** @@ -312,7 +241,7 @@ class AccountController extends BaseController default: throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"'); break; - case 'create_another': + case 'return_to_edit': case 'store': $messages = $acct->validate($data); /** @var MessageBag $messages ['errors'] */ @@ -320,6 +249,7 @@ class AccountController extends BaseController 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! @@ -337,6 +267,7 @@ class AccountController extends BaseController Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); + return Redirect::route('accounts.create', $data['what'])->withInput(); break; } @@ -360,8 +291,10 @@ class AccountController extends BaseController throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"'); break; case 'Default account': + case 'Asset account': $data['what'] = 'asset'; break; + case 'Expense account': case 'Beneficiary account': $data['what'] = 'expense'; break; @@ -382,6 +315,7 @@ class AccountController extends BaseController 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! @@ -391,13 +325,14 @@ class AccountController extends BaseController if ($data['post_submit_action'] == 'create_another') { return Redirect::route('accounts.edit', $account->id); } else { - return Redirect::route('accounts.index',$data['what']); + 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; } diff --git a/app/controllers/BudgetController.php b/app/controllers/BudgetController.php index 54e095461d..c2a857fb6d 100644 --- a/app/controllers/BudgetController.php +++ b/app/controllers/BudgetController.php @@ -1,6 +1,6 @@ set('budgetIncomeTotal' . $date->format('FY'), $value); - return Redirect::route('budgets.index'); - } - - /** - * Update the amount for a budget's limitrepetition and/or create it. - * * @param Budget $budget + * + * @return \Illuminate\Http\JsonResponse + * @throws Exception */ public function amount(Budget $budget) { @@ -51,12 +41,18 @@ class BudgetController extends BaseController $limit->repeat_freq = 'monthly'; $limit->repeats = 0; $limit->save(); + /* + * A newly stored limit also created a limit repetition. + */ Event::fire('limits.store', [$limit]); } else { if ($amount > 0) { $limit->amount = $amount; $limit->save(); + /* + * An updated limit also updates the associated limit repetitions. + */ Event::fire('limits.update', [$limit]); } else { $limit->delete(); @@ -72,19 +68,69 @@ class BudgetController extends BaseController } + /** + * @return $this + */ + public function create() + { + return View::make('budgets.create')->with('subTitle', 'Create a new budget'); + } + + /** + * @param Budget $budget + * + * @return $this + */ + public function delete(Budget $budget) + { + 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 + $repos->destroy($budget); + Session::flash('success', 'The budget was deleted.'); + + return Redirect::route('budgets.index'); + + } + + /** + * @param Budget $budget + * + * @return $this + */ + public function edit(Budget $budget) + { + Session::flash('prefilled', ['name' => $budget->name]); + + return View::make('budgets.edit')->with('budget', $budget)->with('subTitle', 'Edit budget "' . $budget->name . '"'); + + } + + /** + * @return $this + */ public function index() { - /** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */ - $preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); - $date = Session::get('start'); + /** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface'); /** @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'); + $date = \Session::get('start'); $spent = 0; /** @var \Budget $budget */ foreach ($budgets as $budget) { @@ -114,9 +160,9 @@ class BudgetController extends BaseController } $budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000); - $amount = floatval($budgetAmount->data); - $overspent = $spent > $amount; - if($overspent) { + $amount = floatval($budgetAmount->data); + $overspent = $spent > $amount; + if ($overspent) { // overspent on total amount $spentPCT = ceil($amount / $spent * 100); } else { @@ -124,19 +170,22 @@ class BudgetController extends BaseController $spentPCT = ceil($spent / $amount * 100); } - return View::make('budgets.index', compact('budgets','spent','spentPCT','overspent'))->with('budgetAmount', $budgetAmount); + return View::make('budgets.index', compact('budgets', 'spent', 'spentPCT', 'overspent'))->with('budgetAmount', $budgetAmount); } /** - * @return $this + * @return \Illuminate\Http\RedirectResponse */ - public function updateIncome() + public function postUpdateIncome() { - $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); + /** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface'); + $date = Session::get('start'); + + $value = intval(Input::get('amount')); + $preferences->set('budgetIncomeTotal' . $date->format('FY'), $value); + + return Redirect::route('budgets.index'); } /** @@ -150,62 +199,32 @@ class BudgetController extends BaseController if (!is_null($repetition) && $repetition->limit->budget->id != $budget->id) { App::abort(500); } + /** @var \FireflyIII\Database\Budget $repos */ + $repos = App::make('FireflyIII\Database\Budget'); if (is_null($repetition)) { // get all other repetitions: $limits = $budget->limits()->orderBy('startdate', 'DESC')->get(); + // get all transaction journals for this budget. + $journals = $repos->getTransactionJournals($budget, 50); + $subTitle = $budget->name; } else { // get nothing? i dunno $limits = [$repetition->limit]; + // get all transaction journals for this budget and limit repetition. + $subTitle = $budget->name . ' in ' . $repetition->startdate->format('F Y'); + $journals = $repos->getTransactionJournalsInRepetition($budget, $repetition, 50); } + $hideBudget = true; - return View::make('budgets.show', compact('limits', 'budget', 'repetition')); + + return View::make('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget')); } /** * @return $this - */ - public function create() - { - return View::make('budgets.create')->with('subTitle', 'Create a new budget'); - } - - /** - * @param Budget $budget - * - * @return $this - */ - public function delete(Budget $budget) - { - return View::make('budgets.delete')->with('budget', $budget)->with('subTitle', 'Delete budget "' . $budget->name . '"'); - } - - public function destroy(Budget $budget) - { - /** @var \FireflyIII\Database\Budget $repos */ - $repos = App::make('FireflyIII\Database\Budget'); - // remove budget - $repos->destroy($budget); - Session::flash('success', 'The budget was deleted.'); - return Redirect::route('budgets.index'); - - } - - /** - * @param Budget $budget - * - * @return $this - */ - public function edit(Budget $budget) - { - Session::flash('prefilled', ['name' => $budget->name]); - return View::make('budgets.edit')->with('budget', $budget)->with('subTitle', 'Edit budget "' . $budget->name . '"'); - - } - - /** - * @return \Illuminate\Http\RedirectResponse + * @throws FireflyException */ public function store() { @@ -225,6 +244,7 @@ class BudgetController extends BaseController 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! @@ -242,6 +262,7 @@ class BudgetController extends BaseController Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); + return Redirect::route('budgets.create')->withInput(); break; } @@ -250,6 +271,7 @@ class BudgetController extends BaseController /** * @param Budget $budget * + * @return $this * @throws FireflyException */ public function update(Budget $budget) @@ -263,7 +285,7 @@ class BudgetController extends BaseController default: throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"'); break; - case 'create_another': + case 'return_to_edit': case 'update': $messages = $repos->validate($data); /** @var MessageBag $messages ['errors'] */ @@ -271,13 +293,14 @@ class BudgetController extends BaseController 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') { + if ($data['post_submit_action'] == 'return_to_edit') { return Redirect::route('budgets.edit', $budget->id); } else { return Redirect::route('budgets.index'); @@ -287,8 +310,22 @@ class BudgetController extends BaseController Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); + return Redirect::route('budgets.edit', $budget->id)->withInput(); break; } } + + /** + * @return $this + */ + public function updateIncome() + { + $date = Session::get('start'); + /** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface'); + $budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000); + + return View::make('budgets.income')->with('amount', $budgetAmount)->with('date', $date); + } } \ No newline at end of file diff --git a/app/controllers/CategoryController.php b/app/controllers/CategoryController.php index 6b1d7a4e44..2093aa54e0 100644 --- a/app/controllers/CategoryController.php +++ b/app/controllers/CategoryController.php @@ -1,26 +1,17 @@ _repository = $repository; - $this->_category = $category; View::share('title', 'Categories'); View::share('mainTitleIcon', 'fa-bar-chart'); } @@ -40,8 +31,7 @@ class CategoryController extends BaseController */ public function delete(Category $category) { - return View::make('categories.delete')->with('category', $category) - ->with('subTitle', 'Delete category "' . $category->name . '"'); + return View::make('categories.delete')->with('category', $category)->with('subTitle', 'Delete category "' . $category->name . '"'); } /** @@ -51,8 +41,12 @@ class CategoryController extends BaseController */ public function destroy(Category $category) { - $this->_repository->destroy($category); + /** @var \FireflyIII\Database\Category $repos */ + $repos = App::make('FireflyIII\Database\Category'); + + $repos->destroy($category); Session::flash('success', 'The category was deleted.'); + return Redirect::route('categories.index'); } @@ -63,8 +57,7 @@ class CategoryController extends BaseController */ public function edit(Category $category) { - return View::make('categories.edit')->with('category', $category) - ->with('subTitle', 'Edit category "' . $category->name . '"'); + return View::make('categories.edit')->with('category', $category)->with('subTitle', 'Edit category "' . $category->name . '"'); } /** @@ -72,10 +65,11 @@ class CategoryController extends BaseController */ public function index() { - $categories = $this->_repository->get(); + /** @var \FireflyIII\Database\Category $repos */ + $repos = App::make('FireflyIII\Database\Category'); + $categories = $repos->get(); - return View::make('categories.index')->with('categories', $categories) - ->with('subTitle', 'All your categories'); + return View::make('categories.index', compact('categories')); } /** @@ -85,54 +79,106 @@ class CategoryController extends BaseController */ public function show(Category $category) { - $start = \Session::get('start'); - $end = \Session::get('end'); + $hideCategory = true; + /** @var \FireflyIII\Database\Category $repos */ + $repos = App::make('FireflyIII\Database\Category'); - $journals = $this->_category->journalsInRange($category, $start, $end); + $journals = $repos->getTransactionJournals($category, 50); - return View::make('categories.show')->with('category', $category)->with('journals', $journals)->with( - 'highlight', Input::get('highlight') - )->with('subTitle', 'Overview for category "' . $category->name . '"'); + return View::make('categories.show', compact('category', 'journals', 'hideCategory')); } /** - * @return $this|\Illuminate\Http\RedirectResponse + * @return $this + * @throws FireflyException */ public function store() { - $category = $this->_repository->store(Input::all()); - if ($category->validate()) { - Session::flash('success', 'Category "' . $category->name . '" created!'); + $data = Input::all(); + /** @var \FireflyIII\Database\Category $repos */ + $repos = App::make('FireflyIII\Database\Category'); - if (Input::get('create') == '1') { - return Redirect::route('categories.create'); - } + 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 category: ' . $messages['errors']->first()); - return Redirect::route('categories.index'); - } else { - Session::flash('error', 'Could not save the new category!'); + return Redirect::route('categories.create')->withInput()->withErrors($messages['errors']); + } + // store! + $repos->store($data); + Session::flash('success', 'New category stored!'); - return Redirect::route('categories.create')->withInput(); + if ($data['post_submit_action'] == 'create_another') { + return Redirect::route('categories.create')->withInput(); + } else { + return Redirect::route('categories.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('categories.create')->withInput(); + break; } } /** * @param Category $category * - * @return $this|\Illuminate\Http\RedirectResponse + * @return $this + * @throws FireflyException */ public function update(Category $category) { - $category = $this->_repository->update($category, Input::all()); - if ($category->validate()) { - Session::flash('success', 'Category "' . $category->name . '" updated.'); + /** @var \FireflyIII\Database\Category $repos */ + $repos = App::make('FireflyIII\Database\Category'); + $data = Input::except('_token'); - return Redirect::route('categories.index'); - } else { - Session::flash('success', 'Could not update category "' . $category->name . '".'); + switch (Input::get('post_submit_action')) { + default: + throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"'); + break; + case 'return_to_edit': + 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 category: ' . $messages['errors']->first()); - return Redirect::route('categories.edit')->withErrors($category->errors())->withInput(); + return Redirect::route('categories.edit', $category->id)->withInput()->withErrors($messages['errors']); + } + // store! + $repos->update($category, $data); + Session::flash('success', 'Category updated!'); + + if ($data['post_submit_action'] == 'return_to_edit') { + return Redirect::route('categories.edit', $category->id); + } else { + return Redirect::route('categories.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('categories.edit', $category->id)->withInput(); + break; } diff --git a/app/controllers/ChartController.php b/app/controllers/ChartController.php deleted file mode 100644 index 9b104ca379..0000000000 --- a/app/controllers/ChartController.php +++ /dev/null @@ -1,603 +0,0 @@ -_chart = $chart; - $this->_accounts = $accounts; - } - - /** - * This method takes a budget, all limits and all their repetitions and displays three numbers per repetition: - * the amount of money in the repetition (represented as "an envelope"), the amount spent and the spent percentage. - * - * @param Budget $budget - * - * @return \Illuminate\Http\JsonResponse - */ - public function budgetDefault(\Budget $budget) - { - $expense = []; - $left = []; - $envelope = []; - // get all limit repetitions for this budget. - /** @var \Limit $limit */ - foreach ($budget->limits as $limit) { - /** @var \LimitRepetition $rep */ - foreach ($limit->limitrepetitions as $rep) { - // get the amount of money spent in this period on this budget. - $spentInRep = $rep->amount - $rep->leftInRepetition(); - $pct = round((floatval($spentInRep) / floatval($limit->amount)) * 100, 2); - $name = $rep->periodShow(); - $envelope[] = [$name, floatval($limit->amount)]; - $expense[] = [$name, floatval($spentInRep)]; - $left[] = [$name, $pct]; - } - } - - $return = [ - 'chart_title' => 'Overview for budget ' . $budget->name, - 'subtitle' => 'All envelopes', - 'series' => [ - [ - 'type' => 'line', - 'yAxis' => 1, - 'name' => 'Amount in envelope', - 'data' => $envelope - ], - [ - 'type' => 'column', - 'name' => 'Expenses in envelope', - 'data' => $expense - ], - [ - 'type' => 'line', - 'yAxis' => 1, - 'name' => 'Spent percentage for envelope', - 'data' => $left - ] - - - ] - ]; - - return Response::json($return); - } - - /** - * This method takes a single limit repetition (so a single "envelope") and displays the amount of money spent - * per day and subsequently how much money is left. - * - * @param LimitRepetition $rep - * - * @return \Illuminate\Http\JsonResponse - */ - public function budgetLimit(\LimitRepetition $rep) - { - $budget = $rep->limit->budget; - $current = clone $rep->startdate; - $expense = []; - $leftInLimit = []; - $currentLeftInLimit = floatval($rep->limit->amount); - while ($current <= $rep->enddate) { - $spent = $this->_chart->spentOnDay($budget, $current); - $spent = floatval($spent) == 0 ? null : floatval($spent); - $entry = [$current->timestamp * 1000, $spent]; - $expense[] = $entry; - $currentLeftInLimit = $currentLeftInLimit - $spent; - $leftInLimit[] = [$current->timestamp * 1000, $currentLeftInLimit]; - $current->addDay(); - } - - $return = [ - 'chart_title' => 'Overview for budget ' . $budget->name, - 'subtitle' => - 'Between ' . $rep->startdate->format('M jS, Y') . ' and ' . $rep->enddate->format('M jS, Y'), - 'series' => [ - [ - 'type' => 'column', - 'name' => 'Expenses per day', - 'yAxis' => 1, - 'data' => $expense - ], - [ - 'type' => 'line', - 'name' => 'Left in envelope', - 'data' => $leftInLimit - ] - - ] - ]; - - return Response::json($return); - } - - /** - * This method takes a budget and gets all transactions in it which haven't got an envelope (limit). - * - * Usually this means that very old and unorganized or very NEW transactions get displayed; there was never an - * envelope or it hasn't been created (yet). - * - * - * @param Budget $budget - * - * @return \Illuminate\Http\JsonResponse - */ - public function budgetNoLimits(\Budget $budget) - { - /* - * Firefly can go about this two ways. Either it finds all transactions which definitely are IN an envelope - * and exclude them or it searches for transactions outside of the range of any of the envelopes there are. - * - * Since either is kinda shitty Firefly uses the first one because it's easier to build. - */ - $inRepetitions = $this->_chart->allJournalsInBudgetEnvelope($budget); - - /* - * With this set of id's, Firefly can search for all journals NOT in that set. - * BUT they have to be in the budget (duh). - */ - $set = $this->_chart->journalsNotInSet($budget, $inRepetitions); - /* - * Next step: get all transactions for those journals. - */ - $transactions = $this->_chart->transactionsByJournals($set); - - - /* - * this set builds the chart: - */ - $expense = []; - - foreach ($transactions as $t) { - $date = new Carbon($t->date); - $expense[] = [$date->timestamp * 1000, floatval($t->aggregate)]; - } - $return = [ - 'chart_title' => 'Overview for ' . $budget->name, - 'subtitle' => 'Not organized by an envelope', - 'series' => [ - [ - 'type' => 'column', - 'name' => 'Expenses per day', - 'data' => $expense - ] - - ] - ]; - return Response::json($return); - } - - /** - * This method gets all transactions within a budget within the period set by the current session - * start and end date. It also includes any envelopes which might exist within this period. - * - * @param Budget $budget - * - * @return \Illuminate\Http\JsonResponse - */ - public function budgetSession(\Budget $budget) - { - $series = []; - $end = clone Session::get('end'); - $start = clone Session::get('start'); - - - /* - * Expenses per day in the session's period. That's easy. - */ - $expense = []; - $current = clone Session::get('start'); - while ($current <= $end) { - $spent = $this->_chart->spentOnDay($budget, $current); - $spent = floatval($spent) == 0 ? null : floatval($spent); - $expense[] = [$current->timestamp * 1000, $spent]; - $current->addDay(); - } - - $series[] = [ - 'type' => 'column', - 'name' => 'Expenses per day', - 'data' => $expense - ]; - unset($expense, $spent, $current); - - /* - * Find all limit repetitions (for this budget) between start and end. This is - * quite a complex query. - */ - $reps = $this->_chart->limitsInRange($budget, $start, $end); - - /* - * For each limitrepetition Firefly creates a serie that contains the amount left in - * the limitrepetition for its entire date-range. Entries are only actually included when they - * fall into the charts date range. - * - * So example: the user has a session date from Jan 15 to Jan 30. The limitrepetition - * starts at 1 Jan until 1 Feb. - * - * Firefly loops from 1 Jan to 1 Feb but only includes Jan 15 / Jan 30. - * But it does keep count of the amount outside of these dates because otherwise the line might be wrong. - */ - /** @var \LimitRepetition $repetition */ - foreach ($reps as $repetition) { - $limitAmount = $repetition->limit->amount; - - // create a serie for the repetition. - $currentSerie = [ - 'type' => 'spline', - 'id' => 'rep-' . $repetition->id, - 'yAxis' => 1, - 'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(), - 'data' => [] - ]; - $current = clone $repetition->startdate; - while ($current <= $repetition->enddate) { - if ($current >= $start && $current <= $end) { - // spent on limit: - $spentSoFar = $this->_chart->spentOnLimitRepetitionBetweenDates( - $repetition, $repetition->startdate, $current - ); - $leftInLimit = floatval($limitAmount) - floatval($spentSoFar); - - $currentSerie['data'][] = [$current->timestamp * 1000, $leftInLimit]; - } - $current->addDay(); - } - - // do something here. - $series[] = $currentSerie; - } - - $return = [ - 'chart_title' => 'Overview for budget ' . $budget->name, - 'subtitle' => - 'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format( - 'M jS, Y' - ), - 'series' => $series - ]; - - return Response::json($return); - - } - - /** - * @param Category $category - * - * @return \Illuminate\Http\JsonResponse - */ - public function categoryShowChart(Category $category) - { - $start = Session::get('start'); - $end = Session::get('end'); - $range = Session::get('range'); - - $serie = $this->_chart->categoryShowChart($category, $range, $start, $end); - $data = [ - 'chart_title' => $category->name, - 'subtitle' => 'View more', - 'series' => $serie - ]; - - return Response::json($data); - - - } - - /** - * @param Account $account - * - * @return mixed - */ - public function homeAccount(Account $account = null) - { - // get preferences and accounts (if necessary): - $start = Session::get('start'); - $end = Session::get('end'); - - if (is_null($account)) { - // get, depending on preferences: - /** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $prefs */ - $prefs = \App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); - $pref = $prefs->get('frontpageAccounts', []); - - /** @var \Firefly\Storage\Account\AccountRepositoryInterface $acct */ - $acct = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); - $accounts = $acct->getByIds($pref->data); - } else { - $accounts = [$account]; - } - // loop and get array data. - - $url = count($accounts) == 1 && is_array($accounts) - ? 'View more' - : - 'View more'; - $data = [ - 'chart_title' => count($accounts) == 1 ? $accounts[0]->name : 'All accounts', - 'subtitle' => $url, - 'series' => [] - ]; - - foreach ($accounts as $account) { - $data['series'][] = $this->_chart->account($account, $start, $end); - } - - return Response::json($data); - } - - /** - * @param $name - * @param $day - * @param $month - * @param $year - * - * @return $this - */ - public function homeAccountInfo($name, $day, $month, $year) - { - $account = $this->_accounts->findByName($name); - - $date = Carbon::createFromDate($year, $month, $day); - $result = $this->_chart->accountDailySummary($account, $date); - - return View::make('charts.info')->with('rows', $result['rows'])->with('sum', $result['sum'])->with( - 'account', $account - ); - } - - /** - * @return \Illuminate\Http\JsonResponse - */ - public function homeBudgets() - { - $start = Session::get('start'); - $end = Session::get('end'); - $data = [ - 'labels' => [], - 'series' => [ - [ - 'name' => 'Limit', - 'data' => [] - ], - [ - 'name' => 'Spent', - 'data' => [] - ], - ] - ]; - - // Get all budgets. - $budgets = \Auth::user()->budgets()->orderBy('name', 'ASC')->get(); - $budgetIds = []; - /** @var \Budget $budget */ - foreach ($budgets as $budget) { - $budgetIds[] = $budget->id; - - // Does the budget have a limit starting on $start? - $rep = \LimitRepetition:: - leftJoin('limits', 'limit_repetitions.limit_id', '=', 'limits.id')->leftJoin( - 'components', 'limits.component_id', '=', 'components.id' - )->where('limit_repetitions.startdate', $start->format('Y-m-d'))->where( - 'components.id', $budget->id - )->first(['limit_repetitions.*']); - - if (is_null($rep)) { - $limit = 0.0; - $id = null; - $parameter = 'useSession=true'; - } else { - $limit = floatval($rep->amount); - $id = $rep->id; - $parameter = ''; - } - - // Date range to check for expenses made? - if (is_null($rep)) { - // use the session start and end for our search query - $expenseStart = Session::get('start'); - $expenseEnd = Session::get('end'); - - } else { - // use the limit's start and end for our search query - $expenseStart = $rep->startdate; - $expenseEnd = $rep->enddate; - } - // How much have we spent on this budget? - $expenses = floatval($budget->transactionjournals()->before($expenseEnd)->after($expenseStart)->lessThan(0)->sum('amount')) * -1; - - // Append to chart: - if ($limit > 0 || $expenses > 0) { - $data['labels'][] = $budget->name; - $data['series'][0]['data'][] = [ - 'y' => $limit, - 'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter - ]; - $data['series'][1]['data'][] = [ - 'y' => $expenses, - 'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter - ]; - } - } - // 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)->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') - ]; - $data['series'][1]['data'][] = [ - 'y' => floatval($set) * -1, - 'url' => route('budgets.nobudget', 'session') - ]; - - return Response::json($data); - - } - - /** - * @return \Illuminate\Http\JsonResponse - */ - public function homeCategories() - { - $start = Session::get('start'); - $end = Session::get('end'); - - return Response::json($this->_chart->categories($start, $end)); - - - } - - /** - * 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]); - - } -} \ No newline at end of file diff --git a/app/controllers/GoogleChartController.php b/app/controllers/GoogleChartController.php index daeedcfddd..0eb2b58396 100644 --- a/app/controllers/GoogleChartController.php +++ b/app/controllers/GoogleChartController.php @@ -7,6 +7,209 @@ use Carbon\Carbon; class GoogleChartController extends BaseController { + /** + * @param Account $account + * @param string $view + * + * @return \Illuminate\Http\JsonResponse + */ + public function accountBalanceChart(Account $account, $view = 'session') + { + /** @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. + */ + switch ($view) { + default: + case 'session': + $start = Session::get('start'); + $end = Session::get('end'); + break; + case 'all': + $first = $account->transactionjournals()->orderBy('date', 'DESC')->first(); + $last = $account->transactionjournals()->orderBy('date', 'ASC')->first(); + if (is_null($first)) { + $start = Session::get('start'); + } else { + $start = clone $first->date; + } + if (is_null($last)) { + $end = Session::get('end'); + } else { + $end = clone $last->date; + } + break; + } + + $current = clone $start; + + while ($end >= $current) { + $row = [clone $current]; + if ($current > Carbon::now()) { + $row[] = null; + } else { + $row[] = Steam::balance($account, $current); + } + + $chart->addRowArray($row); + $current->addDay(); + } + + + $chart->generate(); + + return Response::json($chart->getData()); + } + + /** + * @param Account $account + * @param string $view + * + * @return \Illuminate\Http\JsonResponse + */ + public function accountSankeyInChart(Account $account, $view = 'session') + { + // 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'); + + switch ($view) { + default: + case 'session': + $start = Session::get('start'); + $end = Session::get('end'); + break; + case 'all': + $first = $account->transactionjournals()->orderBy('date', 'DESC')->first(); + $last = $account->transactionjournals()->orderBy('date', 'ASC')->first(); + if (is_null($first)) { + $start = Session::get('start'); + } else { + $start = clone $first->date; + } + if (is_null($last)) { + $end = Session::get('end'); + } else { + $end = clone $last->date; + } + break; + } + + + $transactions = $account->transactions()->with( + ['transactionjournal', 'transactionjournal.transactions' => function ($q) { + $q->where('amount', '<', 0); + }, 'transactionjournal.budgets', 'transactionjournal.transactiontype', 'transactionjournal.categories'] + )->before($end)->after($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()); + + } + + /** + * @param Account $account + * @param string $view + * + * @return \Illuminate\Http\JsonResponse + */ + public function accountSankeyOutChart(Account $account, $view = 'session') + { + // 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()); + + } + /** * This method renders the b */ @@ -18,11 +221,15 @@ class GoogleChartController extends BaseController /** @var \FireflyIII\Shared\Preferences\Preferences $preferences */ $preferences = App::make('FireflyIII\Shared\Preferences\Preferences'); - $pref = $preferences->get('frontpageAccounts'); + $pref = $preferences->get('frontpageAccounts', []); /** @var \FireflyIII\Database\Account $acct */ - $acct = App::make('FireflyIII\Database\Account'); - $accounts = $acct->getByIds($pref->data); + $acct = App::make('FireflyIII\Database\Account'); + if (count($pref->data) > 0) { + $accounts = $acct->getByIds($pref->data); + } else { + $accounts = $acct->getAssetAccounts(); + } /* @@ -43,12 +250,7 @@ class GoogleChartController extends BaseController $row = [clone $current]; foreach ($accounts as $account) { - //if ($current > Carbon::now()) { - // $row[] = 0; - //} else { - $row[] = $account->balance($current); - //} - + $row[] = Steam::balance($account, $current); } $chart->addRowArray($row); @@ -56,191 +258,11 @@ class GoogleChartController extends BaseController } $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 */ @@ -306,6 +328,7 @@ class GoogleChartController extends BaseController $chart->generate(); + return Response::json($chart->getData()); } @@ -350,13 +373,224 @@ class GoogleChartController extends BaseController $chart->generate(); + + return Response::json($chart->getData()); + + } + + /** + * @param Budget $budget + * @param LimitRepetition $repetition + * + * @return \Illuminate\Http\JsonResponse + */ + public function budgetLimitSpending(\Budget $budget, \LimitRepetition $repetition) + { + $start = clone $repetition->startdate; + $end = $repetition->enddate; + + /** @var \Grumpydictator\Gchart\GChart $chart */ + $chart = App::make('gchart'); + $chart->addColumn('Day', 'date'); + $chart->addColumn('Left', 'number'); + + + $amount = $repetition->amount; + + while ($start <= $end) { + /* + * Sum of expenses on this day: + */ + $sum = floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($start)->sum('amount')); + $amount += $sum; + $chart->addRow(clone $start, $amount); + $start->addDay(); + } + $chart->generate(); + + return Response::json($chart->getData()); + + } + + /** + * @param $year + * + * @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()); + } + + /** + * @param Component $component + * @param $year + * + * @return \Illuminate\Http\JsonResponse + */ + public function componentsAndSpending(Component $component, $year) + { + try { + $start = new Carbon('01-01-' . $year); + } catch (Exception $e) { + App::abort(500); + } + + if ($component->class == 'Budget') { + /** @var \FireflyIII\Database\Budget $repos */ + $repos = App::make('FireflyIII\Database\Budget'); + } else { + /** @var \FireflyIII\Database\Category $repos */ + $repos = App::make('FireflyIII\Database\Category'); + } + + /** @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 = $repos->spentInMonth($component, $start); + $repetition = $repos->repetitionOnStartingOnDate($component, $start); + if ($repetition) { + $budgeted = floatval($repetition->amount); + } else { + $budgeted = null; + } + + $chart->addRow(clone $start, $budgeted, $spent); + + $start->addMonth(); + } + + + $chart->generate(); + + return Response::json($chart->getData()); + + + } + + /** + * @param Piggybank $piggybank + * + * @return \Illuminate\Http\JsonResponse + */ + public function piggyBankHistory(\Piggybank $piggybank) + { + /** @var \Grumpydictator\Gchart\GChart $chart */ + $chart = App::make('gchart'); + $chart->addColumn('Date', 'date'); + $chart->addColumn('Balance', 'number'); + + $set = \DB::table('piggybank_events')->where('piggybank_id', $piggybank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]); + + foreach ($set as $entry) { + $chart->addRow(new Carbon($entry->date), floatval($entry->sum)); + } + + $chart->generate(); + + return Response::json($chart->getData()); + + } + + /** + * @param RecurringTransaction $recurring + * + * @return \Illuminate\Http\JsonResponse + */ + public function recurringOverview(RecurringTransaction $recurring) + { + + /** @var \Grumpydictator\Gchart\GChart $chart */ + $chart = App::make('gchart'); + $chart->addColumn('Date', 'date'); + $chart->addColumn('Max amount', 'number'); + $chart->addColumn('Min amount', 'number'); + $chart->addColumn('Current entry', 'number'); + + // get first transaction or today for start: + $first = $recurring->transactionjournals()->orderBy('date', 'ASC')->first(); + if ($first) { + $start = $first->date; + } else { + $start = new Carbon; + } + $end = new Carbon; + while ($start <= $end) { + $result = $recurring->transactionjournals()->before($end)->after($start)->first(); + if ($result) { + $amount = $result->getAmount(); + } else { + $amount = 0; + } + unset($result); + $chart->addRow(clone $start, $recurring->amount_max, $recurring->amount_min, $amount); + $start = DateKit::addPeriod($start, $recurring->repeat_freq, 0); + } + + $chart->generate(); + return Response::json($chart->getData()); } /** * @return \Illuminate\Http\JsonResponse - * @throws \Firefly\Exception\FireflyException + * @throws \FireflyIII\Exception\FireflyException */ public function recurringTransactionsOverview() { @@ -365,14 +599,8 @@ class GoogleChartController extends BaseController * Set of paid transaction journals. * Set of unpaid recurring transactions. */ - $paid = [ - 'items' => [], - 'amount' => 0 - ]; - $unpaid = [ - 'items' => [], - 'amount' => 0 - ]; + $paid = ['items' => [], 'amount' => 0]; + $unpaid = ['items' => [], 'amount' => 0]; /** @var \Grumpydictator\Gchart\GChart $chart */ $chart = App::make('gchart'); @@ -382,9 +610,6 @@ class GoogleChartController extends BaseController /** @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 */ @@ -404,8 +629,7 @@ class GoogleChartController extends BaseController /* * Get end of period for $current: */ - $currentEnd = clone $current; - $dateKit->endOfPeriod($currentEnd, $entry->repeat_freq); + $currentEnd = DateKit::endOfPeriod($current, $entry->repeat_freq); /* * In the current session range? @@ -430,7 +654,7 @@ class GoogleChartController extends BaseController /* * Add some time for the next loop! */ - $dateKit->addPeriod($current, $entry->repeat_freq, intval($entry->skip)); + $current = DateKit::addPeriod($current, $entry->repeat_freq, intval($entry->skip)); } @@ -440,160 +664,93 @@ class GoogleChartController extends BaseController $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 + * @param $year * * @return \Illuminate\Http\JsonResponse */ - public function accountSankeyOutChart(Account $account) + public function yearInExp($year) { - // collect all relevant entries. - $set = []; - + try { + $start = new Carbon('01-01-' . $year); + } catch (Exception $e) { + App::abort(500); + } /** @var \Grumpydictator\Gchart\GChart $chart */ $chart = App::make('gchart'); - $chart->addColumn('From', 'string'); - $chart->addColumn('To', 'string', 'domain'); - $chart->addColumn('Weight', 'number'); + $chart->addColumn('Month', 'date'); + $chart->addColumn('Income', 'number'); + $chart->addColumn('Expenses', 'number'); - $transactions = $account->transactions()->with( - ['transactionjournal', 'transactionjournal.transactions', 'transactionjournal.budgets', 'transactionjournal.transactiontype', - 'transactionjournal.categories'] - )->before(Session::get('end'))->after( - Session::get('start') - )->get(); + /** @var \FireflyIII\Database\TransactionJournal $tj */ + $tj = App::make('FireflyIII\Database\TransactionJournal'); - /** @var Transaction $transaction */ - foreach ($transactions as $transaction) { - $amount = floatval($transaction->amount); - $type = $transaction->transactionJournal->transactionType->type; + $end = clone $start; + $end->endOfYear(); + while ($start < $end) { - if ($amount < 0 && $type != 'Transfer') { + // total income: + $income = $tj->getSumOfIncomesByMonth($start); + $expense = $tj->getSumOfExpensesByMonth($start); - // 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; - } + $chart->addRow(clone $start, $income, $expense); + $start->addMonth(); } - // 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 + * @param $year * * @return \Illuminate\Http\JsonResponse */ - public function accountSankeyInChart(Account $account) + public function yearInExpSum($year) { - // collect all relevant entries. - $set = []; - + try { + $start = new Carbon('01-01-' . $year); + } catch (Exception $e) { + App::abort(500); + } /** @var \Grumpydictator\Gchart\GChart $chart */ $chart = App::make('gchart'); - $chart->addColumn('From', 'string'); - $chart->addColumn('To', 'string', 'domain'); - $chart->addColumn('Weight', 'number'); + $chart->addColumn('Summary', 'string'); + $chart->addColumn('Income', 'number'); + $chart->addColumn('Expenses', '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 \FireflyIII\Database\TransactionJournal $tj */ + $tj = App::make('FireflyIII\Database\TransactionJournal'); - /** @var Transaction $transaction */ - foreach ($transactions as $transaction) { - $amount = floatval($transaction->amount); - $type = $transaction->transactionJournal->transactionType->type; + $end = clone $start; + $end->endOfYear(); + $income = 0; + $expense = 0; + $count = 0; + while ($start < $end) { - if ($amount > 0 && $type != 'Transfer') { + // total income: + $income += $tj->getSumOfIncomesByMonth($start); + $expense += $tj->getSumOfExpensesByMonth($start); + $count++; - $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; - } + $start->addMonth(); } + $chart->addRow('Sum', $income, $expense); + $count = $count > 0 ? $count : 1; + $chart->addRow('Average', ($income / $count), ($expense / $count)); - // add rows to the chart: - foreach ($grouped as $entry) { - $chart->addRow($entry[0], $entry[1], $entry[2]); - } $chart->generate(); + return Response::json($chart->getData()); } diff --git a/app/controllers/GoogleTableController.php b/app/controllers/GoogleTableController.php deleted file mode 100644 index 8f51270363..0000000000 --- a/app/controllers/GoogleTableController.php +++ /dev/null @@ -1,240 +0,0 @@ -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 - ); - } - -// Date -// Description -// Amount (€) -// From -// To -// Budget / category -// ID - - - $chart->generate(); - return Response::json($chart->getData()); - } -} \ No newline at end of file diff --git a/app/controllers/HelpController.php b/app/controllers/HelpController.php new file mode 100644 index 0000000000..1424351c40 --- /dev/null +++ b/app/controllers/HelpController.php @@ -0,0 +1,54 @@ +There is no help for this route!

'; + $helpTitle = 'Help'; + + return Response::json(['title' => $helpTitle, 'text' => $helpText]); + } + + // content in cache + if (Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text')) { + $helpText = Cache::get('help.' . $route . '.text'); + $helpTitle = Cache::get('help.' . $route . '.title'); + + return Response::json(['title' => $helpTitle, 'text' => $helpText]); + } + + // get the help-content from Github: + $URL = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md'; + try { + $content = file_get_contents($URL); + } catch (ErrorException $e) { + $content = '

There is no help for this route.

'; + } + if (strlen($content) > 0) { + $helpText = \Michelf\Markdown::defaultTransform($content); + $helpTitle = $route; + + Cache::put('help.' . $route . '.text', $helpText, 10080); // a week. + Cache::put('help.' . $route . '.title', $helpTitle, 10080); + + return Response::json(['title' => $helpTitle, 'text' => $helpText]); + } + + $helpText = '

There is no help for this route!

'; + $helpTitle = 'Help'; + + return Response::json(['title' => $helpTitle, 'text' => $helpText]); + + } +} \ No newline at end of file diff --git a/app/controllers/HomeController.php b/app/controllers/HomeController.php index ffd33b86cd..9617608efc 100644 --- a/app/controllers/HomeController.php +++ b/app/controllers/HomeController.php @@ -1,75 +1,11 @@ _accounts = $accounts; - $this->_preferences = $preferences; - $this->_journal = $journal; - } - - 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]); - - } - - /* - * - */ - public function sessionPrev() - { - /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); - $toolkit->prev(); - return Redirect::back(); - //return Redirect::route('index'); - } - - /* - * - */ - public function sessionNext() - { - /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); - $toolkit->next(); - return Redirect::back(); - //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 */ @@ -85,89 +21,81 @@ class HomeController extends BaseController */ public function index() { - Event::fire('limits.check'); - Event::fire('piggybanks.check'); - Event::fire('recurring.check'); - // count, maybe Firefly needs some introducing text to show: - $count = $this->_accounts->count(); + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + + /** @var \FireflyIII\Database\TransactionJournal $jrnls */ + $jrnls = App::make('FireflyIII\Database\TransactionJournal'); + + /** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface'); + + $count = $acct->countAssetAccounts(); + $start = Session::get('start'); $end = Session::get('end'); // get the preference for the home accounts to show: - $frontpage = $this->_preferences->get('frontpageAccounts', []); + $frontpage = $preferences->get('frontpageAccounts', []); if ($frontpage->data == []) { - $accounts = $this->_accounts->getActiveDefault(); + $accounts = $acct->getAssetAccounts(); } else { - $accounts = $this->_accounts->getByIds($frontpage->data); + $accounts = $acct->getByIds($frontpage->data); } $transactions = []; foreach ($accounts as $account) { - $set = $this->_journal->getByAccountInDateRange($account, 10, $start, $end); + $set = $jrnls->getInDateRangeAccount($account, 10, $start, $end); if (count($set) > 0) { $transactions[] = [$set, $account]; } } // 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'); + return View::make('index')->with('count', $count)->with('transactions', $transactions)->with('title', 'Firefly')->with('subTitle', 'What\'s playing?') + ->with('mainTitleIcon', 'fa-fire'); } - public function cleanup() + /** + * @param $range + * + * @return \Illuminate\Http\RedirectResponse + */ + public function rangeJump($range) { - /** @var \FireflyIII\Database\TransactionJournal $jrnls */ - $jrnls = App::make('FireflyIII\Database\TransactionJournal'); - /** @var \FireflyIII\Database\Account $acct */ - $acct = \App::make('FireflyIII\Database\Account'); + $valid = ['1D', '1W', '1M', '3M', '6M', '1Y',]; - /** @var \FireflyIII\Database\AccountType $acctType */ - $acctType = \App::make('FireflyIII\Database\AccountType'); - $rightAcctType = $acctType->findByWhat('revenue'); + /** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface'); - $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 . ')
'; - } - } - break; - } - - - } + if (in_array($range, $valid)) { + $preferences->set('viewRange', $range); + Session::forget('range'); } + return Redirect::back(); + } + /** + * @return \Illuminate\Http\RedirectResponse + */ + public function sessionNext() + { + Navigation::next(); + + return Redirect::back(); + } + + /** + * @return \Illuminate\Http\RedirectResponse + */ + public function sessionPrev() + { + Navigation::prev(); + + return Redirect::back(); } } \ No newline at end of file diff --git a/app/controllers/JsonController.php b/app/controllers/JsonController.php index 671e771b95..9f0512572a 100644 --- a/app/controllers/JsonController.php +++ b/app/controllers/JsonController.php @@ -1,25 +1,11 @@ helper = $helper; - - - } /** * Returns a list of categories. @@ -28,8 +14,8 @@ class JsonController extends BaseController */ public function categories() { - /** @var \Firefly\Storage\Category\EloquentCategoryRepository $categories */ - $categories = App::make('Firefly\Storage\Category\CategoryRepositoryInterface'); + /** @var \FireflyIII\Database\Category $categories */ + $categories = App::make('FireflyIII\Database\Category'); $list = $categories->get(); $return = []; foreach ($list as $entry) { @@ -48,9 +34,9 @@ class JsonController extends BaseController */ public function expenseAccounts() { - /** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */ - $accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); - $list = $accounts->getOfTypes(['Expense account', 'Beneficiary account']); + /** @var \FireflyIII\Database\Account $accounts */ + $accounts = App::make('FireflyIII\Database\Account'); + $list = $accounts->getExpenseAccounts(); $return = []; foreach ($list as $entry) { $return[] = $entry->name; @@ -61,99 +47,13 @@ class JsonController extends BaseController } /** - * Returns a list of transactions, expenses only, using the given parameters. - * - * @return \Illuminate\Http\JsonResponse - */ - public function expenses() - { - - /* - * Gets most parameters from the Input::all() array: - */ - $parameters = $this->helper->dataTableParameters(); - - /* - * Add some more parameters to fine tune the query: - */ - $parameters['transactionTypes'] = ['Withdrawal']; - $parameters['amount'] = 'negative'; - - /* - * Get the query: - */ - $query = $this->helper->journalQuery($parameters); - - /* - * Build result set: - */ - $resultSet = $this->helper->journalDataset($parameters, $query); - - - /* - * Build return data: - */ - 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); - return Response::json($resultSet); - } - - /** - * @return \Illuminate\Http\JsonResponse|string - */ - public function revenue() - { - $parameters = $this->helper->dataTableParameters(); - $parameters['transactionTypes'] = ['Deposit']; - $parameters['amount'] = 'positive'; - - $query = $this->helper->journalQuery($parameters); - $resultSet = $this->helper->journalDataset($parameters, $query); - - - /* - * Build return data: - */ - return Response::json($resultSet); - } - - /** - * Returns a JSON list of all revenue accounts. - * * @return \Illuminate\Http\JsonResponse */ public function revenueAccounts() { - /** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */ - $accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); - $list = $accounts->getOfTypes(['Revenue account']); + /** @var \FireflyIII\Database\Account $accounts */ + $accounts = App::make('FireflyIII\Database\Account'); + $list = $accounts->getRevenueAccounts(); $return = []; foreach ($list as $entry) { $return[] = $entry->name; @@ -162,25 +62,4 @@ class JsonController extends BaseController return Response::json($return); } - - /** - * Returns a list of all transfers. - * - * @return \Illuminate\Http\JsonResponse - */ - public function transfers() - { - $parameters = $this->helper->dataTableParameters(); - $parameters['transactionTypes'] = ['Transfer']; - $parameters['amount'] = 'positive'; - - $query = $this->helper->journalQuery($parameters); - $resultSet = $this->helper->journalDataset($parameters, $query); - - - /* - * Build return data: - */ - return Response::json($resultSet); - } -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/controllers/LimitController.php b/app/controllers/LimitController.php deleted file mode 100644 index f76345a5cf..0000000000 --- a/app/controllers/LimitController.php +++ /dev/null @@ -1,162 +0,0 @@ -_budgets = $budgets; - $this->_limits = $limits; - - View::share('title','Envelopes'); - View::share('mainTitleIcon', 'fa-tasks'); - } - - /** - * @param Budget $budget - * - * @return $this - */ - public function create(\Budget $budget = null) - { - $periods = \Config::get('firefly.periods_to_text'); - $prefilled = [ - 'startdate' => \Input::get('startdate') ? : date('Y-m-d'), - 'repeat_freq' => \Input::get('repeat_freq') ? : 'monthly', - 'budget_id' => $budget ? $budget->id : null - ]; - - /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); - $budgets = $toolkit->makeSelectList($this->_budgets->get()); - - return View::make('limits.create')->with('budgets', $budgets)->with( - 'periods', $periods - )->with('prefilled', $prefilled)->with('subTitle','New envelope'); - } - - /** - * @param Limit $limit - * - * @return $this - */ - public function delete(\Limit $limit) - { - return View::make('limits.delete')->with('limit', $limit)->with('subTitle','Delete envelope'); - } - - /** - * @param Limit $limit - * - * @return \Illuminate\Http\RedirectResponse - */ - public function destroy(\Limit $limit) - { - Event::fire('limits.destroy', [$limit]); // before - $success = $this->_limits->destroy($limit); - - if ($success) { - Session::flash('success', 'The envelope was deleted.'); - } else { - Session::flash('error', 'Could not delete the envelope. Check the logs to be sure.'); - } - if (Input::get('from') == 'date') { - return Redirect::route('budgets.index'); - } else { - return Redirect::route('budgets.index.budget'); - } - } - - /** - * @param Limit $limit - * - * @return $this - */ - public function edit(Limit $limit) - { - /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); - - $budgets = $toolkit->makeSelectList($this->_budgets->get()); - $periods = \Config::get('firefly.periods_to_text'); - - return View::make('limits.edit')->with('limit', $limit)->with('budgets', $budgets)->with( - 'periods', $periods - )->with('subTitle','Edit envelope'); - } - - /** - * @param Budget $budget - * - * @return $this|\Illuminate\Http\RedirectResponse - */ - public function store(Budget $budget = null) - { - - // find a limit with these properties, as Firefly might already have one: - $limit = $this->_limits->store(Input::all()); - if ($limit->validate()) { - Session::flash('success', 'Envelope created!'); - Event::fire('limits.store', [$limit]); - if (Input::get('from') == 'date') { - return Redirect::route('budgets.index'); - } else { - return Redirect::route('budgets.index.budget'); - } - } else { - Session::flash('error', 'Could not save new envelope.'); - $budgetId = $budget ? $budget->id : null; - $parameters = [$budgetId, 'from' => Input::get('from')]; - - return Redirect::route('budgets.limits.create', $parameters)->withInput() - ->withErrors($limit->errors()); - } - } - - /** - * @param Limit $limit - * - * @return $this|\Illuminate\Http\RedirectResponse - */ - public function update(\Limit $limit) - { - - - $limit = $this->_limits->update($limit, Input::all()); - - if ($limit->validate()) { - Event::fire('limits.update', [$limit]); - Session::flash('success', 'Limit saved!'); - if (Input::get('from') == 'date') { - return Redirect::route('budgets.index'); - } else { - return Redirect::route('budgets.index.budget'); - } - - - } else { - Session::flash('error', 'Could not save new limit: ' . $limit->errors()->first()); - - return Redirect::route('budgets.limits.edit', [$limit->id, 'from' => Input::get('from')])->withInput() - ->withErrors($limit->errors()); - } - - } - - -} \ No newline at end of file diff --git a/app/controllers/MigrateController.php b/app/controllers/MigrateController.php deleted file mode 100644 index a9bd16c200..0000000000 --- a/app/controllers/MigrateController.php +++ /dev/null @@ -1,52 +0,0 @@ -with('index', 'Migration')->with('title','Migrate')-> - with('subTitle','From Firefly II to Firefly III'); - } - - /** - * - */ - public function upload() - { - if (Input::hasFile('file') && Input::file('file')->isValid()) { - $path = storage_path(); - $fileName = 'firefly-iii-import-' . date('Y-m-d-H-i') . '.json'; - $fullName = $path . DIRECTORY_SEPARATOR . $fileName; - if (Input::file('file')->move($path, $fileName)) { - // so now Firefly pushes something in a queue and does something with it! Yay! - \Log::debug('Pushed a job to start the import.'); - Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => \Auth::user()->id]); - if (Config::get('queue.default') == 'sync') { - Session::flash('success', 'Your data has been imported!'); - } else { - Session::flash( - 'success', - 'The import job has been queued. Please be patient. Data will appear slowly. Please be patient.' - ); - } - - return Redirect::route('index'); - } - Session::flash('error', 'Could not save file to storage.'); - return Redirect::route('migrate.index'); - - } else { - Session::flash('error', 'Please upload a file.'); - return Redirect::route('migrate.index'); - } - - } - -} \ No newline at end of file diff --git a/app/controllers/PiggybankController.php b/app/controllers/PiggybankController.php index fb582a6652..0b4a8a61ab 100644 --- a/app/controllers/PiggybankController.php +++ b/app/controllers/PiggybankController.php @@ -1,7 +1,6 @@ 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'); - } - - /** - * @param Piggybank $piggyBank + * Add money to piggy bank * - * @return $this - */ - public function delete(Piggybank $piggybank) - { - return View::make('piggybanks.delete') - ->with('piggybank', $piggybank) - ->with('subTitle', 'Delete "' . $piggybank->name . '"') - ->with('title', 'Piggy banks') - ->with('mainTitleIcon', 'fa-sort-amount-asc'); - } - - /** - * @param Piggybank $piggyBank - * - * @return \Illuminate\Http\RedirectResponse - */ - public function destroy(Piggybank $piggyBank) - { - Event::fire('piggybanks.destroy', [$piggyBank]); - - /** @var \FireflyIII\Database\Piggybank $acct */ - $repos = App::make('FireflyIII\Database\Piggybank'); - $repos->destroy($piggyBank); - Session::flash('success', 'Piggy bank deleted.'); - - return Redirect::route('piggybanks.index'); - } - - /** - * @param Piggybank $piggyBank - * - * @return $this - */ - public function edit(Piggybank $piggybank) - { - - /** @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()); - - /* - * 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 * * @return $this */ public function add(Piggybank $piggybank) { - /** @var \FireflyIII\Database\Piggybank $acct */ + /** @var \FireflyIII\Database\Piggybank $repos */ $repos = App::make('FireflyIII\Database\Piggybank'); $leftOnAccount = $repos->leftOnAccount($piggybank->account); @@ -125,66 +39,87 @@ class PiggybankController extends BaseController return View::make('piggybanks.add', compact('piggybank'))->with('maxAmount', $amount); } + /** + * @return mixed + */ + public function create() + { + + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + + $periods = Config::get('firefly.piggybank_periods'); + + + $accounts = FFForm::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'); + } + /** * @param Piggybank $piggybank * + * @return $this + */ + public function delete(Piggybank $piggybank) + { + return View::make('piggybanks.delete')->with('piggybank', $piggybank)->with('subTitle', 'Delete "' . $piggybank->name . '"')->with( + 'title', 'Piggy banks' + )->with('mainTitleIcon', 'fa-sort-amount-asc'); + } + + /** + * @param Piggybank $piggyBank + * * @return \Illuminate\Http\RedirectResponse */ - public function postAdd(Piggybank $piggybank) + public function destroy(Piggybank $piggyBank) { - $amount = round(floatval(Input::get('amount')), 2); - /** @var \FireflyIII\Database\Piggybank $acct */ $repos = App::make('FireflyIII\Database\Piggybank'); + $repos->destroy($piggyBank); + Session::flash('success', 'Piggy bank deleted.'); - $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 + * @return $this */ - public function remove(Piggybank $piggybank) + public function edit(Piggybank $piggybank) { - return View::make('piggybanks.remove', compact('piggybank')); + + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + + $periods = Config::get('firefly.piggybank_periods'); + + $accounts = FFForm::makeSelectList($acct->getAssetAccounts()); + + /* + * Flash some data to fill the form. + */ + $prefilled = ['name' => $piggybank->name, + 'account_id' => $piggybank->account_id, + 'targetamount' => $piggybank->targetamount, + 'targetdate' => !is_null($piggybank->targetdate) ? $piggybank->targetdate->format('Y-m-d') : null, + 'reminder' => $piggybank->reminder, + 'remind_me' => intval($piggybank->remind_me) == 1 || !is_null($piggybank->reminder) ? 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 - * - * @return \Illuminate\Http\RedirectResponse + * @return $this */ - public function postRemove(Piggybank $piggybank) - { - $amount = floatval(Input::get('amount')); - - $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 { - Session::flash('error', 'Could not remove ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".'); - } - return Redirect::route('piggybanks.index'); - } - public function index() { /** @var \FireflyIII\Database\Piggybank $repos */ @@ -205,27 +140,117 @@ class PiggybankController extends BaseController */ $account = $piggybank->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 - ]; + $accounts[$account->id] = ['name' => $account->name, 'balance' => Steam::balance($account), + '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; } } + return View::make('piggybanks.index', compact('piggybanks', 'accounts'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc'); } - - public function show(Piggybank $piggyBank) + /** + * POST add money to piggy bank + * + * @param Piggybank $piggybank + * + * @return \Illuminate\Http\RedirectResponse + */ + public function postAdd(Piggybank $piggybank) { - throw new NotImplementedException; + $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(); + + /* + * Create event! + */ + Event::fire('piggybank.addMoney', [$piggybank, $amount]); // new and used. + + 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\Http\RedirectResponse + */ + public function postRemove(Piggybank $piggybank) + { + $amount = floatval(Input::get('amount')); + + $savedSoFar = $piggybank->currentRelevantRep()->currentamount; + + if ($amount <= $savedSoFar) { + $repetition = $piggybank->currentRelevantRep(); + $repetition->currentamount -= $amount; + $repetition->save(); + + /* + * Create event! + */ + Event::fire('piggybank.removeMoney', [$piggybank, $amount]); // new and used. + + Session::flash('success', 'Removed ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".'); + } else { + Session::flash('error', 'Could not remove ' . mf($amount, false) . ' from "' . 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 $this + */ + public function show(Piggybank $piggybank) + { + + $events = $piggybank->piggybankevents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get(); + + /* + * Number of reminders: + */ + + $amountPerReminder = $piggybank->amountPerReminder(); + $remindersCount = $piggybank->countFutureReminders(); + + return View::make('piggybanks.show', compact('amountPerReminder', 'remindersCount', 'piggybank', 'events'))->with('title', 'Piggy banks')->with( + 'mainTitleIcon', 'fa-sort-amount-asc' + )->with( + 'subTitle', $piggybank->name + ); } @@ -251,14 +276,21 @@ class PiggybankController extends BaseController 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); + $piggyBank = $repos->store($data); + + /* + * Create the relevant repetition per Event. + */ + Event::fire('piggybank.store', [$piggyBank]); // new and used. + Session::flash('success', 'New piggy bank stored!'); if ($data['post_submit_action'] == 'create_another') { - return Redirect::route('piggybanks.create'); + return Redirect::route('piggybanks.create')->withInput(); } else { return Redirect::route('piggybanks.index'); } @@ -277,7 +309,8 @@ class PiggybankController extends BaseController /** * @param Piggybank $piggyBank * - * @return $this|\Illuminate\Http\RedirectResponse + * @return $this + * @throws FireflyException */ public function update(Piggybank $piggyBank) { @@ -290,7 +323,7 @@ class PiggybankController extends BaseController default: throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"'); break; - case 'create_another': + case 'return_to_edit': case 'update': $messages = $repos->validate($data); /** @var MessageBag $messages ['errors'] */ @@ -298,13 +331,15 @@ class PiggybankController extends BaseController 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); + Event::fire('piggybank.update', [$piggyBank]); // new and used. Session::flash('success', 'Piggy bank updated!'); - if ($data['post_submit_action'] == 'create_another') { + if ($data['post_submit_action'] == 'return_to_edit') { return Redirect::route('piggybanks.edit', $piggyBank->id); } else { return Redirect::route('piggybanks.index'); @@ -314,6 +349,7 @@ class PiggybankController extends BaseController Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); + return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput(); break; } diff --git a/app/controllers/PreferencesController.php b/app/controllers/PreferencesController.php index 2e1d82bcda..0d6a373843 100644 --- a/app/controllers/PreferencesController.php +++ b/app/controllers/PreferencesController.php @@ -1,29 +1,19 @@ _accounts = $accounts; - $this->_preferences = $preferences; - View::share('title','Preferences'); - View::share('mainTitleIcon','fa-gear'); + View::share('title', 'Preferences'); + View::share('mainTitleIcon', 'fa-gear'); } /** @@ -31,16 +21,18 @@ class PreferencesController extends BaseController */ public function index() { - $accounts = $this->_accounts->getDefault(); + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); - $viewRange = $this->_preferences->get('viewRange', '1M'); + /** @var \FireflyIII\Shared\Preferences\Preferences $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\Preferences'); + + $accounts = $acct->getAssetAccounts(); + $viewRange = $preferences->get('viewRange', '1M'); $viewRangeValue = $viewRange->data; + $frontpage = $preferences->get('frontpageAccounts', []); - // pref: - $frontpage = $this->_preferences->get('frontpageAccounts', []); - - return View::make('preferences.index')->with('accounts', $accounts)->with('frontpageAccounts', $frontpage) - ->with('viewRange', $viewRangeValue); + return View::make('preferences.index')->with('accounts', $accounts)->with('frontpageAccounts', $frontpage)->with('viewRange', $viewRangeValue); } /** @@ -49,15 +41,18 @@ class PreferencesController extends BaseController public function postIndex() { + /** @var \FireflyIII\Shared\Preferences\Preferences $preferences */ + $preferences = App::make('FireflyIII\Shared\Preferences\Preferences'); + // frontpage accounts $frontpageAccounts = []; foreach (Input::get('frontpageAccounts') as $id) { $frontpageAccounts[] = intval($id); } - $this->_preferences->set('frontpageAccounts', $frontpageAccounts); + $preferences->set('frontpageAccounts', $frontpageAccounts); // view range: - $this->_preferences->set('viewRange', Input::get('viewRange')); + $preferences->set('viewRange', Input::get('viewRange')); // forget session values: Session::forget('start'); Session::forget('end'); diff --git a/app/controllers/ProfileController.php b/app/controllers/ProfileController.php index beda24d3cf..938d41f28a 100644 --- a/app/controllers/ProfileController.php +++ b/app/controllers/ProfileController.php @@ -1,7 +1,5 @@ user = $user; + return View::make('profile.change-password')->with('title', Auth::user()->email)->with('subTitle', 'Change your password')->with( + 'mainTitleIcon', 'fa-user' + ); } /** @@ -22,21 +22,7 @@ class ProfileController extends BaseController */ public function index() { - return View::make('profile.index') - ->with('title', 'Profile') - ->with('subTitle', Auth::user()->email) - ->with('mainTitleIcon', 'fa-user'); - } - - /** - * @return \Illuminate\View\View - */ - public function changePassword() - { - return View::make('profile.change-password') - ->with('title', Auth::user()->email) - ->with('subTitle', 'Change your password') - ->with('mainTitleIcon', 'fa-user'); + return View::make('profile.index')->with('title', 'Profile')->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user'); } /** @@ -70,8 +56,9 @@ class ProfileController extends BaseController } // update the user with the new password. - /** @noinspection PhpParamsInspection */ - $this->user->updatePassword(Auth::user(), Input::get('new1')); + /** @var \FireflyIII\Database\User $repository */ + $repository = \App::make('FireflyIII\Database\User'); + $repository->updatePassword(Auth::user(), Input::get('new1')); Session::flash('success', 'Password changed!'); diff --git a/app/controllers/RecurringController.php b/app/controllers/RecurringController.php index db60b34004..bd3a7faed6 100644 --- a/app/controllers/RecurringController.php +++ b/app/controllers/RecurringController.php @@ -1,27 +1,18 @@ _repository = $repository; - $this->_helper = $helper; View::share('title', 'Recurring transactions'); View::share('mainTitleIcon', 'fa-rotate-right'); @@ -34,9 +25,7 @@ class RecurringController extends BaseController { $periods = \Config::get('firefly.periods_to_text'); - return View::make('recurring.create') - ->with('periods', $periods) - ->with('subTitle', 'Create new'); + return View::make('recurring.create')->with('periods', $periods)->with('subTitle', 'Create new'); } /** @@ -46,9 +35,9 @@ class RecurringController extends BaseController */ public function delete(RecurringTransaction $recurringTransaction) { - return View::make('recurring.delete') - ->with('recurringTransaction', $recurringTransaction) - ->with('subTitle', 'Delete "' . $recurringTransaction->name . '"'); + return View::make('recurring.delete')->with('recurringTransaction', $recurringTransaction)->with( + 'subTitle', 'Delete "' . $recurringTransaction->name . '"' + ); } /** @@ -59,7 +48,11 @@ class RecurringController extends BaseController public function destroy(RecurringTransaction $recurringTransaction) { //Event::fire('recurring.destroy', [$recurringTransaction]); - $result = $this->_repository->destroy($recurringTransaction); + + /** @var \FireflyIII\Database\Recurring $repository */ + $repository = App::make('FireflyIII\Database\Recurring'); + + $result = $repository->destroy($recurringTransaction); if ($result === true) { Session::flash('success', 'The recurring transaction was deleted.'); } else { @@ -79,10 +72,9 @@ class RecurringController extends BaseController { $periods = \Config::get('firefly.periods_to_text'); - return View::make('recurring.edit') - ->with('periods', $periods) - ->with('recurringTransaction', $recurringTransaction) - ->with('subTitle', 'Edit "' . $recurringTransaction->name . '"'); + return View::make('recurring.edit')->with('periods', $periods)->with('recurringTransaction', $recurringTransaction)->with( + 'subTitle', 'Edit "' . $recurringTransaction->name . '"' + ); } /** @@ -90,150 +82,144 @@ class RecurringController extends BaseController */ public function index() { - return View::make('recurring.index'); - } + /** @var \FireflyIII\Database\Recurring $repos */ + $repos = App::make('FireflyIII\Database\Recurring'); - /** - * - */ - public function show(RecurringTransaction $recurringTransaction) - { - return View::make('recurring.show') - ->with('recurring', $recurringTransaction) - ->with('subTitle', $recurringTransaction->name); + $recurring = $repos->get(); + + return View::make('recurring.index', compact('recurring')); } /** * @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]); - } + /** @var \FireflyIII\Database\Recurring $repos */ + $repos = App::make('FireflyIII\Database\Recurring'); + $repos->scanEverything($recurringTransaction); + Session::flash('success', 'Rescanned everything.'); + return Redirect::back(); - - } + /** + * @param RecurringTransaction $recurringTransaction + * + * @return mixed + */ + public function show(RecurringTransaction $recurringTransaction) + { + $journals = $recurringTransaction->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get(); + $hideRecurring = true; + + + return View::make('recurring.show', compact('journals', 'hideRecurring', 'finalDate'))->with('recurring', $recurringTransaction)->with( + 'subTitle', $recurringTransaction->name + ); + } + + /** + * @return $this + * @throws FireflyException + */ public function store() { - $data = Input::except(['_token', 'post_submit_action']); - switch (Input::get('post_submit_action')) { + $data = Input::except('_token'); + /** @var \FireflyIII\Database\Recurring $repos */ + $repos = App::make('FireflyIII\Database\Recurring'); + + switch ($data['post_submit_action']) { default: - throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); + throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"'); break; - case 'store': case 'create_another': - /* - * Try to store: - */ - $messageBag = $this->_repository->store($data); + 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 recurring transaction: ' . $messages['errors']->first()); - /* - * Failure! - */ - if ($messageBag->count() > 0) { - Session::flash('error', 'Could not save recurring transaction: ' . $messageBag->first()); - return Redirect::route('recurring.create')->withInput()->withErrors($messageBag); + return Redirect::route('recurring.create')->withInput()->withErrors($messages['errors']); } + // store! + $repos->store($data); + Session::flash('success', 'New recurring transaction stored!'); - /* - * Success! - */ - Session::flash('success', 'Recurring transaction "' . e(Input::get('name')) . '" saved!'); - - /* - * Redirect to original location or back to the form. - */ - if (Input::get('post_submit_action') == 'create_another') { + if ($data['post_submit_action'] == 'create_another') { return Redirect::route('recurring.create')->withInput(); } else { return Redirect::route('recurring.index'); } break; case 'validate_only': - $messageBags = $this->_helper->validate($data); - + $messageBags = $repos->validate($data); Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); + return Redirect::route('recurring.create')->withInput(); break; } } + /** + * @param RecurringTransaction $recurringTransaction + * + * @return $this + * @throws FireflyException + */ public function update(RecurringTransaction $recurringTransaction) { - $data = Input::except(['_token', 'post_submit_action']); + /** @var \FireflyIII\Database\Recurring $repos */ + $repos = App::make('FireflyIII\Database\Recurring'); + $data = Input::except('_token'); + switch (Input::get('post_submit_action')) { - case 'update': - case 'return_to_edit': - $messageBag = $this->_repository->update($recurringTransaction, $data); - if ($messageBag->count() == 0) { - // has been saved, return to index: - Session::flash('success', 'Recurring transaction updated!'); - - if (Input::get('post_submit_action') == 'return_to_edit') { - return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput(); - } else { - return Redirect::route('recurring.index'); - } - } else { - Session::flash('error', 'Could not update recurring transaction: ' . $messageBag->first()); - - return Redirect::route('transactions.edit', $recurringTransaction->id)->withInput() - ->withErrors($messageBag); - } - - + default: + throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"'); break; - case 'validate_only': - $data = Input::all(); - $data['id'] = $recurringTransaction->id; - $messageBags = $this->_helper->validate($data); + 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 recurring transaction: ' . $messages['errors']->first()); + return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors($messages['errors']); + } + // store! + $repos->update($recurringTransaction, $data); + Session::flash('success', 'Recurring transaction updated!'); + + if ($data['post_submit_action'] == 'create_another') { + return Redirect::route('recurring.edit', $recurringTransaction->id); + } else { + return Redirect::route('recurring.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('recurring.edit', $recurringTransaction->id)->withInput(); - break; - // update - default: - throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); + return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput(); break; } - -// /** @var \RecurringTransaction $recurringTransaction */ -// $recurringTransaction = $this->_repository->update($recurringTransaction, Input::all()); -// if ($recurringTransaction->errors()->count() == 0) { -// Session::flash('success', 'The recurring transaction has been updated.'); -// //Event::fire('recurring.update', [$recurringTransaction]); -// -// return Redirect::route('recurring.index'); -// } else { -// Session::flash( -// 'error', 'Could not update the recurring transaction: ' . $recurringTransaction->errors()->first() -// ); -// -// return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors( -// $recurringTransaction->errors() -// ); -// } } } \ No newline at end of file diff --git a/app/controllers/ReminderController.php b/app/controllers/ReminderController.php index 657f3e8a10..fe8c186e88 100644 --- a/app/controllers/ReminderController.php +++ b/app/controllers/ReminderController.php @@ -1,4 +1,5 @@ remindersable)) { + default: + throw new FireflyException('Cannot act on reminder for ' . get_class($reminder->remindersable)); + break; + break; + case 'Piggybank': + $amount = Reminders::amountForReminder($reminder); + $prefilled = [ + 'amount' => round($amount, 2), + 'description' => 'Money for ' . $reminder->remindersable->name, + 'piggybank_id' => $reminder->remindersable_id, + 'account_to_id' => $reminder->remindersable->account_id + ]; + Session::flash('prefilled', $prefilled); + + return Redirect::route('transactions.create', 'transfer'); + break; + + } + } + + /** + * @param Reminder $reminder + * + * @return \Illuminate\Http\RedirectResponse + */ + public function dismiss(Reminder $reminder) + { + $reminder->active = 0; + $reminder->save(); + Session::flash('success', 'Reminder dismissed'); + + return Redirect::route('index'); + } + + /** + * @param Reminder $reminder + * + * @return \Illuminate\Http\RedirectResponse + */ + public function notnow(Reminder $reminder) + { + $reminder->active = 0; + $reminder->notnow = 1; + $reminder->save(); + Session::flash('success', 'Reminder dismissed'); + + return Redirect::route('index'); + } + + /** + * @param Reminder $reminder + * + * @return \Illuminate\View\View + */ + public function show(Reminder $reminder) + { + + $amount = null; + if (get_class($reminder->remindersable) == 'Piggybank') { + + $amount = Reminders::amountForReminder($reminder); + } + + return View::make('reminders.show', compact('reminder', 'amount')); + } + } \ No newline at end of file diff --git a/app/controllers/RepeatedExpenseController.php b/app/controllers/RepeatedExpenseController.php new file mode 100644 index 0000000000..b3bdd762e0 --- /dev/null +++ b/app/controllers/RepeatedExpenseController.php @@ -0,0 +1,135 @@ +getAssetAccounts()); + + return View::make('repeatedexpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with( + 'subTitleIcon', 'fa-plus' + ); + } + + /** + * @return \Illuminate\View\View + */ + public function index() + { + + $subTitle = 'Overview'; + + /** @var \FireflyIII\Database\RepeatedExpense $repository */ + $repository = App::make('FireflyIII\Database\RepeatedExpense'); + + $expenses = $repository->get(); + $expenses->each( + function (Piggybank $piggyBank) use ($repository) { + $piggyBank->currentRelevantRep(); + } + ); + + return View::make('repeatedexpense.index', compact('expenses', 'subTitle')); + } + + /** + * @param Piggybank $piggyBank + * + * @return \Illuminate\View\View + */ + public function show(Piggybank $piggyBank) + { + $subTitle = $piggyBank->name; + $today = Carbon::now(); + + /** @var \FireflyIII\Database\RepeatedExpense $repository */ + $repository = App::make('FireflyIII\Database\RepeatedExpense'); + + $repetitions = $piggyBank->piggybankrepetitions()->get(); + $repetitions->each( + function (PiggybankRepetition $repetition) use ($repository) { + $repository->calculateParts($repetition); + } + ); + + return View::make('repeatedexpense.show', compact('repetitions', 'piggyBank', 'today', 'subTitle')); + } + + /** + * @return $this + * @throws FireflyException + */ + public function store() + { + $data = Input::all(); + $data['repeats'] = 1; + /** @var \FireflyIII\Database\RepeatedExpense $repository */ + $repository = App::make('FireflyIII\Database\RepeatedExpense'); + + 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 = $repository->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 repeated expense: ' . $messages['errors']->first()); + + return Redirect::route('repeated.create')->withInput()->withErrors($messages['errors']); + } + // store! + $repeated = $repository->store($data); + + /* + * Create the relevant repetition per Event. + */ + Event::fire('piggybank.store', [$repeated]); // new and used. + + Session::flash('success', 'New repeated expense stored!'); + + if ($data['post_submit_action'] == 'create_another') { + return Redirect::route('repeated.create')->withInput(); + } else { + return Redirect::route('repeated.index'); + } + break; + case 'validate_only': + $messageBags = $repository->validate($data); + Session::flash('warnings', $messageBags['warnings']); + Session::flash('successes', $messageBags['successes']); + Session::flash('errors', $messageBags['errors']); + + return Redirect::route('repeated.create')->withInput(); + break; + } + } +} \ No newline at end of file diff --git a/app/controllers/ReportController.php b/app/controllers/ReportController.php index baab2c53a0..ecc45c9649 100644 --- a/app/controllers/ReportController.php +++ b/app/controllers/ReportController.php @@ -7,6 +7,108 @@ use Carbon\Carbon; class ReportController extends BaseController { + /** + * @param $year + * @param $month + * + * @return \Illuminate\View\View + */ + public function budgets($year, $month) + { + try { + $start = new Carbon($year . '-' . $month . '-01'); + } catch (Exception $e) { + App::abort(500); + } + $end = clone $start; + $title = 'Reports'; + $subTitle = 'Budgets in ' . $start->format('F Y'); + $mainTitleIcon = 'fa-line-chart'; + $subTitleIcon = 'fa-bar-chart'; + + $end->endOfMonth(); + + + // get a list of all budgets and expenses. + /** @var \FireflyIII\Database\Budget $budgetRepository */ + $budgetRepository = App::make('FireflyIII\Database\Budget'); + + /** @var \FireflyIII\Database\Account $accountRepository */ + $accountRepository = App::make('FireflyIII\Database\Account'); + + + $budgets = $budgetRepository->get(); + + // calculate some stuff: + $budgets->each( + function (Budget $budget) use ($start, $end, $budgetRepository) { + $limitRepetitions = $budget->limitrepetitions()->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d'))->where( + 'enddate', '<=', $end->format( + 'Y-m-d' + ) + )->get(); + $repInfo = []; + /** @var LimitRepetition $repetition */ + foreach ($limitRepetitions as $repetition) { + $spent = $budgetRepository->spentInPeriod($budget, $start, $end); + if ($spent > floatval($repetition->amount)) { + // overspent! + $overspent = true; + $pct = floatval($repetition->amount) / $spent * 100; + + } else { + $overspent = false; + $pct = $spent / floatval($repetition->amount) * 100; + } + $pctDisplay = $spent / floatval($repetition->amount) * 100; + $repInfo[] = [ + 'date' => DateKit::periodShow($repetition->startdate, $repetition->limit->repeat_freq), + 'spent' => $spent, + 'budgeted' => floatval($repetition->amount), + 'left' => floatval($repetition->amount) - $spent, + 'pct' => ceil($pct), + 'pct_display' => ceil($pctDisplay), + 'overspent' => $overspent, + ]; + } + $budget->repInfo = $repInfo; + + } + ); + + $accounts = $accountRepository->getAssetAccounts(); + + $accounts->each( + function (Account $account) use ($start, $end, $accountRepository) { + $journals = $accountRepository->getTransactionJournalsInRange($account, $start, $end); + $budgets = []; + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $budgetId = isset($journal->budgets[0]) ? $journal->budgets[0]->id : 0; + $budgetName = isset($journal->budgets[0]) ? $journal->budgets[0]->name : '(no budget)'; + if (!isset($budgets[$budgetId])) { + $arr = [ + 'budget_id' => $budgetId, + 'budget_name' => $budgetName, + 'spent' => floatval($journal->getAmount()), + 'budgeted' => 0, + ]; + $budgets[$budgetId] = $arr; + } else { + $budgets[$budgetId]['spent'] += floatval($journal->getAmount()); + } + } + foreach ($budgets as $budgetId => $budget) { + $budgets[$budgetId]['left'] = $budget['budgeted'] - $budget['spent']; + } + $account->budgetInfo = $budgets; + } + ); + + + return View::make('reports.budgets', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'budgets', 'accounts')); + + } /** * @@ -15,53 +117,194 @@ class ReportController extends BaseController { /** @var \FireflyIII\Database\TransactionJournal $journals */ $journals = App::make('FireflyIII\Database\TransactionJournal'); - $journal = $journals->first(); - - $date = $journal->date; - $years = []; + /** @var TransactionJournal $journal */ + $journal = $journals->first(); + if (is_null($journal)) { + $date = Carbon::now(); + } else { + $date = clone $journal->date; + } + $years = []; + $months = []; while ($date <= Carbon::now()) { $years[] = $date->format('Y'); $date->addYear(); } + // months + if (is_null($journal)) { + $date = Carbon::now(); + } else { + $date = clone $journal->date; + } + while ($date <= Carbon::now()) { + $months[] = [ + 'formatted' => $date->format('F Y'), + 'month' => intval($date->format('m')), + 'year' => intval($date->format('Y')), + ]; + $date->addMonth(); + } - return View::make('reports.index', compact('years'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart'); + return View::make('reports.index', compact('years', 'months'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart'); } /** * @param $year + * @param $month + * + * @return \Illuminate\View\View + */ + public function unbalanced($year, $month) + { + try { + $date = new Carbon($year . '-' . $month . '-01'); + } catch (Exception $e) { + App::abort(500); + } + $start = new Carbon($year . '-' . $month . '-01'); + $end = clone $start; + $title = 'Reports'; + $subTitle = 'Unbalanced transactions in ' . $start->format('F Y'); + $mainTitleIcon = 'fa-line-chart'; + $subTitleIcon = 'fa-bar-chart'; + $end->endOfMonth(); + + /** @var \FireflyIII\Database\TransactionJournal $journalRepository */ + $journalRepository = App::make('FireflyIII\Database\TransactionJournal'); + + /* + * Get all journals from this month: + */ + $journals = $journalRepository->getInDateRange($start, $end); + + /* + * Filter withdrawals: + */ + $withdrawals = $journals->filter( + function (TransactionJournal $journal) { + if ($journal->transactionType->type == 'Withdrawal' && count($journal->budgets) == 0) { + + // count groups related to balance. + if ($journal->transactiongroups()->where('relation', 'balance')->count() == 0) { + return $journal; + } + } + + return null; + } + ); + /* + * Filter deposits. + */ + $deposits = $journals->filter( + function (TransactionJournal $journal) { + if ($journal->transactionType->type == 'Deposit' && count($journal->budgets) == 0) { + // count groups related to balance. + if ($journal->transactiongroups()->where('relation', 'balance')->count() == 0) { + return $journal; + } + } + + return null; + } + ); + + + /* + * Filter transfers (not yet used) + */ + // $transfers = $journals->filter( + // function (TransactionJournal $journal) { + // if ($journal->transactionType->type == 'Transfer') { + // return $journal; + // } + // } + // ); + + $journals = $withdrawals->merge($deposits); + + + return View::make('reports.unbalanced', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'journals')); + } + + /** + * @param $year + * + * @return $this */ public function year($year) { + Config::set('app.debug', false); try { $date = new Carbon('01-01-' . $year); } catch (Exception $e) { App::abort(500); } + $date = new Carbon('01-01-' . $year); /** @var \FireflyIII\Database\TransactionJournal $tj */ $tj = App::make('FireflyIII\Database\TransactionJournal'); + /** @var \FireflyIII\Database\Account $accountRepository */ + $accountRepository = App::make('FireflyIII\Database\Account'); + + /** @var \FireflyIII\Database\Report $reportRepository */ + $reportRepository = App::make('FireflyIII\Database\Report'); + + $accounts = $accountRepository->getAssetAccounts(); + // get some sums going $summary = []; + /** @var \Account $account */ + $accounts->each( + function (\Account $account) { + if ($account->getMeta('accountRole') == 'sharedExpense') { + $account->sharedExpense = true; + } else { + $account->sharedExpense = false; + } + } + ); + $end = clone $date; $end->endOfYear(); while ($date < $end) { + $month = $date->format('F'); + + $income = 0; + $incomeShared = 0; + $expense = 0; + $expenseShared = 0; + + foreach ($accounts as $account) { + if ($account->sharedExpense === true) { + $incomeShared += $reportRepository->getIncomeByMonth($account, $date); + $expenseShared += $reportRepository->getExpenseByMonth($account, $date); + } else { + $income += $reportRepository->getIncomeByMonth($account, $date); + $expense += $reportRepository->getExpenseByMonth($account, $date); + } + } + $summary[] = [ - 'month' => $date->format('F'), - 'income' => $tj->getSumOfIncomesByMonth($date), - 'expense' => $tj->getSumOfExpensesByMonth($date), + 'month' => $month, + 'income' => $income, + 'expense' => $expense, + 'incomeShared' => $incomeShared, + 'expenseShared' => $expenseShared, ]; $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); + return View::make('reports.year', compact('summary', 'date'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart')->with('subTitle', $year) + ->with( + 'subTitleIcon', 'fa-bar-chart' + )->with('year', $year); } } \ No newline at end of file diff --git a/app/controllers/SearchController.php b/app/controllers/SearchController.php index 205933400b..e1588d8e3b 100644 --- a/app/controllers/SearchController.php +++ b/app/controllers/SearchController.php @@ -1,50 +1,38 @@ _helper = $helper; - - } - /** * Results always come in the form of an array [results, count, fullCount] */ public function index() { + + /** @var \FireflyIII\Search\Search $searcher */ + $searcher = App::make('FireflyIII\Search\Search'); + $subTitle = null; $rawQuery = null; - $result = []; + $result = []; if (!is_null(Input::get('q'))) { $rawQuery = trim(Input::get('q')); $words = explode(' ', $rawQuery); $subTitle = 'Results for "' . e($rawQuery) . '"'; - $transactions = $this->_helper->searchTransactions($words); - $accounts = $this->_helper->searchAccounts($words); - $categories = $this->_helper->searchCategories($words); - $budgets = $this->_helper->searchBudgets($words); - $tags = $this->_helper->searchTags($words); - $result = [ - 'transactions' => $transactions, - 'accounts' => $accounts, - 'categories' => $categories, - 'budgets' => $budgets, - 'tags' => $tags - ]; + $transactions = $searcher->searchTransactions($words); + $accounts = $searcher->searchAccounts($words); + $categories = $searcher->searchCategories($words); + $budgets = $searcher->searchBudgets($words); + $tags = $searcher->searchTags($words); + $result = ['transactions' => $transactions, 'accounts' => $accounts, 'categories' => $categories, 'budgets' => $budgets, 'tags' => $tags]; } return View::make('search.index')->with('title', 'Search')->with('subTitle', $subTitle)->with( 'mainTitleIcon', 'fa-search' - )->with('query', $rawQuery)->with('result',$result); + )->with('query', $rawQuery)->with('result', $result); } } \ No newline at end of file diff --git a/app/controllers/TransactionController.php b/app/controllers/TransactionController.php index e3ed758ed3..69333d6852 100644 --- a/app/controllers/TransactionController.php +++ b/app/controllers/TransactionController.php @@ -1,37 +1,67 @@ _repository = $repository; - $this->_helper = $helper; View::share('title', 'Transactions'); View::share('mainTitleIcon', 'fa-repeat'); } + /** + * + * TODO this needs cleaning up and thinking over. + * + * @param TransactionJournal $journal + * + * @return array|\Illuminate\Http\JsonResponse + */ + public function alreadyRelated(TransactionJournal $journal) + { + + $ids = []; + /** @var TransactionGroup $group */ + foreach ($journal->transactiongroups()->get() as $group) { + /** @var TransactionJournal $jrnl */ + foreach ($group->transactionjournals()->get() as $jrnl) { + if ($jrnl->id != $journal->id) { + $ids[] = $jrnl->id; + } + } + } + $unique = array_unique($ids); + if (count($ids) > 0) { + + /** @var \FireflyIII\Database\TransactionJournal $repository */ + $repository = App::make('FireflyIII\Database\TransactionJournal'); + $set = $repository->getByIds($ids); + $set->each( + function (TransactionJournal $journal) { + $journal->amount = mf($journal->getAmount()); + } + ); + + return Response::json($set->toArray()); + } else { + return (new Collection)->toArray(); + } + } + /** * Shows the view helping the user to create a new transaction journal. * @@ -44,28 +74,31 @@ class TransactionController extends BaseController /* * The repositories we need: */ - /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); - /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ - $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); + /** @var \FireflyIII\Database\Account $accountRepository */ + $accountRepository = App::make('FireflyIII\Database\Account'); - /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ - $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); + /** @var \FireflyIII\Database\Budget $budgetRepository */ + $budgetRepository = App::make('FireflyIII\Database\Budget'); - /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ - $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); + /** @var \FireflyIII\Database\Piggybank $piggyRepository */ + $piggyRepository = App::make('FireflyIII\Database\Piggybank'); - // get asset accounts with names and id's. - $assetAccounts = $toolkit->makeSelectList($accountRepository->getActiveDefault()); + /** @var \FireflyIII\Database\RepeatedExpense $repRepository */ + $repRepository = App::make('FireflyIII\Database\RepeatedExpense'); + + // get asset accounts with names and id's . + $assetAccounts = FFForm::makeSelectList($accountRepository->getAssetAccounts()); // get budgets as a select list. - $budgets = $toolkit->makeSelectList($budgetRepository->get()); + $budgets = FFForm::makeSelectList($budgetRepository->get()); $budgets[0] = '(no budget)'; // get the piggy banks. - $piggies = $toolkit->makeSelectList($piggyRepository->get()); + $list = $piggyRepository->get()->merge($repRepository->get()); + $piggies = FFForm::makeSelectList($list); $piggies[0] = '(no piggy bank)'; + asort($piggies); /* * respond to a possible given values in the URL. @@ -79,9 +112,8 @@ class TransactionController extends BaseController } Session::put('prefilled', $prefilled); - return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with( - 'what', $what - )->with('piggies', $piggies)->with('subTitle', 'Add a new ' . $what); + return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with('what', $what)->with('piggies', $piggies) + ->with('subTitle', 'Add a new ' . $what); } /** @@ -102,7 +134,6 @@ class TransactionController extends BaseController } - /** * @param TransactionJournal $transactionJournal * @@ -111,19 +142,56 @@ class TransactionController extends BaseController public function destroy(TransactionJournal $transactionJournal) { $type = $transactionJournal->transactionType->type; - $transactionJournal->delete(); + + /** @var \FireflyIII\Database\TransactionJournal $repository */ + $repository = App::make('FireflyIII\Database\TransactionJournal'); + $repository->destroy($transactionJournal); + + $return = 'withdrawal'; + switch ($type) { - case 'Withdrawal': - return Redirect::route('transactions.expenses'); - break; case 'Deposit': - return Redirect::route('transactions.revenue'); + $return = 'deposit'; break; case 'Transfer': - return Redirect::route('transactions.transfers'); + $return = 'transfers'; break; } + + return Redirect::route('transactions.index', $return); + } + + /** + * TODO this needs cleaning up and thinking over. + * + * @return \Illuminate\Http\JsonResponse + */ + public function doRelate() + { + $id = intval(Input::get('id')); + $sister = intval(Input::get('relateTo')); + + /** @var \FireflyIII\Database\TransactionJournal $repository */ + $repository = App::make('FireflyIII\Database\TransactionJournal'); + + $journal = $repository->find($id); + $sis = $repository->find($sister); + + if ($journal && $sis) { + $group = new TransactionGroup; + $group->relation = 'balance'; + $group->user_id = $repository->getUser()->id; + $group->save(); + $group->transactionjournals()->save($journal); + $group->transactionjournals()->save($sis); + + return Response::json(true); + } + + return Response::json(false); + + } /** @@ -138,26 +206,25 @@ class TransactionController extends BaseController /* * All the repositories we need: */ - /** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\Toolkit'); - /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */ - $accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface'); + /** @var \FireflyIII\Database\Account $accountRepository */ + $accountRepository = App::make('FireflyIII\Database\Account'); - /** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */ - $budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface'); + /** @var \FireflyIII\Database\Budget $budgetRepository */ + $budgetRepository = App::make('FireflyIII\Database\Budget'); + + /** @var \FireflyIII\Database\Piggybank $piggyRepository */ + $piggyRepository = App::make('FireflyIII\Database\Piggybank'); - /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ - $piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); // type is useful for display: $what = strtolower($journal->transactiontype->type); // get asset accounts with names and id's. - $accounts = $toolkit->makeSelectList($accountRepository->getActiveDefault()); + $accounts = FFForm::makeSelectList($accountRepository->getAssetAccounts()); // get budgets as a select list. - $budgets = $toolkit->makeSelectList($budgetRepository->get()); + $budgets = FFForm::makeSelectList($budgetRepository->get()); $budgets[0] = '(no budget)'; /* @@ -165,8 +232,8 @@ class TransactionController extends BaseController * of the transactions in the journal has this field, it should all fill in nicely. */ // get the piggy banks. - $piggies = $toolkit->makeSelectList($piggyRepository->get()); - $piggies[0] = '(no piggy bank)'; + $piggies = FFForm::makeSelectList($piggyRepository->get()); + $piggies[0] = '(no piggy bank)'; $piggyBankId = 0; foreach ($journal->transactions as $t) { if (!is_null($t->piggybank_id)) { @@ -198,29 +265,61 @@ class TransactionController extends BaseController */ switch ($what) { case 'withdrawal': - $prefilled['account_id'] = $journal->transactions[0]->account->id; - $prefilled['expense_account'] = $journal->transactions[1]->account->name; - $prefilled['amount'] = floatval($journal->transactions[1]->amount); + if (floatval($journal->transactions[0]->amount) < 0) { + // transactions[0] is the asset account that paid for the withdrawal. + $prefilled['account_id'] = $journal->transactions[0]->account->id; + $prefilled['expense_account'] = $journal->transactions[1]->account->name; + $prefilled['amount'] = floatval($journal->transactions[1]->amount); + } else { + // transactions[1] is the asset account that paid for the withdrawal. + $prefilled['account_id'] = $journal->transactions[1]->account->id; + $prefilled['expense_account'] = $journal->transactions[0]->account->name; + $prefilled['amount'] = floatval($journal->transactions[0]->amount); + } + + $budget = $journal->budgets()->first(); if (!is_null($budget)) { $prefilled['budget_id'] = $budget->id; } break; case 'deposit': - $prefilled['account_id'] = $journal->transactions[1]->account->id; - $prefilled['revenue_account'] = $journal->transactions[0]->account->name; - $prefilled['amount'] = floatval($journal->transactions[1]->amount); + if (floatval($journal->transactions[0]->amount) < 0) { + // transactions[0] contains the account the money came from. + $prefilled['account_id'] = $journal->transactions[1]->account->id; + $prefilled['revenue_account'] = $journal->transactions[0]->account->name; + $prefilled['amount'] = floatval($journal->transactions[1]->amount); + } else { + // transactions[1] contains the account the money came from. + $prefilled['account_id'] = $journal->transactions[0]->account->id; + $prefilled['revenue_account'] = $journal->transactions[1]->account->name; + $prefilled['amount'] = floatval($journal->transactions[0]->amount); + + } + break; case 'transfer': - $prefilled['account_from_id'] = $journal->transactions[1]->account->id; - $prefilled['account_to_id'] = $journal->transactions[0]->account->id; - $prefilled['amount'] = floatval($journal->transactions[1]->amount); + if (floatval($journal->transactions[0]->amount) < 0) { + // zero = from account. + $prefilled['account_from_id'] = $journal->transactions[0]->account->id; + $prefilled['account_to_id'] = $journal->transactions[1]->account->id; + $prefilled['amount'] = floatval($journal->transactions[1]->amount); + } else { + // one = from account + $prefilled['account_from_id'] = $journal->transactions[1]->account->id; + $prefilled['account_to_id'] = $journal->transactions[0]->account->id; + $prefilled['amount'] = floatval($journal->transactions[0]->amount); + } + if ($journal->piggybankevents()->count() > 0) { + $prefilled['piggybank_id'] = $journal->piggybankevents()->first()->piggybank_id; + } break; } /* * Show the view. */ + return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts)->with( 'what', $what )->with('budgets', $budgets)->with('data', $prefilled)->with('piggies', $piggies)->with( @@ -229,23 +328,85 @@ class TransactionController extends BaseController } /** + * @param $what + * * @return $this */ - public function expenses() + public function index($what) { - return View::make('transactions.list')->with('subTitle', 'Expenses')->with( - 'subTitleIcon', 'fa-long-arrow-left' - )->with('what', 'expenses'); + + /** @var \FireflyIII\Database\TransactionJournal $repository */ + $repository = App::make('FireflyIII\Database\TransactionJournal'); + + switch ($what) { + case 'expenses': + case 'withdrawal': + $subTitleIcon = 'fa-long-arrow-left'; + $subTitle = 'Expenses'; + $journals = $repository->getWithdrawalsPaginated(50); + break; + case 'revenue': + case 'deposit': + $subTitleIcon = 'fa-long-arrow-right'; + $subTitle = 'Revenue, income and deposits'; + $journals = $repository->getDepositsPaginated(50); + break; + case 'transfer': + case 'transfers': + $subTitleIcon = 'fa-arrows-h'; + $subTitle = 'Transfers'; + $journals = $repository->getTransfersPaginated(50); + break; + } + + return View::make('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals')); + } /** - * @return $this + * @param TransactionJournal $journal + * + * @return \Illuminate\View\View */ - public function revenue() + public function relate(TransactionJournal $journal) { - return View::make('transactions.list')->with('subTitle', 'Revenue')->with( - 'subTitleIcon', 'fa-long-arrow-right' - )->with('what', 'revenue'); + $groups = $journal->transactiongroups()->get(); + $members = new Collection; + /** @var TransactionGroup $group */ + foreach ($groups as $group) { + /** @var TransactionJournal $jrnl */ + foreach ($group->transactionjournals()->get() as $jrnl) { + if ($jrnl->id != $journal->id) { + $members->push($jrnl); + } + } + } + + return View::make('transactions.relate', compact('journal', 'members')); + } + + /** + * TODO this needs cleaning up and thinking over. + * + * @param TransactionJournal $journal + * + * @return \Illuminate\Http\JsonResponse + */ + public function relatedSearch(TransactionJournal $journal) + { + $search = e(trim(Input::get('searchValue'))); + + /** @var \FireflyIII\Database\TransactionJournal $repository */ + $repository = App::make('FireflyIII\Database\TransactionJournal'); + + $result = $repository->searchRelated($search, $journal); + $result->each( + function (TransactionJournal $j) { + $j->amount = mf($j->getAmount()); + } + ); + + return Response::json($result->toArray()); } /** @@ -255,7 +416,30 @@ class TransactionController extends BaseController */ public function show(TransactionJournal $journal) { - return View::make('transactions.show')->with('journal', $journal)->with( + $journal->transactions->each( + function (\Transaction $t) use ($journal) { + $t->before = floatval( + $t->account->transactions()->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' + )->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))->where( + 'transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s') + )->where('transaction_journals.id', '!=', $journal->id)->sum('transactions.amount') + ); + $t->after = $t->before + $t->amount; + } + ); + $members = new Collection; + /** @var TransactionGroup $group */ + foreach ($journal->transactiongroups()->get() as $group) { + /** @var TransactionJournal $jrnl */ + foreach ($group->transactionjournals()->get() as $jrnl) { + if ($jrnl->id != $journal->id) { + $members->push($jrnl); + } + } + } + + return View::make('transactions.show', compact('journal', 'members'))->with( 'subTitle', $journal->transactionType->type . ' "' . $journal->description . '"' ); } @@ -268,62 +452,92 @@ class TransactionController extends BaseController */ public function store($what) { - /* - * Collect data to process: - */ - $data = Input::except(['_token']); - $data['what'] = $what; + $data = Input::except('_token'); + $data['what'] = $what; + $data['currency'] = 'EUR'; - switch (Input::get('post_submit_action')) { - case 'store': + /** @var \FireflyIII\Database\TransactionJournal $repository */ + $repository = App::make('FireflyIII\Database\TransactionJournal'); + + switch ($data['post_submit_action']) { + default: + throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"'); + break; case 'create_another': - /* - * Try to store: - */ - $messageBag = $this->_helper->store($data); + case 'store': + $messages = $repository->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 transaction: ' . $messages['errors']->first()); + + return Redirect::route('transactions.create', $what)->withInput()->withErrors($messages['errors']); + } + // store! + $journal = $repository->store($data); + Session::flash('success', 'New transaction stored!'); /* - * Failure! + * Trigger a search for the related (if selected) + * piggy bank and store an event. */ - if ($messageBag->count() > 0) { - Session::flash('error', 'Could not save transaction: ' . $messageBag->first()); - return Redirect::route('transactions.create', [$what])->withInput()->withErrors($messageBag); + $piggyID = null; + if (!is_null(Input::get('piggybank_id')) && intval(Input::get('piggybank_id')) > 0) { + $piggyID = intval(Input::get('piggybank_id')); + } + Event::fire('transactionJournal.store', [$journal, $piggyID]); // new and used. + /* + * Also trigger on both transactions. + */ + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + Event::fire('transaction.store', [$transaction]); } - /* - * Success! - */ - Session::flash('success', 'Transaction "' . e(Input::get('description')) . '" saved!'); - - /* - * Redirect to original location or back to the form. - */ - if (Input::get('post_submit_action') == 'create_another') { + if ($data['post_submit_action'] == 'create_another') { return Redirect::route('transactions.create', $what)->withInput(); } else { - return Redirect::route('transactions.index.' . $what); + return Redirect::route('transactions.index', $what); } - break; case 'validate_only': - $messageBags = $this->_helper->validate($data); - + $messageBags = $repository->validate($data); Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); - return Redirect::route('transactions.create', [$what])->withInput(); - break; - default: - throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.'); + + return Redirect::route('transactions.create', $what)->withInput(); break; } } - public function transfers() + /** + * TODO this needs cleaning up and thinking over. + * + * @param TransactionJournal $journal + * + * @return \Illuminate\Http\JsonResponse + * @throws Exception + */ + public function unrelate(TransactionJournal $journal) { - return View::make('transactions.list')->with('subTitle', 'Transfers')->with( - 'subTitleIcon', 'fa-arrows-h' - )->with('what', 'transfers'); + $groups = $journal->transactiongroups()->get(); + $relatedTo = intval(Input::get('relation')); + /** @var TransactionGroup $group */ + foreach ($groups as $group) { + foreach ($group->transactionjournals()->get() as $jrnl) { + if ($jrnl->id == $relatedTo) { + // remove from group: + $group->transactionjournals()->detach($relatedTo); + } + } + if ($group->transactionjournals()->count() == 1) { + $group->delete(); + } + } + + return Response::json(true); } @@ -334,38 +548,52 @@ class TransactionController extends BaseController */ public function update(TransactionJournal $journal) { + /** @var \FireflyIII\Database\TransactionJournal $repos */ + $repos = App::make('FireflyIII\Database\TransactionJournal'); + + $data = Input::except('_token'); + $data['currency'] = 'EUR'; + $data['what'] = strtolower($journal->transactionType->type); + + switch (Input::get('post_submit_action')) { case 'update': case 'return_to_edit': - $what = strtolower($journal->transactionType->type); - $messageBag = $this->_helper->update($journal, Input::all()); + $messageBag = $repos->update($journal, $data); if ($messageBag->count() == 0) { // has been saved, return to index: Session::flash('success', 'Transaction updated!'); - Event::fire('journals.update', [$journal]); + Event::fire('transactionJournal.update', [$journal]); // new and used. + + /* + * Also trigger on both transactions. + */ + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + Event::fire('transaction.update', [$transaction]); + } if (Input::get('post_submit_action') == 'return_to_edit') { return Redirect::route('transactions.edit', $journal->id)->withInput(); } else { - return Redirect::route('transactions.index.' . $what); + return Redirect::route('transactions.index', $data['what']); } } else { - Session::flash('error', 'Could not update transaction: ' . $journal->errors()->first()); + Session::flash('error', 'Could not update transaction: ' . $journal->getErrors()->first()); return Redirect::route('transactions.edit', $journal->id)->withInput()->withErrors( - $journal->errors() + $journal->getErrors() ); } break; case 'validate_only': - $data = Input::all(); - $data['what'] = strtolower($journal->transactionType->type); - $messageBags = $this->_helper->validate($data); + $messageBags = $repos->validate($data); Session::flash('warnings', $messageBags['warnings']); Session::flash('successes', $messageBags['successes']); Session::flash('errors', $messageBags['errors']); + return Redirect::route('transactions.edit', $journal->id)->withInput(); break; default: @@ -376,4 +604,4 @@ class TransactionController extends BaseController } -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/controllers/UserController.php b/app/controllers/UserController.php index 834c2bb9cb..5d101c850f 100644 --- a/app/controllers/UserController.php +++ b/app/controllers/UserController.php @@ -1,8 +1,5 @@ user = $user; - $this->email = $email; - } /** @@ -32,6 +23,18 @@ class UserController extends BaseController return View::make('user.login'); } + /** + * Logout user. + * + * @return \Illuminate\Http\RedirectResponse + */ + public function logout() + { + Auth::logout(); + Session::flush(); + + return Redirect::route('index'); + } /** * Login. @@ -41,10 +44,7 @@ class UserController extends BaseController public function postLogin() { $rememberMe = Input::get('remember_me') == '1'; - $data = [ - 'email' => Input::get('email'), - 'password' => Input::get('password') - ]; + $data = ['email' => Input::get('email'), 'password' => Input::get('password')]; $result = Auth::attempt($data, $rememberMe); if ($result) { return Redirect::route('index'); @@ -55,20 +55,6 @@ class UserController extends BaseController return View::make('user.login'); } - /** - * If allowed, show the register form. - * - * @return $this|\Illuminate\View\View - */ - public function register() - { - if (Config::get('auth.allow_register') !== true) { - return View::make('error')->with('message', 'Not possible'); - } - - return View::make('user.register'); - } - /** * If allowed, register the user. * @@ -84,32 +70,77 @@ class UserController extends BaseController if (Config::get('auth.allow_register') !== true) { return View::make('error')->with('message', 'Not possible'); } - $user = $this->user->register(Input::all()); + + /** @var \FireflyIII\Database\User $repository */ + $repository = App::make('FireflyIII\Database\User'); + + /** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */ + $email = App::make('FireflyIII\Shared\Mail\RegistrationInterface'); + + $user = $repository->register(Input::all()); + + + //$user = $this->user->register(Input::all()); if ($user) { if (Config::get('auth.verify_mail') === true) { - $this->email->sendVerificationMail($user); + $email->sendVerificationMail($user); return View::make('user.verification-pending'); } - $this->email->sendPasswordMail($user); + $email->sendPasswordMail($user); return View::make('user.registered'); } + return View::make('user.register'); } /** - * Logout user. + * If need to verify, send new reset code. + * Otherwise, send new password. * - * @return \Illuminate\Http\RedirectResponse + * @return \Illuminate\View\View */ - public function logout() + public function postRemindme() { - Auth::logout(); - Session::flush(); - return Redirect::route('index'); + /** @var \FireflyIII\Database\User $repository */ + $repository = App::make('FireflyIII\Database\User'); + + /** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */ + $email = App::make('FireflyIII\Shared\Mail\RegistrationInterface'); + + + $user = $repository->findByEmail(Input::get('email')); + if (!$user) { + Session::flash('error', 'No good!'); + + return View::make('user.remindme'); + } + if (Config::get('auth.verify_reset') === true) { + $email->sendResetVerification($user); + + return View::make('user.verification-pending'); + } + $email->sendPasswordMail($user); + + return View::make('user.registered'); + + } + + /** + * If allowed, show the register form. + * + * @return $this|\Illuminate\View\View + */ + public function register() + { + if (Config::get('auth.allow_register') !== true) { + return View::make('error')->with('message', 'Not possible'); + } + + return View::make('user.register'); } /** @@ -122,31 +153,6 @@ class UserController extends BaseController return View::make('user.remindme'); } - /** - * If need to verify, send new reset code. - * Otherwise, send new password. - * - * @return \Illuminate\View\View - */ - public function postRemindme() - { - $user = $this->user->findByEmail(Input::get('email')); - if (!$user) { - Session::flash('error', 'No good!'); - - return View::make('user.remindme'); - } - if (Config::get('auth.verify_reset') === true) { - $this->email->sendResetVerification($user); - - return View::make('user.verification-pending'); - } - $this->email->sendPasswordMail($user); - - return View::make('user.registered'); - - } - /** * Send a user a password based on his reset code. * @@ -156,9 +162,16 @@ class UserController extends BaseController */ public function reset($reset) { - $user = $this->user->findByReset($reset); + + /** @var \FireflyIII\Database\User $repository */ + $repository = App::make('FireflyIII\Database\User'); + + /** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */ + $email = App::make('FireflyIII\Shared\Mail\RegistrationInterface'); + + $user = $repository->findByReset($reset); if ($user) { - $this->email->sendPasswordMail($user); + $email->sendPasswordMail($user); return View::make('user.registered'); } diff --git a/app/database/migrations/2014_06_27_163032_create_users_table.php b/app/database/migrations/2014_06_27_163032_create_users_table.php index 25cc4349a3..7dce4f47b8 100644 --- a/app/database/migrations/2014_06_27_163032_create_users_table.php +++ b/app/database/migrations/2014_06_27_163032_create_users_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateUsersTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('users'); + } + /** * Run the migrations. * @@ -33,14 +43,4 @@ class CreateUsersTable extends Migration ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('users'); - } - } diff --git a/app/database/migrations/2014_06_27_163145_create_account_types_table.php b/app/database/migrations/2014_06_27_163145_create_account_types_table.php index b900c47bed..8a05459eaf 100644 --- a/app/database/migrations/2014_06_27_163145_create_account_types_table.php +++ b/app/database/migrations/2014_06_27_163145_create_account_types_table.php @@ -29,14 +29,14 @@ class CreateAccountTypesTable extends Migration public function up() { Schema::create( - 'account_types', function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->string('type', 50); - $table->boolean('editable'); + 'account_types', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->string('type', 50); + $table->boolean('editable'); - $table->unique('type'); - } + $table->unique('type'); + } ); } diff --git a/app/database/migrations/2014_06_27_163259_create_accounts_table.php b/app/database/migrations/2014_06_27_163259_create_accounts_table.php index e98a4d4581..0af5a60455 100644 --- a/app/database/migrations/2014_06_27_163259_create_accounts_table.php +++ b/app/database/migrations/2014_06_27_163259_create_accounts_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateAccountsTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('accounts'); + } + /** * Run the migrations. * @@ -22,34 +32,21 @@ class CreateAccountsTable extends Migration 'accounts', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->integer('user_id')->unsigned(); $table->integer('account_type_id')->unsigned(); $table->string('name', 100); $table->boolean('active'); // connect accounts to users - $table->foreign('user_id') - ->references('id')->on('users') - ->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); // connect accounts to account_types - $table->foreign('account_type_id') - ->references('id')->on('account_types') - ->onDelete('cascade'); + $table->foreign('account_type_id')->references('id')->on('account_types')->onDelete('cascade'); $table->unique(['user_id', 'account_type_id', 'name']); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('accounts'); - } - } diff --git a/app/database/migrations/2014_06_27_163817_create_components_table.php b/app/database/migrations/2014_06_27_163817_create_components_table.php index 998bef4ddf..210773199d 100644 --- a/app/database/migrations/2014_06_27_163817_create_components_table.php +++ b/app/database/migrations/2014_06_27_163817_create_components_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateComponentsTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('components'); + } + /** * Run the migrations. * @@ -22,14 +32,13 @@ class CreateComponentsTable extends Migration 'components', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->string('name', 50); $table->integer('user_id')->unsigned(); $table->string('class', 20); // connect components to users - $table->foreign('user_id') - ->references('id')->on('users') - ->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->unique(['user_id', 'class', 'name']); } @@ -37,14 +46,4 @@ class CreateComponentsTable extends Migration } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('components'); - } - } diff --git a/app/database/migrations/2014_06_27_163818_create_piggybanks_table.php b/app/database/migrations/2014_06_27_163818_create_piggybanks_table.php index 74ff3e20c8..25efdcf2ef 100644 --- a/app/database/migrations/2014_06_27_163818_create_piggybanks_table.php +++ b/app/database/migrations/2014_06_27_163818_create_piggybanks_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreatePiggybanksTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('piggybanks'); + } + /** * Run the migrations. * @@ -28,32 +38,20 @@ class CreatePiggybanksTable extends Migration $table->date('startdate')->nullable(); $table->date('targetdate')->nullable(); $table->boolean('repeats'); - $table->enum('rep_length', ['day', 'week', 'month', 'year'])->nullable(); + $table->enum('rep_length', ['day', 'week', 'quarter', 'month', 'year'])->nullable(); $table->smallInteger('rep_every')->unsigned(); $table->smallInteger('rep_times')->unsigned()->nullable(); - $table->enum('reminder', ['day', 'week', 'month', 'year'])->nullable(); + $table->enum('reminder', ['day', 'week', 'quarter', 'month', 'year'])->nullable(); $table->smallInteger('reminder_skip')->unsigned(); $table->boolean('remind_me'); $table->integer('order')->unsigned(); // connect account to piggybank. - $table->foreign('account_id') - ->references('id')->on('accounts') - ->onDelete('cascade'); + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); $table->unique(['account_id', 'name']); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('piggybanks'); - } - } diff --git a/app/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php b/app/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php index 64c82f8491..be165577cb 100644 --- a/app/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php +++ b/app/database/migrations/2014_06_27_164042_create_transaction_currencies_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateTransactionCurrenciesTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('transaction_currencies'); + } + /** * Run the migrations. * @@ -22,19 +32,10 @@ class CreateTransactionCurrenciesTable extends Migration 'transaction_currencies', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->string('code', 3); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('transaction_currencies'); - } - } diff --git a/app/database/migrations/2014_06_27_164512_create_transaction_types_table.php b/app/database/migrations/2014_06_27_164512_create_transaction_types_table.php index 6cd34a74c5..40cf43e973 100644 --- a/app/database/migrations/2014_06_27_164512_create_transaction_types_table.php +++ b/app/database/migrations/2014_06_27_164512_create_transaction_types_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateTransactionTypesTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('transaction_types'); + } + /** * Run the migrations. * @@ -22,19 +32,10 @@ class CreateTransactionTypesTable extends Migration 'transaction_types', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->string('type', 50); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('transaction_types'); - } - } diff --git a/app/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php b/app/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php index 46b48bc255..af41979983 100644 --- a/app/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php +++ b/app/database/migrations/2014_06_27_164619_create_recurring_transactions_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateRecurringTransactionsTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('recurring_transactions'); + } + /** * Run the migrations. * @@ -41,14 +51,4 @@ class CreateRecurringTransactionsTable extends Migration ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('recurring_transactions'); - } - } diff --git a/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php b/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php index 75886fa254..f895bca4d1 100644 --- a/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php +++ b/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateTransactionJournalsTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('transaction_journals'); + } + /** * Run the migrations. * @@ -22,6 +32,7 @@ class CreateTransactionJournalsTable extends Migration 'transaction_journals', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->integer('user_id')->unsigned(); $table->integer('transaction_type_id')->unsigned(); $table->integer('recurring_transaction_id')->unsigned()->nullable(); @@ -31,36 +42,18 @@ class CreateTransactionJournalsTable extends Migration $table->date('date'); // connect transaction journals to transaction types - $table->foreign('transaction_type_id') - ->references('id')->on('transaction_types') - ->onDelete('cascade'); + $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); // connect transaction journals to recurring transactions - $table->foreign('recurring_transaction_id') - ->references('id')->on('recurring_transactions') - ->onDelete('set null'); + $table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('set null'); // connect transaction journals to transaction currencies - $table->foreign('transaction_currency_id') - ->references('id')->on('transaction_currencies') - ->onDelete('cascade'); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); // connect users - $table->foreign('user_id') - ->references('id')->on('users') - ->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('transaction_journals'); - } - } diff --git a/app/database/migrations/2014_06_27_164836_create_transactions_table.php b/app/database/migrations/2014_06_27_164836_create_transactions_table.php index 9bd7c6177d..917d65e181 100644 --- a/app/database/migrations/2014_06_27_164836_create_transactions_table.php +++ b/app/database/migrations/2014_06_27_164836_create_transactions_table.php @@ -32,6 +32,7 @@ class CreateTransactionsTable extends Migration 'transactions', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); + $table->softDeletes(); $table->integer('account_id')->unsigned(); $table->integer('piggybank_id')->nullable()->unsigned(); $table->integer('transaction_journal_id')->unsigned(); @@ -39,19 +40,13 @@ class CreateTransactionsTable extends Migration $table->decimal('amount', 10, 2); // connect transactions to transaction journals - $table->foreign('transaction_journal_id') - ->references('id')->on('transaction_journals') - ->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); // connect piggy banks - $table->foreign('piggybank_id') - ->references('id')->on('piggybanks') - ->onDelete('set null'); + $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('set null'); // connect account id: - $table->foreign('account_id') - ->references('id')->on('accounts') - ->onDelete('cascade'); + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); } ); diff --git a/app/database/migrations/2014_06_27_165344_create_component_transaction_table.php b/app/database/migrations/2014_06_27_165344_create_component_transaction_table.php index 4c3a2c825a..7cbef507ee 100644 --- a/app/database/migrations/2014_06_27_165344_create_component_transaction_table.php +++ b/app/database/migrations/2014_06_27_165344_create_component_transaction_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateComponentTransactionTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('component_transaction'); + } + /** * Run the migrations. * @@ -25,26 +35,12 @@ class CreateComponentTransactionTable extends Migration $table->integer('transaction_id')->unsigned(); // connect to components - $table->foreign('component_id') - ->references('id')->on('components') - ->onDelete('cascade'); + $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); // connect to transactions - $table->foreign('transaction_id') - ->references('id')->on('transactions') - ->onDelete('cascade'); + $table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('component_transaction'); - } - } diff --git a/app/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php b/app/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php index 1d4b9c5f51..b34fbc1aa2 100644 --- a/app/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php +++ b/app/database/migrations/2014_07_05_171326_create_component_transaction_journal_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateComponentTransactionJournalTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('component_transaction_journal'); + } + /** * Run the migrations. * @@ -25,26 +35,12 @@ class CreateComponentTransactionJournalTable extends Migration $table->integer('transaction_journal_id')->unsigned(); // link components with component_id - $table->foreign('component_id') - ->references('id')->on('components') - ->onDelete('cascade'); + $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); // link transaction journals with transaction_journal_id - $table->foreign('transaction_journal_id') - ->references('id')->on('transaction_journals') - ->onDelete('cascade'); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('component_transaction_journal'); - } - } diff --git a/app/database/migrations/2014_07_06_123842_create_preferences_table.php b/app/database/migrations/2014_07_06_123842_create_preferences_table.php index 9aabb3bbcd..fb74ddd1bc 100644 --- a/app/database/migrations/2014_07_06_123842_create_preferences_table.php +++ b/app/database/migrations/2014_07_06_123842_create_preferences_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreatePreferencesTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('preferences'); + } + /** * Run the migrations. * @@ -27,21 +37,9 @@ class CreatePreferencesTable extends Migration $table->text('data'); // connect preferences to users - $table->foreign('user_id') - ->references('id')->on('users') - ->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('preferences'); - } - } diff --git a/app/database/migrations/2014_07_09_204843_create_session_table.php b/app/database/migrations/2014_07_09_204843_create_session_table.php index 7e29be1cce..c4423f33cf 100644 --- a/app/database/migrations/2014_07_09_204843_create_session_table.php +++ b/app/database/migrations/2014_07_09_204843_create_session_table.php @@ -10,6 +10,16 @@ use Illuminate\Database\Migrations\Migration; class CreateSessionTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('sessions'); + } + /** * Run the migrations. * @@ -26,14 +36,4 @@ class CreateSessionTable extends Migration ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('sessions'); - } - } diff --git a/app/database/migrations/2014_07_17_183717_create_limits_table.php b/app/database/migrations/2014_07_17_183717_create_limits_table.php index 0edb4a17c8..fb3dbe3660 100644 --- a/app/database/migrations/2014_07_17_183717_create_limits_table.php +++ b/app/database/migrations/2014_07_17_183717_create_limits_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateLimitsTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('limits'); + } + /** * Run the migrations. * @@ -31,21 +41,9 @@ class CreateLimitsTable extends Migration $table->unique(['component_id', 'startdate', 'repeat_freq']); // connect component - $table->foreign('component_id') - ->references('id')->on('components') - ->onDelete('cascade'); + $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('limits'); - } - } diff --git a/app/database/migrations/2014_07_19_055011_create_limit_repeat_table.php b/app/database/migrations/2014_07_19_055011_create_limit_repeat_table.php index f0578b463a..e69902a3eb 100644 --- a/app/database/migrations/2014_07_19_055011_create_limit_repeat_table.php +++ b/app/database/migrations/2014_07_19_055011_create_limit_repeat_table.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class CreateLimitRepeatTable extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('limit_repetitions'); + } + /** * Run the migrations. * @@ -30,21 +40,9 @@ class CreateLimitRepeatTable extends Migration $table->unique(['limit_id', 'startdate', 'enddate']); // connect limit - $table->foreign('limit_id') - ->references('id')->on('limits') - ->onDelete('cascade'); + $table->foreign('limit_id')->references('id')->on('limits')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('limit_repetitions'); - } - } diff --git a/app/database/migrations/2014_08_06_044416_recurring_transactions_to_components.php b/app/database/migrations/2014_08_06_044416_recurring_transactions_to_components.php index 18a789b4bd..326228af51 100644 --- a/app/database/migrations/2014_08_06_044416_recurring_transactions_to_components.php +++ b/app/database/migrations/2014_08_06_044416_recurring_transactions_to_components.php @@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint; class RecurringTransactionsToComponents extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('component_recurring_transaction'); + } + /** * Run the migrations. * @@ -26,26 +36,12 @@ class RecurringTransactionsToComponents extends Migration $table->boolean('optional'); // link components with component_id - $table->foreign('component_id') - ->references('id')->on('components') - ->onDelete('cascade'); + $table->foreign('component_id')->references('id')->on('components')->onDelete('cascade'); // link transaction journals with transaction_journal_id - $table->foreign('recurring_transaction_id') - ->references('id')->on('recurring_transactions') - ->onDelete('cascade'); + $table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('component_recurring_transaction'); - } - } diff --git a/app/database/migrations/2014_08_12_173919_create_piggy_instance.php b/app/database/migrations/2014_08_12_173919_create_piggy_instance.php index c4a0c0ceeb..96dbb1ec06 100644 --- a/app/database/migrations/2014_08_12_173919_create_piggy_instance.php +++ b/app/database/migrations/2014_08_12_173919_create_piggy_instance.php @@ -6,6 +6,16 @@ use Illuminate\Database\Schema\Blueprint; class CreatePiggyInstance extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('piggybank_repetitions'); + } + /** * Run the migrations. * @@ -25,21 +35,9 @@ class CreatePiggyInstance extends Migration $table->unique(['piggybank_id', 'startdate', 'targetdate']); // connect instance to piggybank. - $table->foreign('piggybank_id') - ->references('id')->on('piggybanks') - ->onDelete('cascade'); + $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('piggybank_repetitions'); - } - } diff --git a/app/database/migrations/2014_08_18_100330_create_piggy_events.php b/app/database/migrations/2014_08_18_100330_create_piggy_events.php index 3551a6b0d9..6abd680a19 100644 --- a/app/database/migrations/2014_08_18_100330_create_piggy_events.php +++ b/app/database/migrations/2014_08_18_100330_create_piggy_events.php @@ -6,6 +6,16 @@ use Illuminate\Database\Schema\Blueprint; class CreatePiggyEvents extends Migration { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('piggybank_events'); + } + /** * Run the migrations. * @@ -22,21 +32,9 @@ class CreatePiggyEvents extends Migration $table->decimal('amount', 10, 2); // connect instance to piggybank. - $table->foreign('piggybank_id') - ->references('id')->on('piggybanks') - ->onDelete('cascade'); + $table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('piggybank_events'); - } - } diff --git a/app/database/migrations/2014_08_23_113221_create_reminders_table.php b/app/database/migrations/2014_08_23_113221_create_reminders_table.php index 12a04c3b00..bf7ac668b4 100644 --- a/app/database/migrations/2014_08_23_113221_create_reminders_table.php +++ b/app/database/migrations/2014_08_23_113221_create_reminders_table.php @@ -33,9 +33,7 @@ class CreateRemindersTable extends Migration $table->boolean('active'); // connect reminders to users - $table->foreign('user_id') - ->references('id')->on('users') - ->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); } ); } diff --git a/app/database/migrations/2014_08_31_153322_create_importmaps_table.php b/app/database/migrations/2014_08_31_153322_create_importmaps_table.php index c4b68c7b68..5d7eeefcf9 100644 --- a/app/database/migrations/2014_08_31_153322_create_importmaps_table.php +++ b/app/database/migrations/2014_08_31_153322_create_importmaps_table.php @@ -1,41 +1,41 @@ increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->string('file',500); - $table->integer('totaljobs')->unsigned(); - $table->integer('jobsdone')->unsigned(); + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('importmaps'); + } - // connect maps to users - $table->foreign('user_id') - ->references('id')->on('users') - ->onDelete('cascade'); - }); - } + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::create( + 'importmaps', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('user_id')->unsigned(); + $table->string('file', 500); + $table->integer('totaljobs')->unsigned(); + $table->integer('jobsdone')->unsigned(); - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('importmaps'); - } + // connect maps to users + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } } diff --git a/app/database/migrations/2014_09_01_052816_create_importentries_table.php b/app/database/migrations/2014_09_01_052816_create_importentries_table.php index 70cd8e862c..5003dfc9d5 100644 --- a/app/database/migrations/2014_09_01_052816_create_importentries_table.php +++ b/app/database/migrations/2014_09_01_052816_create_importentries_table.php @@ -1,11 +1,21 @@ integer('new')->unsigned(); // connect import map. - $table->foreign('importmap_id') - ->references('id')->on('importmaps') - ->onDelete('cascade'); + $table->foreign('importmap_id')->references('id')->on('importmaps')->onDelete('cascade'); } ); } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('importentries'); - } - } diff --git a/app/database/migrations/2014_09_02_103101_create_failed_jobs_table.php b/app/database/migrations/2014_09_02_103101_create_failed_jobs_table.php index eb0e8e6c12..89cb377e9d 100644 --- a/app/database/migrations/2014_09_02_103101_create_failed_jobs_table.php +++ b/app/database/migrations/2014_09_02_103101_create_failed_jobs_table.php @@ -23,13 +23,15 @@ class CreateFailedJobsTable extends Migration */ public function up() { - Schema::create('failed_jobs', function (Blueprint $table) { - $table->increments('id'); - $table->text('connection'); - $table->text('queue'); - $table->text('payload'); - $table->timestamp('failed_at'); - }); + Schema::create( + 'failed_jobs', function (Blueprint $table) { + $table->increments('id'); + $table->text('connection'); + $table->text('queue'); + $table->text('payload'); + $table->timestamp('failed_at'); + } + ); } } diff --git a/app/database/migrations/2014_11_10_172053_create_account_meta.php b/app/database/migrations/2014_11_10_172053_create_account_meta.php index 8038f3726f..55281d7748 100644 --- a/app/database/migrations/2014_11_10_172053_create_account_meta.php +++ b/app/database/migrations/2014_11_10_172053_create_account_meta.php @@ -1,11 +1,22 @@ integer('transaction_journal_id')->unsigned()->nullable(); + $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('set null'); + } + ); + } + +} diff --git a/app/database/migrations/2014_11_17_194734_event_table_additions_1.php b/app/database/migrations/2014_11_17_194734_event_table_additions_1.php new file mode 100644 index 0000000000..6ccfb73235 --- /dev/null +++ b/app/database/migrations/2014_11_17_194734_event_table_additions_1.php @@ -0,0 +1,36 @@ +boolean('notnow')->default(0); + $table->integer('remindersable_id')->unsigned()->nullable(); + $table->string('remindersable_type')->nullable(); + } + ); + } + +} diff --git a/app/database/migrations/2014_11_25_204833_cacheTable.php b/app/database/migrations/2014_11_25_204833_cacheTable.php new file mode 100644 index 0000000000..2ffbce905d --- /dev/null +++ b/app/database/migrations/2014_11_25_204833_cacheTable.php @@ -0,0 +1,34 @@ +string('key')->unique(); + $table->text('value'); + $table->integer('expiration'); + } + ); + } + +} diff --git a/app/database/migrations/2014_11_29_135749_transactiongroups.php b/app/database/migrations/2014_11_29_135749_transactiongroups.php new file mode 100644 index 0000000000..09fecd7feb --- /dev/null +++ b/app/database/migrations/2014_11_29_135749_transactiongroups.php @@ -0,0 +1,42 @@ +increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id')->unsigned(); + $table->enum('relation', ['balance']); + + // connect reminders to users + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + + + } + +} diff --git a/app/database/migrations/2014_11_29_140217_transactiongroupsjoin.php b/app/database/migrations/2014_11_29_140217_transactiongroupsjoin.php new file mode 100644 index 0000000000..ca3c9c1c6d --- /dev/null +++ b/app/database/migrations/2014_11_29_140217_transactiongroupsjoin.php @@ -0,0 +1,42 @@ +increments('id'); + $table->integer('transaction_group_id')->unsigned(); + $table->integer('transaction_journal_id')->unsigned(); + + $table->foreign('transaction_group_id', 'tr_grp_id')->references('id')->on('transaction_groups')->onDelete('cascade'); + $table->foreign('transaction_journal_id', 'tr_trj_id')->references('id')->on('transaction_journals')->onDelete('cascade'); + + $table->unique(['transaction_group_id', 'transaction_journal_id'], 'tt_joined'); + } + ); + } + +} diff --git a/app/database/seeds/DatabaseSeeder.php b/app/database/seeds/DatabaseSeeder.php index dd35eb592a..29319ca38c 100644 --- a/app/database/seeds/DatabaseSeeder.php +++ b/app/database/seeds/DatabaseSeeder.php @@ -19,6 +19,7 @@ class DatabaseSeeder extends Seeder $this->call('TransactionCurrencySeeder'); $this->call('TransactionTypeSeeder'); $this->call('DefaultUserSeeder'); + $this->call('TestContentSeeder'); } } diff --git a/app/database/seeds/DefaultUserSeeder.php b/app/database/seeds/DefaultUserSeeder.php index dc7a102cbf..1b484c895c 100644 --- a/app/database/seeds/DefaultUserSeeder.php +++ b/app/database/seeds/DefaultUserSeeder.php @@ -8,16 +8,15 @@ class DefaultUserSeeder extends Seeder public function run() { DB::table('users')->delete(); + if (App::environment() == 'testing') { - User::create( - [ - 'email' => 's@nder.be', - 'password' => 'sander', - 'reset' => null, - 'remember_token' => null, - 'migrated' => 0 - ] - ); + User::create( + ['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null, 'migrated' => 0] + ); + User::create( + ['email' => 'acceptance@example.com', 'password' => 'acceptance', 'reset' => null, 'remember_token' => null, 'migrated' => 0] + ); + } } diff --git a/app/database/seeds/TestContentSeeder.php b/app/database/seeds/TestContentSeeder.php new file mode 100644 index 0000000000..202b8ebd11 --- /dev/null +++ b/app/database/seeds/TestContentSeeder.php @@ -0,0 +1,176 @@ +first(); + $expenseType = AccountType::whereType('Expense account')->first(); + $revenueType = AccountType::whereType('Revenue account')->first(); + $ibType = AccountType::whereType('Initial balance account')->first(); + + $euro = TransactionCurrency::whereCode('EUR')->first(); + + $obType = TransactionType::whereType('Opening balance')->first(); + $withdrawal = TransactionType::whereType('Withdrawal')->first(); + $transfer = TransactionType::whereType('Transfer')->first(); + $deposit = TransactionType::whereType('Deposit')->first(); + + $user = User::whereEmail('thegrumpydictator@gmail.com')->first(); + + if ($user) { + // create two asset accounts. + $checking = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Checking account', 'active' => 1]); + $savings = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Savings account', 'active' => 1]); + + // create two budgets: + $groceriesBudget = Budget::create(['user_id' => $user->id, 'name' => 'Groceries']); + $billsBudget = Budget::create(['user_id' => $user->id, 'name' => 'Bills']); + + // create two categories: + $dailyGroceries = Category::create(['user_id' => $user->id, 'name' => 'Daily groceries']); + $lunch = Category::create(['user_id' => $user->id, 'name' => 'Lunch']); + $house = Category::create(['user_id' => $user->id, 'name' => 'House']); + + // create some expense accounts. + $ah = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Albert Heijn', 'active' => 1]); + $plus = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'PLUS', 'active' => 1]); + $vitens = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Vitens', 'active' => 1]); + $greenchoice = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Greenchoice', 'active' => 1]); + $portaal = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Portaal', 'active' => 1]); + $store = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Buy More', 'active' => 1]); + + // create three revenue accounts. + $employer = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Employer', 'active' => 1]); + $taxes = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'IRS', 'active' => 1]); + $job = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Job', 'active' => 1]); + + // put money in the two accounts (initial balance) + $ibChecking = Account::create( + ['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Checking account initial balance', 'active' => 0] + ); + $ibSavings = Account::create( + ['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Savings account initial balance', 'active' => 0] + ); + + $this->createTransaction($ibChecking, $checking, 4000, $obType, 'Initial Balance for Checking account', '2014-01-01'); + $this->createTransaction($ibSavings, $savings, 10000, $obType, 'Initial Balance for Savings account', '2014-01-01'); + + + // create some expenses and incomes and what-not (for every month): + $start = new Carbon('2014-01-01'); + $end = Carbon::now()->startOfMonth()->subDay(); + while ($start <= $end) { + $this->createTransaction( + $checking, $portaal, 500, $withdrawal, 'Rent for ' . $start->format('F Y'), $start->format('Y-m-') . '01', $billsBudget, $house + ); + $this->createTransaction( + $checking, $vitens, 12, $withdrawal, 'Water for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house + ); + $this->createTransaction( + $checking, $greenchoice, 110, $withdrawal, 'Power for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house + ); + + // spend on groceries + $groceriesStart = clone $start; + for ($i = 0; $i < 13; $i++) { + $amt = rand(100, 300) / 10; + $lunchAmount = rand(30, 60) / 10; + $this->createTransaction( + $checking, $plus, $lunchAmount, $withdrawal, 'Lunch', $groceriesStart->format('Y-m-d'), $groceriesBudget, $lunch + ); + $groceriesStart->addDay(); + if (intval($groceriesStart->format('d')) % 2 == 0) { + $this->createTransaction( + $checking, $ah, $amt, $withdrawal, 'Groceries', $groceriesStart->format('Y-m-d'), $groceriesBudget, $dailyGroceries + ); + } + $groceriesStart->addDay(); + } + + // get income: + $this->createTransaction($employer, $checking, rand(1400, 1600), $deposit, 'Salary', $start->format('Y-m-') . '23'); + + // pay taxes: + $this->createTransaction($checking, $taxes, rand(50, 70), $withdrawal, 'Taxes in ' . $start->format('F Y'), $start->format('Y-m-') . '27'); + + // some other stuff. + + + $start->addMonth(); + + } + + // create some big expenses, move some money around. + $this->createTransaction($savings, $checking, 1259, $transfer, 'Money for new PC', $end->format('Y-m') . '-11'); + $this->createTransaction($checking, $store, 1259, $withdrawal, 'New PC', $end->format('Y-m') . '-12'); + + // create two budgets + + // create two categories + + // create + } + + } + } + + /** + * @param Account $from + * @param Account $to + * @param $amount + * @param TransactionType $type + * @param $description + * @param $date + * + * @return TransactionJournal + */ + public function createTransaction( + Account $from, Account $to, $amount, TransactionType $type, $description, $date, Budget $budget = null, Category $category = null + ) { + $user = User::whereEmail('thegrumpydictator@gmail.com')->first(); + $euro = TransactionCurrency::whereCode('EUR')->first(); + + /** @var TransactionJournal $journal */ + $journal = TransactionJournal::create( + [ + 'user_id' => $user->id, + 'transaction_type_id' => $type->id, + 'transaction_currency_id' => $euro->id, + 'description' => $description, + 'completed' => 1, + 'date' => $date + ] + ); + + Transaction::create( + [ + 'account_id' => $from->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount * -1 + ] + + ); + Transaction::create( + [ + 'account_id' => $to->id, + 'transaction_journal_id' => $journal->id, + 'amount' => $amount + ] + + ); + if (!is_null($budget)) { + $journal->budgets()->save($budget); + } + if (!is_null($category)) { + $journal->categories()->save($category); + } + + return $journal; + } +} \ No newline at end of file diff --git a/app/filters.php b/app/filters.php index 5b4cf13296..a55467f0e3 100644 --- a/app/filters.php +++ b/app/filters.php @@ -4,14 +4,14 @@ App::before( function ($request) { + $reminders = []; if (Auth::check()) { - /** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */ - $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); - $toolkit->getDateRange(); - $toolkit->checkImportJobs(); - Event::fire('recurring.verify'); + Filter::setSessionDateRange(); + Reminders::updateReminders(); + $reminders = Reminders::getReminders(); } + View::share('reminders', $reminders); } ); @@ -68,6 +68,7 @@ Route::filter( if (Auth::check()) { return Redirect::to('/'); } + return null; } ); diff --git a/app/lang/en/pagination.php b/app/lang/en/pagination.php index 6b99ef5fd8..88e22db661 100644 --- a/app/lang/en/pagination.php +++ b/app/lang/en/pagination.php @@ -1,6 +1,6 @@ 'Next »', -); +]; diff --git a/app/lang/en/reminders.php b/app/lang/en/reminders.php index e42148e9fb..a416c65cb5 100644 --- a/app/lang/en/reminders.php +++ b/app/lang/en/reminders.php @@ -1,24 +1,24 @@ "Passwords must be at least six characters and match the confirmation.", + "password" => "Passwords must be at least six characters and match the confirmation.", - "user" => "We can't find a user with that e-mail address.", + "user" => "We can't find a user with that e-mail address.", - "token" => "This password reset token is invalid.", + "token" => "This password reset token is invalid.", - "sent" => "Password reminder sent!", + "sent" => "Password reminder sent!", -); +]; diff --git a/app/lang/en/validation.php b/app/lang/en/validation.php index 4c17703856..e6ee8b9253 100644 --- a/app/lang/en/validation.php +++ b/app/lang/en/validation.php @@ -21,12 +21,12 @@ return [ "alpha_num" => "The :attribute may only contain letters and numbers.", "array" => "The :attribute must be an array.", "before" => "The :attribute must be a date before :date.", - "between" => array( + "between" => [ "numeric" => "The :attribute must be between :min and :max.", "file" => "The :attribute must be between :min and :max kilobytes.", "string" => "The :attribute must be between :min and :max characters.", "array" => "The :attribute must have between :min and :max items.", - ), + ], "confirmed" => "The :attribute confirmation does not match.", "date" => "The :attribute is not a valid date.", "date_format" => "The :attribute does not match the format :format.", @@ -39,19 +39,19 @@ return [ "in" => "The selected :attribute is invalid.", "integer" => "The :attribute must be an integer.", "ip" => "The :attribute must be a valid IP address.", - "max" => array( + "max" => [ "numeric" => "The :attribute may not be greater than :max.", "file" => "The :attribute may not be greater than :max kilobytes.", "string" => "The :attribute may not be greater than :max characters.", "array" => "The :attribute may not have more than :max items.", - ), + ], "mimes" => "The :attribute must be a file of type: :values.", - "min" => array( + "min" => [ "numeric" => "The :attribute must be at least :min.", "file" => "The :attribute must be at least :min kilobytes.", "string" => "The :attribute must be at least :min characters.", "array" => "The :attribute must have at least :min items.", - ), + ], "not_in" => "The selected :attribute is invalid.", "numeric" => "The :attribute must be a number.", "regex" => "The :attribute format is invalid.", @@ -62,12 +62,12 @@ return [ "required_without" => "The :attribute field is required when :values is not present.", "required_without_all" => "The :attribute field is required when none of :values are present.", "same" => "The :attribute and :other must match.", - "size" => array( + "size" => [ "numeric" => "The :attribute must be :size.", "file" => "The :attribute must be :size kilobytes.", "string" => "The :attribute must be :size characters.", "array" => "The :attribute must contain :size items.", - ), + ], "unique" => "The :attribute has already been taken.", "url" => "The :attribute format is invalid.", @@ -82,11 +82,11 @@ return [ | */ - 'custom' => array( - 'attribute-name' => array( + 'custom' => [ + 'attribute-name' => [ 'rule-name' => 'custom-message', - ), - ), + ], + ], 'alphabasic' => 'The :attribute field must consist of basic alphanumeric characters.', /* @@ -100,6 +100,6 @@ return [ | */ - 'attributes' => array(), + 'attributes' => [], ]; diff --git a/app/lib/Firefly/Helper/Toolkit/Toolkit.php b/app/lib/Firefly/Helper/Toolkit/Toolkit.php index 51941a2508..2e9dbeb725 100644 --- a/app/lib/Firefly/Helper/Toolkit/Toolkit.php +++ b/app/lib/Firefly/Helper/Toolkit/Toolkit.php @@ -107,9 +107,6 @@ class Toolkit implements ToolkitInterface } } - /** - * @return mixed - */ protected function _getRange() { if (!is_null(\Session::get('range'))) { diff --git a/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php b/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php index 1d234a5dd9..2bbf39cbfe 100644 --- a/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php +++ b/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php @@ -68,7 +68,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface /** * - * TODO + * * Also delete: initial balance, initial balance account, and transactions */ diff --git a/app/lib/Firefly/Trigger/Budgets/EloquentBudgetTrigger.php b/app/lib/Firefly/Trigger/Budgets/EloquentBudgetTrigger.php index 54a389c2df..696e039b61 100644 --- a/app/lib/Firefly/Trigger/Budgets/EloquentBudgetTrigger.php +++ b/app/lib/Firefly/Trigger/Budgets/EloquentBudgetTrigger.php @@ -46,9 +46,9 @@ class EloquentBudgetTrigger */ public function subscribe(Dispatcher $events) { - $events->listen('budgets.destroy', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@destroy'); - $events->listen('budgets.store', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@store'); - $events->listen('budgets.update', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@update'); +// $events->listen('budgets.destroy', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@destroy'); +// $events->listen('budgets.store', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@store'); +// $events->listen('budgets.update', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@update'); } diff --git a/app/lib/Firefly/Trigger/Journals/EloquentJournalTrigger.php b/app/lib/Firefly/Trigger/Journals/EloquentJournalTrigger.php index b2cdbf9bde..cbb5196fae 100644 --- a/app/lib/Firefly/Trigger/Journals/EloquentJournalTrigger.php +++ b/app/lib/Firefly/Trigger/Journals/EloquentJournalTrigger.php @@ -45,8 +45,8 @@ class EloquentJournalTrigger */ public function subscribe(Dispatcher $events) { - $events->listen('journals.store', 'Firefly\Trigger\Journals\EloquentJournalTrigger@store'); - $events->listen('journals.update', 'Firefly\Trigger\Journals\EloquentJournalTrigger@update'); +// $events->listen('journals.store', 'Firefly\Trigger\Journals\EloquentJournalTrigger@store'); +// $events->listen('journals.update', 'Firefly\Trigger\Journals\EloquentJournalTrigger@update'); } diff --git a/app/lib/Firefly/Trigger/Limits/EloquentLimitTrigger.php b/app/lib/Firefly/Trigger/Limits/EloquentLimitTrigger.php index 67f1e4dd03..2633684c14 100644 --- a/app/lib/Firefly/Trigger/Limits/EloquentLimitTrigger.php +++ b/app/lib/Firefly/Trigger/Limits/EloquentLimitTrigger.php @@ -107,11 +107,11 @@ class EloquentLimitTrigger public function subscribe(Dispatcher $events) { //$events->listen('budgets.change', 'Firefly\Trigger\Limits\EloquentLimitTrigger@updateLimitRepetitions'); - $events->listen('limits.destroy', 'Firefly\Trigger\Limits\EloquentLimitTrigger@destroy'); - $events->listen('limits.store', 'Firefly\Trigger\Limits\EloquentLimitTrigger@store'); - $events->listen('limits.update', 'Firefly\Trigger\Limits\EloquentLimitTrigger@update'); - $events->listen('limits.check', 'Firefly\Trigger\Limits\EloquentLimitTrigger@checkRepeatingLimits'); - $events->listen('limits.repetition', 'Firefly\Trigger\Limits\EloquentLimitTrigger@madeRepetition'); +// $events->listen('limits.destroy', 'Firefly\Trigger\Limits\EloquentLimitTrigger@destroy'); +// $events->listen('limits.store', 'Firefly\Trigger\Limits\EloquentLimitTrigger@store'); +// $events->listen('limits.update', 'Firefly\Trigger\Limits\EloquentLimitTrigger@update'); +// $events->listen('limits.check', 'Firefly\Trigger\Limits\EloquentLimitTrigger@checkRepeatingLimits'); +// $events->listen('limits.repetition', 'Firefly\Trigger\Limits\EloquentLimitTrigger@madeRepetition'); //\Event::fire('limits.repetition', [$repetition]); } diff --git a/app/lib/Firefly/Trigger/Piggybanks/EloquentPiggybankTrigger.php b/app/lib/Firefly/Trigger/Piggybanks/EloquentPiggybankTrigger.php index 09e3943099..5a51efe653 100644 --- a/app/lib/Firefly/Trigger/Piggybanks/EloquentPiggybankTrigger.php +++ b/app/lib/Firefly/Trigger/Piggybanks/EloquentPiggybankTrigger.php @@ -107,16 +107,6 @@ class EloquentPiggybankTrigger return true; } - /** - * @param \Piggybank $piggyBank - * - * @return bool - */ - public function destroy(\Piggybank $piggyBank) - { - return true; - } - /** * @param \Piggybank $piggyBank * @param $amount @@ -175,26 +165,13 @@ class EloquentPiggybankTrigger */ public function subscribe(Dispatcher $events) { - $events->listen('piggybanks.destroy', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@destroy'); - $events->listen( - 'piggybanks.modifyAmountAdd', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountAdd' - ); - $events->listen( - 'piggybanks.modifyAmountRemove', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountRemove' - ); - $events->listen('piggybanks.store', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@store'); - $events->listen('piggybanks.update', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@update'); - $events->listen( - 'piggybanks.createRelatedTransfer', - 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createRelatedTransfer' - ); - $events->listen( - 'piggybanks.updateRelatedTransfer', - 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@updateRelatedTransfer' - ); - $events->listen( - 'piggybanks.storepiggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies' - ); +// $events->listen('piggybanks.modifyAmountAdd', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountAdd'); +// $events->listen('piggybanks.modifyAmountRemove', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountRemove'); +// $events->listen('piggybanks.store', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@store'); +// $events->listen('piggybanks.update', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@update'); +// $events->listen('piggybanks.createRelatedTransfer', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createRelatedTransfer'); +// $events->listen('piggybanks.updateRelatedTransfer', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@updateRelatedTransfer'); +// $events->listen('piggybanks.storepiggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies'); } diff --git a/app/lib/Firefly/Trigger/Recurring/EloquentRecurringTrigger.php b/app/lib/Firefly/Trigger/Recurring/EloquentRecurringTrigger.php index 1faface9c0..b29167bb48 100644 --- a/app/lib/Firefly/Trigger/Recurring/EloquentRecurringTrigger.php +++ b/app/lib/Firefly/Trigger/Recurring/EloquentRecurringTrigger.php @@ -99,7 +99,7 @@ class EloquentRecurringTrigger */ public function subscribe(Dispatcher $events) { - $events->listen('recurring.rescan', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@rescan'); + //$events->listen('recurring.rescan', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@rescan'); } /** diff --git a/app/lib/FireflyIII/Collection/PiggybankPart.php b/app/lib/FireflyIII/Collection/PiggybankPart.php new file mode 100644 index 0000000000..faf28173b2 --- /dev/null +++ b/app/lib/FireflyIII/Collection/PiggybankPart.php @@ -0,0 +1,167 @@ +reminder; + } + + /** + * @param \Reminder $reminder + */ + public function setReminder($reminder) + { + $this->reminder = $reminder; + } + + /** + * @return \PiggybankRepetition + */ + public function getRepetition() + { + return $this->repetition; + } + + /** + * @param \PiggybankRepetition $repetition + */ + public function setRepetition($repetition) + { + $this->repetition = $repetition; + } + + /** + * @return Carbon + */ + public function getStartdate() + { + return $this->startdate; + } + + /** + * @param Carbon $startdate + */ + public function setStartdate($startdate) + { + $this->startdate = $startdate; + } + + /** + * @return Carbon + */ + public function getTargetdate() + { + return $this->targetdate; + } + + /** + * @param Carbon $targetdate + */ + public function setTargetdate($targetdate) + { + $this->targetdate = $targetdate; + } + + public function hasReminder() + { + return !is_null($this->reminder); + } + + public function percentage() + { + if ($this->getCurrentamount() < $this->getCumulativeAmount()) { + $pct = 0; + // calculate halway point? + if ($this->getCumulativeAmount() - $this->getCurrentamount() < $this->getAmountPerBar()) { + $left = $this->getCurrentamount() % $this->getAmountPerBar(); + $pct = round($left / $this->getAmountPerBar() * 100); + } + + return $pct; + } else { + return 100; + } + } + + /** + * @return float + */ + public function getCurrentamount() + { + return $this->currentamount; + } + + /** + * @param float $currentamount + */ + public function setCurrentamount($currentamount) + { + $this->currentamount = $currentamount; + } + + /** + * @return float + */ + public function getAmountPerBar() + { + return $this->amountPerBar; + } + + /** + * @param float $amountPerBar + */ + public function setAmountPerBar($amountPerBar) + { + $this->amountPerBar = $amountPerBar; + } + + /** + * @return float + */ + public function getCumulativeAmount() + { + return $this->cumulativeAmount; + } + + /** + * @param float $cumulativeAmount + */ + public function setCumulativeAmount($cumulativeAmount) + { + $this->cumulativeAmount = $cumulativeAmount; + } + + + + +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Account.php b/app/lib/FireflyIII/Database/Account.php index 60726af53c..6dcd5ae0b3 100644 --- a/app/lib/FireflyIII/Database/Account.php +++ b/app/lib/FireflyIII/Database/Account.php @@ -6,9 +6,9 @@ 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 FireflyIII\Exception\NotImplementedException; use Illuminate\Support\Collection; +use Illuminate\Support\MessageBag; /** * Class Account @@ -27,122 +27,14 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface $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 + * @return int */ - public function getAccountsByType(array $types, array $parameters = []) + public function countAccountsByType(array $types) { - /* - * 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 $this->getUser()->accounts()->accountTypeIn($types)->count(); } /** @@ -161,58 +53,6 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface 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. * @@ -224,27 +64,104 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface } /** - * Get all revenue accounts. + * @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 + * + * @return Collection + */ + public function getAccountsByType(array $types) + { + /* + * Basic query: + */ + $query = $this->getUser()->accounts()->accountTypeIn($types)->withMeta(); + + + /* + * 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. + */ + $query->where( + function ($q) { + $q->where('transaction_journals.date', '<=', Carbon::now()->format('Y-m-d')); + $q->orWhereNull('transaction_journals.date'); + } + ); + + $query->groupBy('accounts.id'); + + /* + * If present, process parameters for sorting: + */ + $query->orderBy('name', 'ASC'); + + return $query->get(['accounts.*', \DB::Raw('SUM(`transactions`.`amount`) as `balance`')]); + } + + /** + * Get all asset accounts. Optional JSON based parameters. * * @param array $parameters * * @return Collection */ - public function getRevenueAccounts(array $parameters = []) + public function getAssetAccounts() { - return $this->getAccountsByType(['Revenue account'], $parameters); + $list = $this->getAccountsByType(['Default account', 'Asset account']); + $list->each( + function (\Account $account) { + /** @var \AccountMeta $entry */ + foreach ($account->accountmeta as $entry) { + if ($entry->name == 'accountRole') { + $account->accountRole = \Config::get('firefly.accountRoles.' . $entry->data); + } + } + if (!isset($account->accountRole)) { + $account->accountRole = 'Default expense account'; + } + } + ); + + return $list; + } /** - * @param Ardent $model - * - * @return bool + * @return Collection */ - public function destroy(Ardent $model) + public function getExpenseAccounts() { - $model->delete(); - return true; + return $this->getAccountsByType(['Expense account', 'Beneficiary account']); + } + /** + * Get all revenue accounts. + * + * @return Collection + */ + public function getRevenueAccounts() + { + return $this->getAccountsByType(['Revenue account']); } /** @@ -254,27 +171,208 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface */ 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.*']); + 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 \Account $account + * @param array $data * - * @param Ardent $model - * - * @return array + * @return bool */ - public function validateObject(Ardent $model) + public function storeInitialBalance(\Account $account, array $data) { - die('No impl'); + $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; + } + + } + + /** + * @param \Eloquent $model + * + * @return bool + */ + public function destroy(\Eloquent $model) + { + + // delete journals: + $journals = \TransactionJournal::whereIn( + 'id', function ($query) use ($model) { + $query->select('transaction_journal_id') + ->from('transactions')->whereIn( + 'account_id', function ($query) use ($model) { + $query + ->select('id') + ->from('accounts') + ->where( + function ($q) use ($model) { + $q->where('id', $model->id); + $q->orWhere( + function ($q) use ($model) { + $q->where('accounts.name', 'LIKE', '%' . $model->name . '%'); + // TODO magic number! + $q->where('accounts.account_type_id', 3); + $q->where('accounts.active', 0); + } + ); + } + )->where('accounts.user_id', $this->getUser()->id); + } + )->get(); + } + )->delete(); + + /* + * Trigger deletion: + */ + \Event::fire('account.destroy', [$model]); + + // delete accounts: + \Account::where( + function ($q) use ($model) { + $q->where('id', $model->id); + $q->orWhere( + function ($q) use ($model) { + $q->where('accounts.name', 'LIKE', '%' . $model->name . '%'); + // TODO magic number! + $q->where('accounts.account_type_id', 3); + $q->where('accounts.active', 0); + } + ); + })->delete(); + + return true; + + } + + /** + * @param array $data + * + * @return \Eloquent + */ + 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, ['_token', 'what']); + $account = new \Account($data); + if (!$account->isValid()) { + var_dump($account->getErrors()->all()); + exit; + } + $account->save(); + if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) { + $this->storeInitialBalance($account, $data); + } + + // TODO this needs cleaning up and thinking over. + switch ($account->accountType->type) { + case 'Asset account': + case 'Default account': + $account->updateMeta('accountRole', $data['account_role']); + break; + } + + + /* Tell transaction journal to store a new one.*/ + \Event::fire('account.store', [$account]); + + + return $account; + + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + $model->name = $data['name']; + $model->active = isset($data['active']) ? intval($data['active']) : 0; + + // TODO this needs cleaning up and thinking over. + switch ($model->accountType->type) { + case 'Asset account': + case 'Default account': + $model->updateMeta('accountRole', $data['account_role']); + break; + } + + $model->save(); + + if (isset($data['openingbalance']) && isset($data['openingbalancedate']) && strlen($data['openingbalancedate']) > 0) { + $openingBalance = $this->openingBalanceTransaction($model); + // TODO this needs cleaning up and thinking over. + if (is_null($openingBalance)) { + $this->storeInitialBalance($model, $data); + } else { + $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(); + } + } + } + \Event::fire('account.update', [$model]); + + return true; } /** @@ -309,6 +407,12 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface $errors->merge($errors); } + if (isset($model['account_role']) && !in_array($model['account_role'], array_keys(\Config::get('firefly.accountRoles')))) { + $errors->add('account_role', 'Invalid account role'); + } else { + $successes->add('account_role', 'OK'); + } + /* * type validation. */ @@ -342,107 +446,20 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface if (!$errors->has('openingbalancedate')) { $successes->add('openingbalancedate', 'OK'); } - return [ - 'errors' => $errors, - 'warnings' => $warnings, - 'successes' => $successes - ]; + + return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; } /** - * @param array $data + * Returns an object with id $id. * - * @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 + * @param int $id * - * @return bool + * @return \Eloquent */ - public function storeInitialBalance(\Account $account, array $data) + public function find($id) { - $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; + return $this->getUser()->accounts()->find($id); } /** @@ -455,6 +472,18 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface public function findByWhat($what) { // TODO: Implement findByWhat() method. + throw new NotImplementedException; + } + + /** + * Returns all objects. + * + * @return Collection + */ + public function get() + { + // TODO: Implement get() method. + throw new NotImplementedException; } /** @@ -466,4 +495,103 @@ class Account implements CUD, CommonDatabaseCalls, AccountInterface { return $this->getUser()->accounts()->whereIn('id', $ids)->get(); } + + public function firstExpenseAccountOrCreate($name) + { + /** @var \FireflyIII\Database\AccountType $accountTypeRepos */ + $accountTypeRepos = \App::make('FireflyIII\Database\AccountType'); + + $accountType = $accountTypeRepos->findByWhat('expense'); + + // if name is "", find cash account: + if (strlen($name) == 0) { + $cashAccountType = $accountTypeRepos->findByWhat('cash'); + + // find or create cash account: + return \Account::firstOrCreate( + ['name' => 'Cash account', 'account_type_id' => $cashAccountType->id, 'active' => 1, 'user_id' => $this->getUser()->id,] + ); + } + + $data = ['user_id' => $this->getUser()->id, 'account_type_id' => $accountType->id, 'name' => $name, 'active' => 1]; + + return \Account::firstOrCreate($data); + + } + + public function firstRevenueAccountOrCreate($name) + { + /** @var \FireflyIII\Database\AccountType $accountTypeRepos */ + $accountTypeRepos = \App::make('FireflyIII\Database\AccountType'); + + $accountType = $accountTypeRepos->findByWhat('revenue'); + + $data = ['user_id' => $this->getUser()->id, 'account_type_id' => $accountType->id, 'name' => $name, 'active' => 1]; + + return \Account::firstOrCreate($data); + + } + + public function getAllTransactionJournals(\Account $account, $limit = 50) + { + $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0; + $set = $this->getUser()->transactionJournals()->withRelevantData()->leftJoin( + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' + )->where('transactions.account_id', $account->id)->take($limit)->offset($offset)->orderBy('date', 'DESC')->get( + ['transaction_journals.*'] + ); + $count = $this->getUser()->transactionJournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->orderBy('date', 'DESC')->where('transactions.account_id', $account->id)->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + + + } + + public function getTransactionJournals(\Account $account, $limit = 50) + { + $start = \Session::get('start'); + $end = \Session::get('end'); + $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0; + $set = $this->getUser()->transactionJournals()->withRelevantData()->leftJoin( + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' + )->where('transactions.account_id', $account->id)->take($limit)->offset($offset)->before($end)->after($start)->orderBy('date', 'DESC')->get( + ['transaction_journals.*'] + ); + $count = $this->getUser()->transactionJournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->before($end)->after($start)->orderBy('date', 'DESC')->where('transactions.account_id', $account->id)->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + + + } + + /** + * @param \Account $account + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Pagination\Paginator + */ + public function getTransactionJournalsInRange(\Account $account, Carbon $start, Carbon $end) + { + $set = $this->getUser()->transactionJournals()->transactionTypes(['Withdrawal'])->withRelevantData()->leftJoin( + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' + )->where('transactions.account_id', $account->id)->before($end)->after($start)->orderBy('date', 'DESC')->get( + ['transaction_journals.*'] + ); + + return $set; + + } + + } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/AccountType.php b/app/lib/FireflyIII/Database/AccountType.php index 8a4e13f1dc..6a7131d2b1 100644 --- a/app/lib/FireflyIII/Database/AccountType.php +++ b/app/lib/FireflyIII/Database/AccountType.php @@ -2,21 +2,82 @@ namespace FireflyIII\Database; -use Firefly\Exception\FireflyException; -use Illuminate\Support\Collection; -use LaravelBook\Ardent\Ardent; +use FireflyIII\Exception\FireflyException; use FireflyIII\Database\Ifaces\AccountTypeInterface; use FireflyIII\Database\Ifaces\CommonDatabaseCalls; use FireflyIII\Database\Ifaces\CUD; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; /** * Class AccountType * * @package FireflyIII\Database */ -class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls +class AccountType implements CUD, CommonDatabaseCalls { + /** + * @param \Eloquent $model + * + * @return bool + */ + public function destroy(\Eloquent $model) + { + // TODO: Implement destroy() method. + throw new NotImplementedException; + } + + /** + * @param array $data + * + * @return \Eloquent + */ + public function store(array $data) + { + // TODO: Implement store() method. + throw new NotImplementedException; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + // TODO: Implement update() method. + throw new NotImplementedException; + } + + /** + * 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. + throw new NotImplementedException; + } + + /** + * Returns an object with id $id. + * + * @param int $id + * + * @return \Eloquent + */ + public function find($id) + { + // TODO: Implement find() method. + throw new NotImplementedException; + } + /** * Finds an account type using one of the "$what"'s: expense, asset, revenue * @@ -37,6 +98,9 @@ class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls case 'revenue': return \AccountType::whereType('Revenue account')->first(); break; + case 'cash': + return \AccountType::whereType('Cash account')->first(); + break; case 'initial': return \AccountType::whereType('Initial balance account')->first(); break; @@ -45,65 +109,6 @@ class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls 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. } /** @@ -114,6 +119,7 @@ class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls public function get() { // TODO: Implement get() method. + throw new NotImplementedException; } /** @@ -124,16 +130,6 @@ class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls 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. + throw new NotImplementedException; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Budget.php b/app/lib/FireflyIII/Database/Budget.php index ba8d22fcf4..f9563d8066 100644 --- a/app/lib/FireflyIII/Database/Budget.php +++ b/app/lib/FireflyIII/Database/Budget.php @@ -2,13 +2,13 @@ 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\BudgetInterface; use FireflyIII\Database\Ifaces\CommonDatabaseCalls; use FireflyIII\Database\Ifaces\CUD; -use FireflyIII\Database\Ifaces\BudgetInterface; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; +use Illuminate\Support\MessageBag; + /** * Class Budget * @@ -26,6 +26,178 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface $this->setUser(\Auth::user()); } + /** + * @param \Eloquent $model + * + * @return bool + */ + public function destroy(\Eloquent $model) + { + $model->delete(); + + return true; + } + + /** + * @param array $data + * + * @return \Eloquent + */ + public function store(array $data) + { + $data['user_id'] = $this->getUser()->id; + + $budget = new \Budget($data); + $budget->class = 'Budget'; + + if (!$budget->isValid()) { + var_dump($budget->getErrors()->all()); + exit; + } + $budget->save(); + + return $budget; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + $model->name = $data['name']; + if (!$model->isValid()) { + var_dump($model->getErrors()->all()); + exit; + } + + + $model->save(); + + return true; + } + + /** + * 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]; + } + + /** + * Returns an object with id $id. + * + * @param int $id + * + * @return \Eloquent + */ + public function find($id) + { + return $this->getUser()->budgets()->find($id); + } + + /** + * 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. + throw new NotImplementedException; + } + + /** + * Returns all objects. + * + * @return Collection + */ + public function get() + { + $budgets = $this->getUser()->budgets()->get(); + + return $budgets; + } + + /** + * @param array $ids + * + * @return Collection + */ + public function getByIds(array $ids) + { + // TODO: Implement getByIds() method. + throw new NotImplementedException; + } + + public function getTransactionJournals(\Budget $budget, $limit = 50) + { + $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0; + $set = $budget->transactionJournals()->withRelevantData()->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']); + $count = $budget->transactionJournals()->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + + } + + public function getTransactionJournalsInRepetition(\Budget $budget, \LimitRepetition $repetition, $limit = 50) + { + $start = $repetition->startdate; + $end = $repetition->enddate; + + $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0; + $set = $budget->transactionJournals()->withRelevantData()->before($end)->after($start)->take($limit)->offset($offset)->orderBy('date', 'DESC')->get( + ['transaction_journals.*'] + ); + $count = $budget->transactionJournals()->before($end)->after($start)->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + } + /** * @param \Budget $budget * @param Carbon $date @@ -42,152 +214,6 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface )->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 @@ -197,38 +223,44 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface public function transactionsWithoutBudgetInDateRange(Carbon $start, Carbon $end) { // Add expenses that have no budget: - return \Auth::user()->transactionjournals()->whereNotIn( + return $this->getUser()->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'); + $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 + * @param \Budget $budget + * @param Carbon $date * - * @return bool + * @return float */ - public function update(Ardent $model, array $data) + public function spentInMonth(\Budget $budget, Carbon $date) { - $model->name = $data['name']; - if (!$model->validate()) { - var_dump($model->errors()->all()); - exit; - } + $end = clone $date; + $date->startOfMonth(); + $end->endOfMonth(); + $sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1; + return $sum; + } - $model->save(); + /** + * @param \Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return float + */ + public function spentInPeriod(\Budget $budget, Carbon $start, Carbon $end) + { + $sum = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1; - return true; + return $sum; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Category.php b/app/lib/FireflyIII/Database/Category.php index 47349ac021..649dfc47ea 100644 --- a/app/lib/FireflyIII/Database/Category.php +++ b/app/lib/FireflyIII/Database/Category.php @@ -2,19 +2,20 @@ namespace FireflyIII\Database; use Carbon\Carbon; -use Illuminate\Support\MessageBag; -use LaravelBook\Ardent\Ardent; -use Illuminate\Support\Collection; +use FireflyIII\Database\Ifaces\CategoryInterface; use FireflyIII\Database\Ifaces\CommonDatabaseCalls; use FireflyIII\Database\Ifaces\CUD; -use FireflyIII\Database\Ifaces\CategoryInterface; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; +use Illuminate\Support\MessageBag; + /** * Class Category * * @package FireflyIII\Database */ -class Category implements CUD, CommonDatabaseCalls, CategoryInterface +class Category implements CUD, CommonDatabaseCalls { use SwitchUser; @@ -27,26 +28,55 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface } /** - * @param Ardent $model + * @param \Eloquent $model * * @return bool */ - public function destroy(Ardent $model) + public function destroy(\Eloquent $model) { - // TODO: Implement destroy() method. + $model->delete(); + + return true; } /** - * Validates a model. Returns an array containing MessageBags - * errors/warnings/successes. + * @param array $data * - * @param Ardent $model - * - * @return array + * @return \Eloquent */ - public function validateObject(Ardent $model) + public function store(array $data) { - // TODO: Implement validateObject() method. + $category = new \Category; + $category->name = $data['name']; + $category->class = 'Category'; + $category->user()->associate($this->getUser()); + if (!$category->isValid()) { + var_dump($category->getErrors()); + exit(); + } + $category->save(); + + return $category; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + $model->name = $data['name']; + if (!$model->isValid()) { + var_dump($model->getErrors()->all()); + exit; + } + + + $model->save(); + + return true; } /** @@ -59,17 +89,33 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface */ public function validate(array $model) { - // TODO: Implement validate() method. - } + $warnings = new MessageBag; + $successes = new MessageBag; + $errors = new MessageBag; - /** - * @param array $data - * - * @return Ardent - */ - public function store(array $data) - { - // TODO: Implement store() method. + 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->getErrors()); + } + + + if (!$errors->has('name')) { + $successes->add('name', 'OK'); + } + + return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; } /** @@ -77,31 +123,12 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface * * @param int $id * - * @return Ardent + * @return \Eloquent */ 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. + throw new NotImplementedException; } /** @@ -114,16 +141,73 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface public function findByWhat($what) { // TODO: Implement findByWhat() method. + throw new NotImplementedException; } /** - * @param Ardent $model - * @param array $data + * Returns all objects. * - * @return bool + * @return Collection */ - public function update(Ardent $model, array $data) + public function get() { - // TODO: Implement update() method. + return $this->getUser()->categories()->orderBy('name', 'ASC')->get(); + } + + /** + * @param array $ids + * + * @return Collection + */ + public function getByIds(array $ids) + { + // TODO: Implement getByIds() method. + throw new NotImplementedException; + } + + public function firstOrCreate($name) + { + return \Category::firstOrCreate(['user_id' => $this->getUser()->id, 'name' => $name]); + } + + public function getTransactionJournals(\Category $category, $limit = 50) + { + $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0; + $set = $category->transactionJournals()->withRelevantData()->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']); + $count = $category->transactionJournals()->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + + } + + /** + * @param \Category $budget + * @param Carbon $date + * + * @return null + */ + public function repetitionOnStartingOnDate(\Category $category, Carbon $date) + { + return null; + } + + /** + * @param \Category $category + * @param Carbon $date + * + * @return float + */ + public function spentInMonth(\Category $category, Carbon $date) + { + $end = clone $date; + $date->startOfMonth(); + $end->endOfMonth(); + $sum = floatval($category->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1; + + return $sum; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Ifaces/AccountInterface.php b/app/lib/FireflyIII/Database/Ifaces/AccountInterface.php index a24fa07421..614c476556 100644 --- a/app/lib/FireflyIII/Database/Ifaces/AccountInterface.php +++ b/app/lib/FireflyIII/Database/Ifaces/AccountInterface.php @@ -13,13 +13,13 @@ interface AccountInterface { /** - * Get all asset accounts. The parameters are optional and are provided by the DataTables plugin. + * Counts the number of accounts found with the included types. * - * @param array $parameters + * @param array $types * - * @return Collection + * @return int */ - public function getAssetAccounts(array $parameters = []); + public function countAccountsByType(array $types); /** * Counts the number of total asset accounts. Useful for DataTables. @@ -42,12 +42,6 @@ interface AccountInterface */ public function countRevenueAccounts(); - /** - * @param array $parameters - * - * @return Collection - */ - /** * @param \Account $account * @@ -55,42 +49,33 @@ interface AccountInterface */ 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 = []); + public function getAccountsByType(array $types); /** - * Counts the number of accounts found with the included types. - * - * @param array $types - * - * @return int - */ - public function countAccountsByType(array $types); - - /** - * Get all default accounts. + * Get all asset accounts. The parameters are optional and are provided by the DataTables plugin. * * @return Collection */ - public function getDefaultAccounts(); + public function getAssetAccounts(); + + /** + * @return Collection + */ + public function getExpenseAccounts(); + + /** + * Get all revenue accounts. + * + * @return Collection + */ + public function getRevenueAccounts(); /** * @param \Account $account @@ -101,7 +86,7 @@ interface AccountInterface /** * @param \Account $account - * @param array $data + * @param array $data * * @return bool */ diff --git a/app/lib/FireflyIII/Database/Ifaces/AccountTypeInterface.php b/app/lib/FireflyIII/Database/Ifaces/AccountTypeInterface.php deleted file mode 100644 index d06621d702..0000000000 --- a/app/lib/FireflyIII/Database/Ifaces/AccountTypeInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -balance(); - /** @var \Piggybank $p */ - foreach ($account->piggybanks()->get() as $p) { - $balance -= $p->currentRelevantRep()->currentamount; - } - - return $balance; - - } - /** * */ @@ -45,26 +29,75 @@ class Piggybank implements CUD, CommonDatabaseCalls, PiggybankInterface } /** - * @param Ardent $model + * @param \Eloquent $model * * @return bool */ - public function destroy(Ardent $model) + public function destroy(\Eloquent $model) { $model->delete(); } /** - * Validates a model. Returns an array containing MessageBags - * errors/warnings/successes. + * @param array $data * - * @param Ardent $model - * - * @return array + * @return \Eloquent */ - public function validateObject(Ardent $model) + public function store(array $data) { - // TODO: Implement validateObject() method. + $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; + + if ($data['remind_me'] == 0) { + $data['reminder'] = null; + } + + + $piggybank = new \Piggybank($data); + if (!$piggybank->isValid()) { + var_dump($piggybank->getErrors()->all()); + exit; + } + $piggybank->save(); + + return $piggybank; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $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; + $model->reminder = isset($data['reminder']) ? $data['reminder'] : 'month'; + + if ($model->remind_me == 0) { + $model->reminder = null; + } + + if (!$model->isValid()) { + var_dump($model->getErrors()); + exit(); + } + + $model->save(); + + return true; } /** @@ -105,7 +138,7 @@ class Piggybank implements CUD, CommonDatabaseCalls, PiggybankInterface try { new Carbon($model['targetdate']); } catch (\Exception $e) { - $errors->add('date', 'Invalid date.'); + $errors->add('targetdate', 'Invalid date.'); } } if (floatval($model['targetamount']) < 0.01) { @@ -147,36 +180,7 @@ class Piggybank implements CUD, CommonDatabaseCalls, PiggybankInterface } } - 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(); + return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; } /** @@ -184,11 +188,26 @@ class Piggybank implements CUD, CommonDatabaseCalls, PiggybankInterface * * @param int $id * - * @return Ardent + * @return \Eloquent */ public function find($id) { - // TODO: Implement find() method. + return \Piggybank:: + leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where('piggybanks.id', '=', $id)->where('accounts.user_id', $this->getUser()->id) + ->first(['piggybanks.*']); + } + + /** + * 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. + throw new NotImplementedException; } /** @@ -209,42 +228,35 @@ class Piggybank implements CUD, CommonDatabaseCalls, PiggybankInterface public function getByIds(array $ids) { // TODO: Implement getByIds() method. + throw new NotImplementedException; } - /** - * Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc. - * - * @param $what - * - * @return \AccountType|null - */ - public function findByWhat($what) + public function findRepetitionByDate(\Piggybank $piggybank, Carbon $date) { - // 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(); + $reps = $piggybank->piggybankrepetitions()->get(); + if ($reps->count() == 1) { + return $reps->first(); } - $model->save(); - return true; + if ($reps->count() == 0) { + throw new FireflyException('Should always find a piggy bank repetition.'); + } + throw new NotImplementedException; + } + + /** + * @param \Account $account + * + * @return float + */ + public function leftOnAccount(\Account $account) + { + $balance = \Steam::balance($account); + /** @var \Piggybank $p */ + foreach ($account->piggybanks()->get() as $p) { + $balance -= $p->currentRelevantRep()->currentamount; + } + + return $balance; + } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Recurring.php b/app/lib/FireflyIII/Database/Recurring.php index b8cb6ba29f..d87a7f2b33 100644 --- a/app/lib/FireflyIII/Database/Recurring.php +++ b/app/lib/FireflyIII/Database/Recurring.php @@ -4,11 +4,14 @@ 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; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; +use Illuminate\Support\MessageBag; + +use stdObject; /** * Class Recurring @@ -28,39 +31,87 @@ class Recurring implements CUD, CommonDatabaseCalls, RecurringInterface } /** - * @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 + * @param \Eloquent $model * * @return bool */ - public function destroy(Ardent $model) + public function destroy(\Eloquent $model) { - // TODO: Implement destroy() method. + $model->delete(); + + return true; } /** - * Validates a model. Returns an array containing MessageBags - * errors/warnings/successes. + * @param array $data * - * @param Ardent $model - * - * @return array + * @return \Eloquent */ - public function validateObject(Ardent $model) + public function store(array $data) { - // TODO: Implement validateObject() method. + var_dump($data); + $recurring = new \RecurringTransaction; + $recurring->user()->associate($this->getUser()); + $recurring->name = $data['name']; + $recurring->match = $data['match']; + $recurring->amount_max = floatval($data['amount_max']); + $recurring->amount_min = floatval($data['amount_min']); + + $date = new Carbon($data['date']); + + + $recurring->active = isset($data['active']) && intval($data['active']) == 1 ? 1 : 0; + $recurring->automatch = isset($data['automatch']) && intval($data['automatch']) == 1 ? 1 : 0; + $recurring->repeat_freq = $data['repeat_freq']; + + /* + * Jump to the start of the period. + */ + $date = DateKit::startOfPeriod($date, $data['repeat_freq']); + $recurring->date = $date; + $recurring->skip = intval($data['skip']); + + if (!$recurring->isValid()) { + var_dump($recurring->getErrors()); + exit(); + } + + $recurring->save(); + + return $recurring; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + var_dump($data); + + $model->name = $data['name']; + $model->match = $data['match']; + $model->amount_max = floatval($data['amount_max']); + $model->amount_min = floatval($data['amount_min']); + + $date = new Carbon($data['date']); + + $model->date = $date; + $model->active = isset($data['active']) && intval($data['active']) == 1 ? 1 : 0; + $model->automatch = isset($data['automatch']) && intval($data['automatch']) == 1 ? 1 : 0; + $model->repeat_freq = $data['repeat_freq']; + $model->skip = intval($data['skip']); + + if (!$model->isValid()) { + var_dump($model->getErrors()); + exit(); + } + + $model->save(); + + return true; } /** @@ -73,17 +124,57 @@ class Recurring implements CUD, CommonDatabaseCalls, RecurringInterface */ public function validate(array $model) { - // TODO: Implement validate() method. - } + $warnings = new MessageBag; + $successes = new MessageBag; + $errors = new MessageBag; - /** - * @param array $data - * - * @return Ardent - */ - public function store(array $data) - { - // TODO: Implement store() method. + if (isset($model['name']) && strlen($model['name']) == 0) { + $errors->add('name', 'Name must be longer.'); + } + if (isset($model['name']) && strlen($model['name']) > 200) { + $errors->add('name', 'Name must be shorter.'); + } + + if (isset($model['match']) && strlen(trim($model['match'])) <= 2) { + $errors->add('match', 'Needs more matches.'); + } + + if (isset($model['amount_min']) && floatval($model['amount_min']) < 0.01) { + $errors->add('amount_min', 'Minimum amount must be higher.'); + } + if (isset($model['amount_max']) && floatval($model['amount_max']) < 0.02) { + $errors->add('amount_max', 'Maximum amount must be higher.'); + } + if (isset($model['amount_min']) && isset($model['amount_max']) && floatval($model['amount_min']) > floatval($model['amount_max'])) { + $errors->add('amount_max', 'Maximum amount can not be less than minimum amount.'); + $errors->add('amount_min', 'Minimum amount can not be more than maximum amount.'); + } + + if ($model['date'] != '') { + try { + new Carbon($model['date']); + } catch (\Exception $e) { + $errors->add('date', 'Invalid date.'); + } + } + + $reminders = \Config::get('firefly.budget_periods'); + if (!isset($model['repeat_freq']) || (isset($model['repeat_freq']) && !in_array($model['repeat_freq'], $reminders))) { + $errors->add('repeat_freq', 'Invalid reminder period'); + } + + if (isset($model['skip']) && intval($model['skip']) < 0) { + $errors->add('skip', 'Invalid skip.'); + } + + $set = ['name', 'match', 'amount_min', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active']; + foreach ($set as $entry) { + if (!$errors->has($entry)) { + $successes->add($entry, 'OK'); + } + } + + return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; } /** @@ -91,11 +182,25 @@ class Recurring implements CUD, CommonDatabaseCalls, RecurringInterface * * @param int $id * - * @return Ardent + * @return \Eloquent */ public function find($id) { // TODO: Implement find() method. + throw new NotImplementedException; + } + + /** + * 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. + throw new NotImplementedException; } /** @@ -116,28 +221,124 @@ class Recurring implements CUD, CommonDatabaseCalls, RecurringInterface public function getByIds(array $ids) { // TODO: Implement getByIds() method. + throw new NotImplementedException; } /** - * Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc. + * @param \RecurringTransaction $recurring + * @param Carbon $start + * @param Carbon $end * - * @param $what - * - * @return \AccountType|null + * @return \TransactionJournal|null */ - public function findByWhat($what) + public function getJournalForRecurringInRange(\RecurringTransaction $recurring, Carbon $start, Carbon $end) { - // TODO: Implement findByWhat() method. + return $this->getUser()->transactionjournals()->where('recurring_transaction_id', $recurring->id)->after($start)->before($end)->first(); + } /** - * @param Ardent $model - * @param array $data + * @param \RecurringTransaction $recurring + * @param \TransactionJournal $journal * * @return bool */ - public function update(Ardent $model, array $data) + public function scan(\RecurringTransaction $recurring, \TransactionJournal $journal) { - // TODO: Implement update() method. + /* + * Match words. + */ + $wordMatch = false; + $matches = explode(',', $recurring->match); + $description = strtolower($journal->description); + + /* + * Attach expense account to description for more narrow matching. + */ + if (count($journal->transactions) < 2) { + $transactions = $journal->transactions()->get(); + } else { + $transactions = $journal->transactions; + } + /** @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); + } + } + \Log::debug('Final description: ' . $description); + \Log::debug('Matches searched: ' . join(':', $matches)); + + $count = 0; + foreach ($matches as $word) { + if (!(strpos($description, strtolower($word)) === false)) { + $count++; + } + } + if ($count >= count($matches)) { + $wordMatch = true; + \Log::debug('word match is 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; + \Log::debug('Amount match is true!'); + } + } + + /* + * If both, update! + */ + if ($wordMatch && $amountMatch) { + $journal->recurringTransaction()->associate($recurring); + $journal->save(); + } + } + + /** + * @param \RecurringTransaction $recurring + * + * @return bool + */ + public function scanEverything(\RecurringTransaction $recurring) + { + // get all journals that (may) be relevant. + // this is usually almost all of them. + + /** @var \FireflyIII\Database\TransactionJournal $journalRepository */ + $journalRepository = \App::make('FireflyIII\Database\TransactionJournal'); + + $set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $recurring->amount_min)->where('amount', '<=', $recurring->amount_max) + ->get(['transaction_journal_id']); + $ids = []; + + /** @var \Transaction $entry */ + foreach ($set as $entry) { + $ids[] = intval($entry->transaction_journal_id); + } + if (count($ids) > 0) { + $journals = $journalRepository->getByIds($ids); + /** @var \TransactionJournal $journal */ + foreach ($journals as $journal) { + $this->scan($recurring, $journal); + } + } + + return true; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/RepeatedExpense.php b/app/lib/FireflyIII/Database/RepeatedExpense.php new file mode 100644 index 0000000000..e0294558ce --- /dev/null +++ b/app/lib/FireflyIII/Database/RepeatedExpense.php @@ -0,0 +1,480 @@ +setUser(\Auth::user()); + } + + /** + * Based on the piggy bank, the reminder-setting and + * other variables this method tries to divide the piggy bank into equal parts. Each is + * accommodated by a reminder (if everything goes to plan). + * + * @return \PiggybankRepetition + */ + public function calculateParts(\PiggybankRepetition $repetition) + { + \Log::debug('NOW in calculateParts()'); + \Log::debug('Repetition id is ' . $repetition->id); + /** @var \Piggybank $piggyBank */ + $piggyBank = $repetition->piggybank()->first(); + $bars = new Collection; + \Log::debug('connected piggy bank is: ' . $piggyBank->name . ' (#' . $piggyBank->id . ')'); + + /* + * If no reminders are set, the repetition is split in exactly one part: + */ + if (is_null($piggyBank->reminder)) { + $part = new PiggybankPart; + $part->setRepetition($repetition); + $part->setAmountPerBar(floatval($piggyBank->targetamount)); + $part->setCurrentamount($repetition->currentamount); + $part->setCumulativeAmount($piggyBank->targetamount); + $part->setStartdate(clone $repetition->startdate); + $part->setTargetdate(clone $repetition->targetdate); + $bars->push($part); + $repetition->bars = $bars; + + return $repetition; + } + $currentStart = clone $repetition->startdate; + /* + * Loop between start and target instead of counting manually. + */ + $index = 0; + //echo 'Looping!
'; + //echo $repetition->startdate . ' until ' . $repetition->targetdate . '
'; + while ($currentStart < $repetition->targetdate) { + $currentTarget = \DateKit::endOfX($currentStart, $piggyBank->reminder); + if ($currentTarget > $repetition->targetdate) { + $currentTarget = clone $repetition->targetdate; + } + + // create a part: + $part = new PiggybankPart; + $part->setRepetition($repetition); + $part->setCurrentamount($repetition->currentamount); + $part->setStartdate($currentStart); + $part->setTargetdate($currentTarget); + $bars->push($part); + //echo 'Loop #' . $index . ', from ' . $currentStart . ' until ' . $currentTarget . '
'; + + + /* + * Jump to the next period by adding a day. + */ + $currentStart = clone $currentTarget; + $currentStart->addDay();//\DateKit::addPeriod($currentTarget, $piggyBank->reminder, 0); + $index++; + + } + /* + * Loop parts again to add some + */ + $parts = $bars->count(); + $amountPerBar = floatval($piggyBank->targetamount) / $parts; + $cumulative = $amountPerBar; + /** @var PiggybankPart $bar */ + foreach ($bars as $index => $bar) { + $bar->setAmountPerBar($amountPerBar); + $bar->setCumulativeAmount($cumulative); + if($parts -1 == $index) { + $bar->setCumulativeAmount($piggyBank->targetamount); + } + + $reminder = $piggyBank->reminders() + ->where('startdate', $bar->getStartdate()->format('Y-m-d')) + ->where('enddate', $bar->getTargetdate()->format('Y-m-d')) + ->first(); + if ($reminder) { + $bar->setReminder($reminder); + } + + $cumulative += $amountPerBar; + } + + $repetition->bars = $bars; + + return $repetition; + // + // // if($parts > 12) { + // // $parts = 12; + // // $currentStart = \DateKit::startOfPeriod(Carbon::now(), $piggyBank->reminder); + // // $currentEnd = \DateKit::endOfPeriod($currentEnd, $piggyBank->reminder); + // // } + // + // for ($i = 0; $i < $parts; $i++) { + // /* + // * If it's not the first repetition, jump the start date a [period] + // * and jump the target date a [period] + // */ + // if ($i > 0) { + // $currentStart = clone $currentTarget; + // $currentStart->addDay(); + // $currentTarget = \DateKit::addPeriod($currentStart, $piggyBank->reminder, 0); + // } + // /* + // * If it's the first one, and has reminders, jump to the end of the [period] + // */ + // if ($i == 0 && !is_null($piggyBank->reminder)) { + // $currentTarget = \DateKit::endOfX($currentStart, $piggyBank->reminder); + // } + // if ($currentStart > $repetition->targetdate) { + // break; + // } + // + // + // /* + // * Jump one month ahead after the first instance: + // */ + // // if ($i > 0) { + // // $currentStart = \DateKit::addPeriod($currentStart, $piggyBank->reminder, 0); + // // /* + // // * Jump to the start of the period too: + // // */ + // // $currentStart = \DateKit::startOfPeriod($currentStart, $piggyBank->reminder); + // // + // // } + // + // + // /* + // * Move the current start to the actual start of + // * the [period] once the first iteration has passed. + // */ + // // if ($i != 0) { + // // $currentStart = \DateKit::startOfPeriod($currentStart, $piggyBank->reminder); + // // } + // // if($i == 0 && !is_null($piggyBank->reminder)) { + // // $currentEnd = \DateKit::startOfPeriod($currentStart, $piggyBank->reminder); + // // $currentEnd = \DateKit::endOfPeriod($currentEnd, $piggyBank->reminder); + // // } + // + // $part = new PiggybankPart; + // $part->setRepetition($repetition); + // $part->setAmount($currentAmount); + // $part->setAmountPerBar($amountPerBar); + // $part->setCurrentamount($repetition->currentamount); + // $part->setStartdate($currentStart); + // $part->setTargetdate($currentTarget); + // if (!is_null($piggyBank->reminder)) { + // // might be a reminder for this range? + // $reminder = $piggyBank->reminders() + // ->where('startdate', $currentStart->format('Y-m-d')) + // ->where('enddate', $currentTarget->format('Y-m-d')) + // ->first(); + // if ($reminder) { + // $part->setReminder($reminder); + // } + // + // } + // + // // if (!is_null($piggyBank->reminder)) { + // // $currentStart = \DateKit::addPeriod($currentStart, $piggyBank->reminder, 0); + // // $currentEnd = \DateKit::endOfPeriod($currentStart, $piggyBank->reminder); + // // } + // + // + // $bars->push($part); + // $currentAmount += $amountPerBar; + // } + // $repetition->bars = $bars; + // + // return $repetition; + // exit; + // + // + // $repetition->hello = 'World!'; + // + // return $repetition; + // + // $return = new Collection; + // $repetitions = $piggyBank->piggybankrepetitions()->get(); + // /** @var \PiggybankRepetition $repetition */ + // foreach ($repetitions as $repetition) { + // + // + // if (is_null($piggyBank->reminder)) { + // // simple, one part "repetition". + // $part = new PiggybankPart; + // $part->setRepetition($repetition); + // } else { + // $part = new PiggybankPart; + // } + // + // + // // end! + // $return->push($part); + // } + // + // exit; + // + // return $return; + // $piggyBank->currentRelevantRep(); // get the current relevant repetition. + // if (!is_null($piggyBank->reminder)) { + // switch ($piggyBank->reminder) { + // default: + // throw new FireflyException('Cannot handle "' . $piggyBank->reminder . '" reminders for repeated expenses'); + // break; + // case 'month': + // $start = clone $piggyBank->currentRep->startdate; + // $start->startOfMonth(); + // $end = clone $piggyBank->currentRep->targetdate; + // $end->endOfMonth(); + // $piggyBank->parts = $start->diffInMonths($end); + // unset($start, $end); + // break; + // } + // + // } else { + // $piggyBank->parts = 1; + // } + // + // // number of bars: + // $piggyBank->barCount = floor(12 / $piggyBank->parts) == 0 ? 1 : floor(12 / $piggyBank->parts); + // $amountPerBar = floatval($piggyBank->targetamount) / $piggyBank->parts; + // $currentAmount = floatval($amountPerBar); + // $bars = []; + // $currentStart = clone $piggyBank->currentRep->startdate; + // for ($i = 0; $i < $piggyBank->parts; $i++) { + // // niet elke keer een andere dinges pakken? om target te redden? + // if (!is_null($piggyBank->reminder)) { + // $currentStart = \DateKit::addPeriod($currentStart, $piggyBank->reminder, 0); + // } + // $bars[] = [ + // 'amount' => $currentAmount, + // 'date' => $currentStart + // ]; + // + // + // $currentAmount += $amountPerBar; + // } + // $piggyBank->bars = $bars; + } + + /** + * @param \Eloquent $model + * + * @return bool + */ + public function destroy(\Eloquent $model) + { + // TODO: Implement destroy() method. + throw new NotImplementedException; + } + + /** + * @param array $data + * + * @return \Eloquent + */ + 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; + $data['account_id'] = intval($data['account_id']); + + + if ($data['remind_me'] == 0) { + $data['reminder'] = null; + } + + + $repeated = new \Piggybank($data); + if (!$repeated->isValid()) { + var_dump($repeated->getErrors()->all()); + exit; + } + $repeated->save(); + + return $repeated; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + // TODO: Implement update() method. + throw new NotImplementedException; + } + + /** + * 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('targetdate', 'Invalid date.'); + } + $diff = Carbon::now()->diff(new Carbon($model['targetdate'])); + if ($diff->days > 365) { + $errors->add('targetdate', 'First target date should a a year or less from now.'); + } + } else { + $errors->add('targetdate', 'Invalid target 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'] . ')'); + } + + if (!in_array(ucfirst($model['rep_length']), \Config::get('firefly.piggybank_periods'))) { + $errors->add('rep_length', 'Invalid repeat period (' . $model['rep_length'] . ')'); + } + + // 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', 'rep_every', 'rep_times', 'rep_length', '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]; + } + + /** + * Returns an object with id $id. + * + * @param int $id + * + * @return \Eloquent + */ + public function find($id) + { + // TODO: Implement find() method. + throw new NotImplementedException; + } + + /** + * 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. + throw new NotImplementedException; + } + + /** + * Returns all objects. + * + * @return Collection + */ + public function get() + { + return $this->getUser()->piggybanks()->where('repeats', 1)->get(); + } + + /** + * @param array $ids + * + * @return Collection + */ + public function getByIds(array $ids) + { + // TODO: Implement getByIds() method. + throw new NotImplementedException; + } + + /** + * @param \Account $account + * + * @return float + */ + public function leftOnAccount(\Account $account) + { + // TODO: Implement leftOnAccount() method. + throw new NotImplementedException; + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Report.php b/app/lib/FireflyIII/Database/Report.php new file mode 100644 index 0000000000..db48935f09 --- /dev/null +++ b/app/lib/FireflyIII/Database/Report.php @@ -0,0 +1,175 @@ +sharedExpense) && $account->sharedExpense === true) { + $shared = true; + } else { + if (isset($account->sharedExpense) && $account->sharedExpense === false) { + $shared = false; + } else { + $shared = ($account->getMeta('accountRole') == 'sharedExpense'); + } + } + + $start = clone $month; + $end = clone $month; + $start->startOfMonth(); + $end->endOfMonth(); + $sum = 0; + + // get all journals. + $journals = \TransactionJournal::whereIn( + 'id', function ($query) use ($account, $start, $end) { + $query->select('transaction_journal_id') + ->from('transactions') + ->where('account_id', $account->id); + } + )->before($end)->after($start)->get(); + + + if ($shared) { + $expenses = $journals->filter( + function (\TransactionJournal $journal) use ($account) { + // any withdrawal is an expense: + if ($journal->transactionType->type == 'Withdrawal') { + return $journal; + } + // any transfer away from this account is an expense. + if ($journal->transactionType->type == 'Transfer') { + /** @var \Transaction $t */ + foreach ($journal->transactions as $t) { + if ($t->account_id == $account->id && floatval($t->amount) < 0) { + return $journal; + } + } + } + return null; + } + ); + } else { + $expenses = $journals->filter( + function (\TransactionJournal $journal) use ($account) { + // only withdrawals are expenses: + if ($journal->transactionType->type == 'Withdrawal') { + return $journal; + } + // transfers TO a shared account are also expenses. + if ($journal->transactionType->type == 'Transfer') { + /** @var \Transaction $t */ + foreach ($journal->transactions() as $t) { + if ($t->account->getMeta('accountRole') == 'sharedExpense') { + echo '#'.$journal->id.' is a shared expense!
'; + return $journal; + } + } + } + return null; + } + ); + } + /** @var \TransactionJournal $expense */ + foreach ($expenses as $expense) { + $sum += $expense->getAmount(); + } + + + return $sum; + } + + /** + * @param \Account $account + * @param Carbon $month + * + * @return float + */ + public function getIncomeByMonth(\Account $account, Carbon $month) + { + if (isset($account->sharedExpense) && $account->sharedExpense === true) { + $shared = true; + } else { + if (isset($account->sharedExpense) && $account->sharedExpense === false) { + $shared = false; + } else { + $shared = ($account->getMeta('accountRole') == 'sharedExpense'); + } + } + + $start = clone $month; + $end = clone $month; + $start->startOfMonth(); + $end->endOfMonth(); + $sum = 0; + + // get all journals. + $journals = \TransactionJournal::whereIn( + 'id', function ($query) use ($account, $start, $end) { + $query->select('transaction_journal_id') + ->from('transactions') + ->where('account_id', $account->id); + } + )->before($end)->after($start)->get(); + + + if ($shared) { + $incomes = $journals->filter( + function (\TransactionJournal $journal) use ($account) { + // any deposit is an income: + if ($journal->transactionType->type == 'Deposit') { + return $journal; + } + // any transfer TO this account is an income. + if ($journal->transactionType->type == 'Transfer') { + /** @var \Transaction $t */ + foreach ($journal->transactions as $t) { + if ($t->account_id == $account->id && floatval($t->amount) > 0) { + return $journal; + } + } + } + return null; + } + ); + } else { + $incomes = $journals->filter( + function (\TransactionJournal $journal) use ($account) { + // only deposits are incomes: + if ($journal->transactionType->type == 'Deposit') { + return $journal; + } + return null; + } + ); + } + /** @var \TransactionJournal $expense */ + foreach ($incomes as $income) { + $sum += $income->getAmount(); + } + + + return $sum; + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/Transaction.php b/app/lib/FireflyIII/Database/Transaction.php index 744880dd73..514ea66217 100644 --- a/app/lib/FireflyIII/Database/Transaction.php +++ b/app/lib/FireflyIII/Database/Transaction.php @@ -2,44 +2,71 @@ 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\Exception\FireflyException; use FireflyIII\Database\Ifaces\CommonDatabaseCalls; use FireflyIII\Database\Ifaces\CUD; use FireflyIII\Database\Ifaces\TransactionInterface; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; +use Illuminate\Support\MessageBag; + + /** * Class Transaction * * @package FireflyIII\Database */ -class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls +class Transaction implements CUD, CommonDatabaseCalls { use SwitchUser; /** - * @param Ardent $model + * @param \Eloquent $model * * @return bool */ - public function destroy(Ardent $model) + public function destroy(\Eloquent $model) { // TODO: Implement destroy() method. + throw new NotImplementedException; } /** - * Validates a model. Returns an array containing MessageBags - * errors/warnings/successes. + * @param array $data * - * @param Ardent $model - * - * @return array + * @return \Eloquent */ - public function validateObject(Ardent $model) + public function store(array $data) { - // TODO: Implement validateObject() 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->isValid()) { + $transaction->save(); + } else { + throw new FireflyException($transaction->getErrors()->first()); + } + + return $transaction; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + // TODO: Implement update() method. + throw new NotImplementedException; } /** @@ -105,37 +132,7 @@ class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls $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; + return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes]; } /** @@ -143,21 +140,12 @@ class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls * * @param int $id * - * @return Ardent + * @return \Eloquent */ public function find($id) { // TODO: Implement find() method. - } - - /** - * Returns all objects. - * - * @return Collection - */ - public function get() - { - // TODO: Implement get() method. + throw new NotImplementedException; } /** @@ -170,17 +158,18 @@ class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls public function findByWhat($what) { // TODO: Implement findByWhat() method. + throw new NotImplementedException; } /** - * @param Ardent $model - * @param array $data + * Returns all objects. * - * @return bool + * @return Collection */ - public function update(Ardent $model, array $data) + public function get() { - // TODO: Implement update() method. + // TODO: Implement get() method. + throw new NotImplementedException; } /** @@ -191,5 +180,6 @@ class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls public function getByIds(array $ids) { // TODO: Implement getByIds() method. + throw new NotImplementedException; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/TransactionCurrency.php b/app/lib/FireflyIII/Database/TransactionCurrency.php index 9e46efb207..6de3860ada 100644 --- a/app/lib/FireflyIII/Database/TransactionCurrency.php +++ b/app/lib/FireflyIII/Database/TransactionCurrency.php @@ -3,10 +3,6 @@ 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; /** @@ -14,89 +10,9 @@ use FireflyIII\Database\Ifaces\TransactionCurrencyInterface; * * @package FireflyIII\Database */ -class TransactionCurrency implements TransactionCurrencyInterface, CUD, CommonDatabaseCalls +class TransactionCurrency implements TransactionCurrencyInterface { - /** - * @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 * @@ -106,25 +22,4 @@ class TransactionCurrency implements TransactionCurrencyInterface, CUD, CommonDa { 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. - } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/TransactionJournal.php b/app/lib/FireflyIII/Database/TransactionJournal.php index 3355f458a8..e0f8184fc0 100644 --- a/app/lib/FireflyIII/Database/TransactionJournal.php +++ b/app/lib/FireflyIII/Database/TransactionJournal.php @@ -4,14 +4,13 @@ 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; +use FireflyIII\Exception\FireflyException; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; +use Illuminate\Support\MessageBag; /** * Class TransactionJournal @@ -31,90 +30,251 @@ class TransactionJournal implements TransactionJournalInterface, CUD, CommonData } /** - * @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 + * @param \Eloquent $model * * @return bool */ - public function destroy(Ardent $model) + public function destroy(\Eloquent $model) { - // TODO: Implement destroy() method. + /* + * Trigger deletion. + */ + \Event::fire('transactionJournal.destroy', [$model]); // new and used. + /* + * Since this event will also destroy both transactions, trigger on those as + * well because we might want to update some caches and what-not. + */ + /** @var Transaction $transaction */ + foreach ($model->transactions as $transaction) { + \Event::fire('transaction.destroy', [$transaction]); + } + + $model->delete(); + + return true; } /** - * Validates a model. Returns an array containing MessageBags - * errors/warnings/successes. + * @param array $data * - * @param Ardent $model - * - * @return array + * @return \Eloquent */ - public function validateObject(Ardent $model) + public function store(array $data) { - // TODO: Implement validateObject() method. + /** @var \FireflyIII\Database\TransactionType $typeRepository */ + $typeRepository = \App::make('FireflyIII\Database\TransactionType'); + + /** @var \FireflyIII\Database\Account $accountRepository */ + $accountRepository = \App::make('FireflyIII\Database\Account'); + + /** @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; + + /* + * This must be enough to store the journal: + */ + if (!$journal->isValid()) { + \Log::error($journal->getErrors()->all()); + throw new FireflyException('store() transaction journal failed, but it should not!'); + } + $journal->save(); + + /* + * Still need to find the accounts related to the transactions. + * This depends on the type of transaction. + */ + switch ($data['what']) { + case 'withdrawal': + $data['from'] = $accountRepository->find($data['account_id']); + $data['to'] = $accountRepository->firstExpenseAccountOrCreate($data['expense_account']); + break; + case 'opening': + break; + case 'deposit': + $data['to'] = $accountRepository->find($data['account_id']); + $data['from'] = $accountRepository->firstRevenueAccountOrCreate($data['revenue_account']); + break; + case 'transfer': + $data['from'] = $accountRepository->find($data['account_from_id']); + $data['to'] = $accountRepository->find($data['account_to_id']); + break; + + default: + throw new FireflyException('Cannot save transaction journal with accounts based on $what "' . $data['what'] . '".'); + break; + } + + /* + * 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()); + } + + /* + * Store the budget. + */ + if (isset($data['budget_id']) && intval($data['budget_id']) > 0) { + /** @var \FireflyIII\Database\Budget $budgetRepository */ + $budgetRepository = \App::make('FireflyIII\Database\Budget'); + $budget = $budgetRepository->find(intval($data['budget_id'])); + if ($budget) { + $journal->budgets()->save($budget); + } + } + if (isset($data['category']) && strlen($data['category']) > 0) { + /** @var \FireflyIII\Database\Category $categoryRepository */ + $categoryRepository = \App::make('FireflyIII\Database\Category'); + $category = $categoryRepository->firstOrCreate($data['category']); + if ($category) { + $journal->categories()->save($category); + } + } + + $journal->completed = 1; + $journal->save(); + + /* + * Trigger a search for a relevant recurring transaction. + */ + + + return $journal; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + /** @var \FireflyIII\Database\TransactionType $typeRepository */ + $typeRepository = \App::make('FireflyIII\Database\TransactionType'); + + /** @var \FireflyIII\Database\Account $accountRepository */ + $accountRepository = \App::make('FireflyIII\Database\Account'); + + /** @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']); + + $model->transactionType()->associate($journalType); + $model->transactionCurrency()->associate($currency); + $model->user()->associate($this->getUser()); + $model->description = $data['description']; + $model->date = $data['date']; + + /* + * This must be enough to store the journal: + */ + if (!$model->isValid()) { + \Log::error($model->getErrors()->all()); + throw new FireflyException('store() transaction journal failed, but it should not!'); + } + $model->save(); + + /* + * Still need to find the accounts related to the transactions. + * This depends on the type of transaction. + */ + switch ($data['what']) { + case 'withdrawal': + $data['from'] = $accountRepository->find($data['account_id']); + $data['to'] = $accountRepository->firstExpenseAccountOrCreate($data['expense_account']); + break; + case 'opening': + break; + case 'deposit': + $data['to'] = $accountRepository->find($data['account_id']); + $data['from'] = $accountRepository->firstRevenueAccountOrCreate($data['revenue_account']); + break; + case 'transfer': + $data['from'] = $accountRepository->find($data['account_from_id']); + $data['to'] = $accountRepository->find($data['account_to_id']); + break; + + default: + throw new FireflyException('Cannot save transaction journal with accounts based on $what "' . $data['what'] . '".'); + break; + } + + /* + * Update the budget and the category. + */ + $components = []; + if (isset($data['budget_id']) && intval($data['budget_id']) > 0) { + /** @var \FireflyIII\Database\Budget $budgetRepository */ + $budgetRepository = \App::make('FireflyIII\Database\Budget'); + $budget = $budgetRepository->find(intval($data['budget_id'])); + if ($budget) { + $components[] = $budget->id; + } + } + if (strlen($data['category']) > 0) { + /** @var \FireflyIII\Database\Category $categoryRepository */ + $categoryRepository = \App::make('FireflyIII\Database\Category'); + $category = $categoryRepository->firstOrCreate($data['category']); + if ($category) { + $components[] = $category->id; + } + } + $model->components()->sync($components); + + /* + * Now we can update the transactions related to this journal. + */ + $amount = floatval($data['amount']); + /** @var \Transaction $transaction */ + foreach ($model->transactions()->get() as $transaction) { + if (floatval($transaction->amount) > 0) { + // the TO transaction. + $transaction->account()->associate($data['to']); + $transaction->amount = $amount; + } else { + $transaction->account()->associate($data['from']); + $transaction->amount = $amount * -1; + } + if (!$transaction->isValid()) { + throw new FireflyException('Could not validate transaction while saving.'); + } + $transaction->save(); + } + + return new MessageBag; } /** @@ -163,26 +323,103 @@ class TransactionJournal implements TransactionJournalInterface, CUD, CommonData $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!'); + /* + * Amount: + */ + if (isset($model['amount']) && floatval($model['amount']) < 0.01) { + $errors->add('amount', 'Amount must be > 0.01'); + } else { + if (!isset($model['amount'])) { + $errors->add('amount', 'Amount must be set!'); + } else { + $successes->add('amount', 'OK'); + } } - $validator = \Validator::make([$model], \Transaction::$rules); + /* + * Budget: + */ + if (isset($model['budget_id']) && !ctype_digit($model['budget_id'])) { + $errors->add('budget_id', 'Invalid budget'); + } else { + $successes->add('budget_id', 'OK'); + } + + $successes->add('category', 'OK'); + + /* + * Many checks to catch invalid or not-existing accounts. + */ + $accountError = false; + switch (true) { + // this combination is often seen in withdrawals. + case (isset($model['account_id']) && isset($model['expense_account'])): + if (intval($model['account_id']) < 1) { + $errors->add('account_id', 'Invalid account.'); + } else { + $successes->add('account_id', 'OK'); + } + $successes->add('expense_account', 'OK'); + break; + case (isset($model['account_id']) && isset($model['revenue_account'])): + if (intval($model['account_id']) < 1) { + $errors->add('account_id', 'Invalid account.'); + } else { + $successes->add('account_id', 'OK'); + } + $successes->add('revenue_account', 'OK'); + break; + case (isset($model['account_from_id']) && isset($model['account_to_id'])): + if (intval($model['account_from_id']) < 1 || intval($model['account_from_id']) < 1) { + $errors->add('account_from_id', 'Invalid account selected.'); + $errors->add('account_to_id', 'Invalid account selected.'); + + } else { + if (intval($model['account_from_id']) == intval($model['account_to_id'])) { + $errors->add('account_to_id', 'Cannot be the same as "from" account.'); + $errors->add('account_from_id', 'Cannot be the same as "to" account.'); + } else { + $successes->add('account_from_id', 'OK'); + $successes->add('account_to_id', 'OK'); + } + } + break; + + case (isset($model['to']) && isset($model['from'])): + if (is_object($model['to']) && is_object($model['from'])) { + $successes->add('from', 'OK'); + $successes->add('to', 'OK'); + } + break; + + default: + throw new FireflyException('Cannot validate accounts for transaction journal.'); + break; + } + + // if (isset($model['to_id']) && intval($model['to_id']) < 1) { + // $errors->add('account_to', 'Invalid to-account'); + // } + // + // if (isset($model['from_id']) && intval($model['from_id']) < 1) { + // $errors->add('account_from', 'Invalid from-account'); + // + // } + // if (isset($model['account_id']) && intval($model['account_id']) < 1) { + // $errors->add('account_id', 'Invalid 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'); + // } + + + $validator = \Validator::make([$model], \TransactionJournal::$rules); if ($validator->invalid()) { $errors->merge($errors); } @@ -197,108 +434,24 @@ class TransactionJournal implements TransactionJournalInterface, CUD, CommonData if (!$errors->has('date')) { $successes->add('date', 'OK'); } - return [ - 'errors' => $errors, - 'warnings' => $warnings, - 'successes' => $successes - ]; + + 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 + * @return \Eloquent */ 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. * @@ -309,6 +462,18 @@ class TransactionJournal implements TransactionJournalInterface, CUD, CommonData public function findByWhat($what) { // TODO: Implement findByWhat() method. + throw new NotImplementedException; + } + + /** + * Returns all objects. + * + * @return Collection + */ + public function get() + { + return $this->getUser()->transactionjournals()->with(['TransactionType', 'transactions', 'transactions.account', 'transactions.account.accountType']) + ->get(); } /** @@ -318,17 +483,171 @@ class TransactionJournal implements TransactionJournalInterface, CUD, CommonData */ public function getByIds(array $ids) { - // TODO: Implement getByIds() method. + return $this->getUser()->transactionjournals()->with('transactions')->whereIn('id', $ids)->orderBy('date', 'ASC')->get(); } /** - * @param Ardent $model - * @param array $data - * - * @return bool + * @return TransactionJournal */ - public function update(Ardent $model, array $data) + public function first() { - // TODO: Implement update() method. + return $this->getUser()->transactionjournals()->orderBy('date', 'ASC')->first(); + } + + /** + * @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(); + } + + /** + * @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 $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; + } + + public function getDepositsPaginated($limit = 50) + { + $offset = intval(\Input::get('page')) > 0 ? (intval(\Input::get('page')) - 1) * $limit : 0; + + $set = $this->getUser()->transactionJournals()->transactionTypes(['Deposit'])->withRelevantData()->take($limit)->offset($offset)->orderBy( + 'date', 'DESC' + )->get(['transaction_journals.*']); + $count = $this->getUser()->transactionJournals()->transactionTypes(['Deposit'])->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + } + + /** + * @param \Account $account + * @param int $count + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getInDateRangeAccount(\Account $account, $count = 20, Carbon $start, Carbon $end) + { + + $accountID = $account->id; + $query = $this->_user->transactionjournals()->with(['transactions', 'transactioncurrency', 'transactiontype'])->leftJoin( + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' + )->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $accountID)->where( + 'date', '>=', $start->format('Y-m-d') + )->where('date', '<=', $end->format('Y-m-d'))->orderBy('transaction_journals.date', 'DESC')->orderBy('transaction_journals.id', 'DESC')->take( + $count + )->get(['transaction_journals.*']); + + return $query; + } + + public function getTransfersPaginated($limit = 50) + { + $offset = intval(\Input::get('page')) > 0 ? (intval(\Input::get('page')) - 1) * $limit : 0; + + $set = $this->getUser()->transactionJournals()->transactionTypes(['Transfer'])->withRelevantData()->take($limit)->offset($offset)->orderBy( + 'date', 'DESC' + )->get(['transaction_journals.*']); + $count = $this->getUser()->transactionJournals()->transactionTypes(['Transfer'])->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + } + + public function getWithdrawalsPaginated($limit = 50) + { + $offset = intval(\Input::get('page')) > 0 ? (intval(\Input::get('page')) - 1) * $limit : 0; + + $set = $this->getUser()->transactionJournals()->transactionTypes(['Withdrawal'])->withRelevantData()->take($limit)->offset($offset)->orderBy( + 'date', 'DESC' + )->get(['transaction_journals.*']); + $count = $this->getUser()->transactionJournals()->transactionTypes(['Withdrawal'])->count(); + $items = []; + foreach ($set as $entry) { + $items[] = $entry; + } + + return \Paginator::make($items, $count, $limit); + } + + /** + * @param string $query + * @param \TransactionJournal $journal + * + * @return Collection + */ + public function searchRelated($query, \TransactionJournal $journal) + { + $start = clone $journal->date; + $end = clone $journal->date; + $start->startOfMonth(); + $end->endOfMonth(); + + // get already related transactions: + $exclude = [$journal->id]; + foreach ($journal->transactiongroups()->get() as $group) { + foreach ($group->transactionjournals() as $jrnl) { + $exclude[] = $jrnl->id; + } + } + $exclude = array_unique($exclude); + + $query = $this->getUser()->transactionjournals() + ->withRelevantData() + ->before($end) + ->after($start) + ->whereNotIn('id', $exclude) + ->where('description', 'LIKE', '%' . $query . '%') + ->get(); + + return $query; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/TransactionType.php b/app/lib/FireflyIII/Database/TransactionType.php index 0022ec36fb..566c60ba45 100644 --- a/app/lib/FireflyIII/Database/TransactionType.php +++ b/app/lib/FireflyIII/Database/TransactionType.php @@ -3,41 +3,54 @@ 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; +use FireflyIII\Exception\FireflyException; +use FireflyIII\Exception\NotImplementedException; +use Illuminate\Support\Collection; + /** * Class TransactionType * * @package FireflyIII\Database */ -class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCalls +class TransactionType implements CUD, CommonDatabaseCalls { /** - * @param Ardent $model + * @param \Eloquent $model * * @return bool */ - public function destroy(Ardent $model) + public function destroy(\Eloquent $model) { // TODO: Implement destroy() method. + throw new NotImplementedException; } /** - * Validates a model. Returns an array containing MessageBags - * errors/warnings/successes. + * @param array $data * - * @param Ardent $model - * - * @return array + * @return \Eloquent */ - public function validateObject(Ardent $model) + public function store(array $data) { - // TODO: Implement validateObject() method. + // TODO: Implement store() method. + throw new NotImplementedException; + } + + /** + * @param \Eloquent $model + * @param array $data + * + * @return bool + */ + public function update(\Eloquent $model, array $data) + { + // TODO: Implement update() method. + throw new NotImplementedException; } /** @@ -51,16 +64,7 @@ class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCa public function validate(array $model) { // TODO: Implement validate() method. - } - - /** - * @param array $data - * - * @return Ardent - */ - public function store(array $data) - { - // TODO: Implement store() method. + throw new NotImplementedException; } /** @@ -68,21 +72,12 @@ class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCa * * @param int $id * - * @return Ardent + * @return \Eloquent */ public function find($id) { // TODO: Implement find() method. - } - - /** - * Returns all objects. - * - * @return Collection - */ - public function get() - { - // TODO: Implement get() method. + throw new NotImplementedException; } /** @@ -98,12 +93,32 @@ class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCa case 'opening': return \TransactionType::whereType('Opening balance')->first(); break; + case 'transfer': + return \TransactionType::whereType('Transfer')->first(); + break; + case 'withdrawal': + return \TransactionType::whereType('Withdrawal')->first(); + break; + case 'deposit': + return \TransactionType::whereType('Deposit')->first(); + break; default: throw new FireflyException('Cannot find transaction type described as "' . e($what) . '".'); break; + } - return null; + } + + /** + * Returns all objects. + * + * @return Collection + */ + public function get() + { + // TODO: Implement get() method. + throw new NotImplementedException; } /** @@ -114,16 +129,6 @@ class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCa 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. + throw new NotImplementedException; } } \ No newline at end of file diff --git a/app/lib/FireflyIII/Database/User.php b/app/lib/FireflyIII/Database/User.php new file mode 100644 index 0000000000..e33ef42f4c --- /dev/null +++ b/app/lib/FireflyIII/Database/User.php @@ -0,0 +1,73 @@ +first(); + } + + /** + * @param $reset + * + * @return null|User + */ + public function findByReset($reset) + { + return \User::where('reset', $reset)->first(); + } + + /** + * @param array $data + * + * @return bool|\User + */ + public function register(array $data) + { + $user = new \User; + $user->email = isset($data['email']) ? $data['email'] : null; + $user->migrated = 0; + $user->reset = \Str::random(32); + $user->password = \Hash::make(\Str::random(12)); + + if (!$user->save()) { + \Log::error('Invalid user with data: ' . isset($data['email']) ? $data['email'] : '(no email!)'); + \Session::flash('error', 'Input invalid, please try again: ' . $user->getErrors()->first()); + + return false; + } + $user->save(); + + return $user; + + } + + /** + * @param \User $user + * @param $password + * + * @return bool + */ + public function updatePassword(\User $user, $password) + { + $user->password = $password; + $user->forceSave(); + + return true; + } + +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Event/Account.php b/app/lib/FireflyIII/Event/Account.php new file mode 100644 index 0000000000..7b265b9816 --- /dev/null +++ b/app/lib/FireflyIII/Event/Account.php @@ -0,0 +1,39 @@ +id . '.latestBalance'); + \Cache::forget('account.' . $account->id . '.lastActivityDate'); + } + + public function store(\Account $account) + { + + \Cache::forget('account.' . $account->id . '.latestBalance'); + \Cache::forget('account.' . $account->id . '.lastActivityDate'); + } + + /** + * @param Dispatcher $events + */ + public function subscribe(Dispatcher $events) + { + // triggers when others are updated. + $events->listen('account.store', 'FireflyIII\Event\Account@store'); + $events->listen('account.update', 'FireflyIII\Event\Account@update'); + $events->listen('account.destroy', 'FireflyIII\Event\Account@destroy'); + } + + public function update(\Account $account) + { + \Cache::forget('account.' . $account->id . '.latestBalance'); + \Cache::forget('account.' . $account->id . '.lastActivityDate'); + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Event/Budget.php b/app/lib/FireflyIII/Event/Budget.php new file mode 100644 index 0000000000..087d23c968 --- /dev/null +++ b/app/lib/FireflyIII/Event/Budget.php @@ -0,0 +1,62 @@ +startdate, $limit->repeat_freq, 0); + $end->subDay(); + + $set = $limit->limitrepetitions()->where('startdate', $limit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))->get(); + /* + * Create new LimitRepetition: + */ + if ($set->count() == 0) { + + $repetition = new \LimitRepetition(); + $repetition->startdate = $limit->startdate; + $repetition->enddate = $end; + $repetition->amount = $limit->amount; + $repetition->limit()->associate($limit); + + try { + $repetition->save(); + } catch (QueryException $e) { + \Log::error('Trying to save new Limitrepetition failed!'); + \Log::error($e->getMessage()); + } + } else { + if ($set->count() == 1) { + /* + * Update existing one. + */ + $repetition = $set->first(); + $repetition->amount = $limit->amount; + $repetition->save(); + + } + } + + } + + /** + * @param Dispatcher $events + */ + public function subscribe(Dispatcher $events) + { + $events->listen('limits.store', 'FireflyIII\Event\Budget@storeOrUpdateLimit'); + $events->listen('limits.update', 'FireflyIII\Event\Budget@storeOrUpdateLimit'); + + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Event/Event.php b/app/lib/FireflyIII/Event/Event.php new file mode 100644 index 0000000000..1f670ccb20 --- /dev/null +++ b/app/lib/FireflyIII/Event/Event.php @@ -0,0 +1,35 @@ +piggybanks()->get(); + + // get reminders for each + /** @var \Piggybank $piggyBank */ + foreach ($piggies as $piggyBank) { + $reminders = $piggyBank->reminders()->get(); + /** @var \Reminder $reminder */ + foreach ($reminders as $reminder) { + $reminder->delete(); + } + } + } + + + /** + * @param Dispatcher $events + */ + public function subscribe(Dispatcher $events) + { + // triggers when others are updated. + $events->listen('account.destroy', 'FireflyIII\Event\Event@deleteAccount'); + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Event/Piggybank.php b/app/lib/FireflyIII/Event/Piggybank.php new file mode 100644 index 0000000000..05657efafb --- /dev/null +++ b/app/lib/FireflyIII/Event/Piggybank.php @@ -0,0 +1,311 @@ + 0) { + $event = new \PiggybankEvent; + $event->piggybank()->associate($piggybank); + $event->amount = floatval($amount); + $event->date = new Carbon; + if (!$event->isValid()) { + var_dump($event->getErrors()); + exit(); + } + $event->save(); + } + } + + public function destroyTransfer(\TransactionJournal $journal) + { + if ($journal->piggybankevents()->count() > 0) { + + /** @var \FireflyIII\Database\Piggybank $repository */ + $repository = \App::make('FireflyIII\Database\Piggybank'); + + /** @var \Piggybank $piggyBank */ + $piggyBank = $journal->piggybankevents()->first()->piggybank()->first(); + + /** @var \PiggybankRepetition $repetition */ + $repetition = $repository->findRepetitionByDate($piggyBank, $journal->date); + + $relevantTransaction = null; + /** @var \Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + if ($transaction->account_id == $piggyBank->account_id) { + $relevantTransaction = $transaction; + } + } + if (is_null($relevantTransaction)) { + return; + } + + $repetition->currentamount += floatval($relevantTransaction->amount * -1); + $repetition->save(); + + + $event = new \PiggybankEvent; + $event->piggybank()->associate($piggyBank); + $event->amount = floatval($relevantTransaction->amount * -1); + $event->date = new Carbon; + if (!$event->isValid()) { + var_dump($event->getErrors()); + exit(); + } + $event->save(); + } + } + + /** + * @param \Piggybank $piggybank + * @param float $amount + */ + public function removeMoney(\Piggybank $piggybank, $amount = 0.0) + { + $amount = $amount * -1; + if ($amount < 0) { + $event = new \PiggybankEvent; + $event->piggybank()->associate($piggybank); + $event->amount = floatval($amount); + $event->date = new Carbon; + if (!$event->isValid()) { + var_dump($event->getErrors()); + exit(); + } + $event->save(); + } + } + + public function storePiggybank(\Piggybank $piggybank) + { + if (intval($piggybank->repeats) == 0) { + $repetition = new \PiggybankRepetition; + $repetition->piggybank()->associate($piggybank); + $repetition->startdate = $piggybank->startdate; + $repetition->targetdate = $piggybank->targetdate; + $repetition->currentamount = 0; + $repetition->save(); + } + } + + /* + * + */ + + /** + * @param \TransactionJournal $journal + * @param int $piggybankId + */ + public function storeTransfer(\TransactionJournal $journal, $piggybankId = 0) + { + if ($piggybankId == 0 || is_null($piggybankId)) { + return; + } + /** @var \FireflyIII\Database\Piggybank $repository */ + $repository = \App::make('FireflyIII\Database\Piggybank'); + + /** @var \Piggybank $piggyBank */ + $piggyBank = $repository->find($piggybankId); + + /** @var \PiggybankRepetition $repetition */ + $repetition = $repository->findRepetitionByDate($piggyBank, $journal->date); + + \Log::debug( + 'Connecting transfer "' . $journal->description . '" (#' . $journal->id . ') to piggy bank "' . $piggyBank->name . '" (#' . $piggyBank->id . ').' + ); + + // some variables to double check the connection. + $start = $piggyBank->startdate; + $end = $piggyBank->targetdate; + $amount = floatval($piggyBank->targetamount); + $leftToSave = $amount - floatval($repetition->currentamount); + $relevantTransaction = null; + /** @var \Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + if ($transaction->account_id == $piggyBank->account_id) { + $relevantTransaction = $transaction; + } + } + if (is_null($relevantTransaction)) { + return; + } + \Log::debug('Relevant transaction is #' . $relevantTransaction->id . ' with amount ' . $relevantTransaction->amount); + + // if FF3 should save this connection depends on some variables: + if ($start && $end && $journal->date >= $start && $journal->date <= $end) { + if ($relevantTransaction->amount < 0) { // amount removed from account, so removed from piggy bank. + \Log::debug('Remove from piggy bank.'); + $continue = ($relevantTransaction->amount * -1 <= floatval($repetition->currentamount)); + \Log::debug( + 'relevantTransaction.amount *-1 = ' . ($relevantTransaction->amount * -1) . ' >= current = ' . floatval($repetition->currentamount) + ); + } else { // amount added + \Log::debug('Add from piggy bank.'); + $continue = $relevantTransaction->amount <= $leftToSave; + } + if ($continue) { + \Log::debug('Update repetition.'); + $repetition->currentamount += floatval($relevantTransaction->amount); + $repetition->save(); + + $event = new \PiggybankEvent; + $event->piggybank()->associate($piggyBank); + $event->transactionjournal()->associate($journal); + $event->amount = floatval($relevantTransaction->amount); + $event->date = new Carbon; + if (!$event->isValid()) { + var_dump($event->getErrors()); + exit(); + } + $event->save(); + } + } + } + + /** + * @param Dispatcher $events + */ + public function subscribe(Dispatcher $events) + { + // triggers on piggy bank events: + $events->listen('piggybank.addMoney', 'FireflyIII\Event\Piggybank@addMoney'); + $events->listen('piggybank.removeMoney', 'FireflyIII\Event\Piggybank@removeMoney'); + $events->listen('piggybank.store', 'FireflyIII\Event\Piggybank@storePiggybank'); + $events->listen('piggybank.update', 'FireflyIII\Event\Piggybank@updatePiggybank'); + + \App::before( + function ($request) { + $this->validateRepeatedExpenses(); + } + ); + + //$events->listen('piggybank.boo', 'FireflyIII\Event\Piggybank@updatePiggybank'); + + + // triggers when others are updated. + $events->listen('transactionJournal.store', 'FireflyIII\Event\Piggybank@storeTransfer'); + $events->listen('transactionJournal.update', 'FireflyIII\Event\Piggybank@updateTransfer'); + $events->listen('transactionJournal.destroy', 'FireflyIII\Event\Piggybank@destroyTransfer'); + } + + /** + * Validates the presence of repetitions for all repeated expenses! + */ + public function validateRepeatedExpenses() + { + if(!\Auth::check()) { + return; + } + /** @var \FireflyIII\Database\RepeatedExpense $repository */ + $repository = \App::make('FireflyIII\Database\RepeatedExpense'); + + $list = $repository->get(); + $today = Carbon::now(); + + /** @var \Piggybank $entry */ + foreach ($list as $entry) { + $start = $entry->startdate; + $target = $entry->targetdate; + // find a repetition on this date: + $count = $entry->piggybankrepetitions()->starts($start)->targets($target)->count(); + if ($count == 0) { + $repetition = new \PiggybankRepetition; + $repetition->piggybank()->associate($entry); + $repetition->startdate = $start; + $repetition->targetdate = $target; + $repetition->currentamount = 0; + $repetition->save(); + } + // then continue and do something in the current relevant timeframe. + + $currentTarget = clone $target; + $currentStart = null; + while ($currentTarget < $today) { + $currentStart = \DateKit::subtractPeriod($currentTarget, $entry->rep_length, 0); + $currentTarget = \DateKit::addPeriod($currentTarget, $entry->rep_length, 0); + // create if not exists: + $count = $entry->piggybankrepetitions()->starts($currentStart)->targets($currentTarget)->count(); + if ($count == 0) { + $repetition = new \PiggybankRepetition; + $repetition->piggybank()->associate($entry); + $repetition->startdate = $currentStart; + $repetition->targetdate = $currentTarget; + $repetition->currentamount = 0; + $repetition->save(); + } + + } + } + } + + public function updatePiggybank(\Piggybank $piggyBank) + { + // get the repetition: + $repetition = $piggyBank->currentRelevantRep(); + $repetition->startdate = $piggyBank->startdate; + $repetition->targetdate = $piggyBank->targetdate; + $repetition->save(); + } + + public function updateTransfer(\TransactionJournal $journal) + { + + if ($journal->piggybankevents()->count() > 0) { + + $event = $journal->piggybankevents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->first(); + $eventSum = floatval($journal->piggybankevents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->sum('amount')); + + /** @var \FireflyIII\Database\Piggybank $repository */ + $repository = \App::make('FireflyIII\Database\Piggybank'); + + /** @var \Piggybank $piggyBank */ + $piggyBank = $journal->piggybankevents()->first()->piggybank()->first(); + + /** @var \PiggybankRepetition $repetition */ + $repetition = $repository->findRepetitionByDate($piggyBank, $journal->date); + + $relevantTransaction = null; + /** @var \Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + if ($transaction->account_id == $piggyBank->account_id) { + $relevantTransaction = $transaction; + } + } + if (is_null($relevantTransaction)) { + return; + } + + $diff = floatval($relevantTransaction->amount) - floatval($eventSum); + /* + * Create an event to remove /add the difference from the piggy + */ + $repetition->currentamount += $diff; + $repetition->save(); + + + $event = new \PiggybankEvent; + $event->piggybank()->associate($piggyBank); + $event->transactionJournal()->associate($journal); + $event->amount = $diff; + $event->date = new Carbon; + if (!$event->isValid()) { + var_dump($event->getErrors()); + exit(); + } + $event->save(); + } + + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Event/Transaction.php b/app/lib/FireflyIII/Event/Transaction.php new file mode 100644 index 0000000000..285e942875 --- /dev/null +++ b/app/lib/FireflyIII/Event/Transaction.php @@ -0,0 +1,38 @@ +account_id . '.latestBalance'); + \Cache::forget('account.' . $transaction->account_id . '.lastActivityDate'); + } + + public function store(\Transaction $transaction) + { + \Cache::forget('account.' . $transaction->account_id . '.latestBalance'); + \Cache::forget('account.' . $transaction->account_id . '.lastActivityDate'); + } + + /** + * @param Dispatcher $events + */ + public function subscribe(Dispatcher $events) + { + // triggers when others are updated. + $events->listen('transaction.store', 'FireflyIII\Event\Transaction@store'); + $events->listen('transaction.update', 'FireflyIII\Event\Transaction@update'); + $events->listen('transaction.destroy', 'FireflyIII\Event\Transaction@destroy'); + } + + public function update(\Transaction $transaction) + { + \Cache::forget('account.' . $transaction->account_id . '.latestBalance'); + \Cache::forget('account.' . $transaction->account_id . '.lastActivityDate'); + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Event/TransactionJournal.php b/app/lib/FireflyIII/Event/TransactionJournal.php new file mode 100644 index 0000000000..aabc4c0537 --- /dev/null +++ b/app/lib/FireflyIII/Event/TransactionJournal.php @@ -0,0 +1,46 @@ +get(); + + + /** @var \RecurringTransaction $entry */ + foreach ($set as $entry) { + $repository->scan($entry, $journal); + } + } + + /** + * @param Dispatcher $events + */ + public function subscribe(Dispatcher $events) + { + // triggers when others are updated. + $events->listen('transactionJournal.store', 'FireflyIII\Event\TransactionJournal@store'); + $events->listen('transactionJournal.update', 'FireflyIII\Event\TransactionJournal@update'); + } + + public function update(\TransactionJournal $journal) + { + /** @var \FireflyIII\Database\Recurring $repository */ + $repository = \App::make('FireflyIII\Database\Recurring'); + $set = $repository->get(); + $journal->recurring_transaction_id = null; + $journal->save(); + + /** @var \RecurringTransaction $entry */ + foreach ($set as $entry) { + $repository->scan($entry, $journal); + } + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Exception/FireflyException.php b/app/lib/FireflyIII/Exception/FireflyException.php new file mode 100644 index 0000000000..69890ddfc4 --- /dev/null +++ b/app/lib/FireflyIII/Exception/FireflyException.php @@ -0,0 +1,14 @@ +app->validator->resolver( + function ($translator, $data, $rules, $messages) { + return new FireflyValidator($translator, $data, $rules, $messages); + } + ); + } + + /** + * Return the services bla bla. + * + * @return array + */ + public function provides() + { + return ['reminders', 'filters', 'datekit', 'navigation']; + } + + /** + * Triggered automatically by Laravel + */ + public function register() + { + // FORMAT: + #$this->app->bind('Interface', 'Class'); + + $this->app->bind( + 'reminders', function () { + return new Reminders; + } + ); + $this->app->bind( + 'filter', function () { + return new Filter; + } + ); + $this->app->bind( + 'datekit', function () { + return new Date; + } + ); + $this->app->bind( + 'navigation', function () { + return new Navigation; + } + ); + $this->app->bind( + 'ffform', function () { + return new Form; + } + ); + + /* + * For models, various stuff: + */ + $this->app->bind( + 'steam', function () { + return new Steam; + } + ); + + // preferences: + $this->app->bind('FireflyIII\Shared\Preferences\PreferencesInterface', 'FireflyIII\Shared\Preferences\Preferences'); + + // registration and user mail: + $this->app->bind('FireflyIII\Shared\Mail\RegistrationInterface', 'FireflyIII\Shared\Mail\Registration'); + + // Shortcut so developers don't need to add an Alias in app/config/app.php + $this->app->booting( + function () { + $loader = AliasLoader::getInstance(); + $loader->alias('Reminders', 'FireflyIII\Shared\Facade\Reminders'); + $loader->alias('Filter', 'FireflyIII\Shared\Facade\Filter'); + $loader->alias('DateKit', 'FireflyIII\Shared\Facade\DateKit'); + $loader->alias('Navigation', 'FireflyIII\Shared\Facade\Navigation'); + $loader->alias('FFForm', 'FireflyIII\Shared\Facade\FFForm'); + $loader->alias('Steam', 'FireflyIII\Shared\Facade\Steam'); + } + ); + + } + +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Form/Form.php b/app/lib/FireflyIII/Form/Form.php new file mode 100644 index 0000000000..7388523742 --- /dev/null +++ b/app/lib/FireflyIII/Form/Form.php @@ -0,0 +1,383 @@ +has($name)): + $classes = 'form-group has-error has-feedback'; + break; + case (!is_null($warnings) && $warnings->has($name)): + $classes = 'form-group has-warning has-feedback'; + break; + case (!is_null($successes) && $successes->has($name)): + $classes = 'form-group has-success has-feedback'; + break; + default: + $classes = 'form-group'; + break; + } + + /* + * Add some HTML. + */ + $html = '
'; + $html .= ''; + $html .= '
'; + + + /* + * Switch input type: + */ + unset($options['label']); + switch ($type) { + case 'text': + $html .= \Form::input('text', $name, $value, $options); + break; + case 'amount': + $html .= '
'; + $html .= \Form::input('number', $name, $value, $options); + $html .= '
'; + break; + case 'number': + $html .= \Form::input('number', $name, $value, $options); + break; + case 'checkbox': + $checked = $options['checked']; + unset($options['placeholder'], $options['autocomplete'], $options['class']); + $html .= '
'; + + + break; + case 'date': + $html .= \Form::input('date', $name, $value, $options); + break; + case 'select': + $html .= \Form::select($name, $list, $value, $options); + break; + default: + throw new FireflyException('Cannot handle type "' . $type . '" in FFFormBuilder.'); + break; + } + + /* + * If errors, respond to them: + */ + + if (!is_null($errors)) { + if ($errors->has($name)) { + $html .= ''; + $html .= '

' . e($errors->first($name)) . '

'; + } + } + unset($errors); + /* + * If warnings, respond to them: + */ + + if (!is_null($warnings)) { + if ($warnings->has($name)) { + $html .= ''; + $html .= '

' . e($warnings->first($name)) . '

'; + } + } + unset($warnings); + + /* + * If successes, respond to them: + */ + + if (!is_null($successes)) { + if ($successes->has($name)) { + $html .= ''; + $html .= '

' . e($successes->first($name)) . '

'; + } + } + unset($successes); + + $html .= '
'; + $html .= '
'; + + return $html; + + } + + /** + * @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', + 'account_from_id' => 'Account from', 'account_to_id' => 'Account to', 'account_id' => 'Asset account']; + + return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name)); + + } + + /** + * @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 int $value + * @param null $checked + * @param array $options + * + * @return string + * @throws FireflyException + */ + public static function ffCheckbox($name, $value = 1, $checked = null, $options = []) + { + $options['checked'] = $checked === true ? true : null; + + return self::ffInput('checkbox', $name, $value, $options); + } + + /** + * @param $name + * @param null $value + * @param array $options + * + * @return string + * @throws FireflyException + */ + public static function ffDate($name, $value = null, array $options = []) + { + return self::ffInput('date', $name, $value, $options); + } + + /** + * @param $name + * @param null $value + * @param array $options + * + * @return string + * @throws FireflyException + */ + public static function ffInteger($name, $value = null, array $options = []) + { + $options['step'] = '1'; + + return self::ffInput('number', $name, $value, $options); + + } + + /** + * Return buttons for update/validate/return. + * + * @param $type + * @param $name + * + * @return string + * @throws FireflyException + */ + public static function ffOptionsList($type, $name) + { + $previousValue = \Input::old('post_submit_action'); + $previousValue = is_null($previousValue) ? 'store' : $previousValue; + /* + * Store. + */ + $store = ''; + switch ($type) { + case 'create': + $store = '
'; + $store .= '
'; + break; + case 'update': + $store = '
'; + $store .= '
'; + break; + default: + throw new FireflyException('Cannot create ffOptionsList for option (store) ' . $type); + break; + } + + /* + * validate is always the same: + */ + $validate = '
'; + + /* + * Store & return: + */ + switch ($type) { + case 'create': + $return = '
'; + break; + case 'update': + $return = '
'; + break; + default: + throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type); + break; + } + + return $store . $validate . $return; + } + + /** + * @param $name + * @param array $list + * @param null $selected + * @param array $options + * + * @return string + * @throws FireflyException + */ + public static function ffSelect($name, array $list = [], $selected = null, array $options = []) + { + return self::ffInput('select', $name, $selected, $options, $list); + } + + /** + * @param $name + * @param null $value + * @param array $options + * + * @return string + * @throws FireflyException + */ + public static function ffTags($name, $value = null, array $options = []) + { + $options['data-role'] = 'tagsinput'; + + return self::ffInput('text', $name, $value, $options); + } + + /** + * @param $name + * @param null $value + * @param array $options + * + * @return string + * @throws FireflyException + */ + public static function ffText($name, $value = null, array $options = []) + { + return self::ffInput('text', $name, $value, $options); + + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Search/Search.php b/app/lib/FireflyIII/Search/Search.php new file mode 100644 index 0000000000..6af5a92c15 --- /dev/null +++ b/app/lib/FireflyIII/Search/Search.php @@ -0,0 +1,101 @@ +accounts()->with('accounttype')->where( + function ($q) use ($words) { + foreach ($words as $word) { + $q->orWhere('name', 'LIKE', '%' . e($word) . '%'); + } + } + )->get(); + } + + /** + * @param array $words + * + * @return Collection + */ + public function searchBudgets(array $words) + { + /** @var Collection $set */ + $set = \Auth::user()->budgets()->get(); + $newSet = $set->filter( + function (\Budget $b) use ($words) { + $found = 0; + foreach ($words as $word) { + if (!(strpos(strtolower($b->name), strtolower($word)) === false)) { + $found++; + } + } + + return $found > 0; + } + ); + + return $newSet; + } + + /** + * @param array $words + * + * @return Collection + */ + public function searchCategories(array $words) + { + /** @var Collection $set */ + $set = \Auth::user()->categories()->get(); + $newSet = $set->filter( + function (\Category $c) use ($words) { + $found = 0; + foreach ($words as $word) { + if (!(strpos(strtolower($c->name), strtolower($word)) === false)) { + $found++; + } + } + + return $found > 0; + } + ); + + return $newSet; + } + + /** + * @param array $words + * + * @return Collection + */ + public function searchTags(array $words) + { + return new Collection; + } + + /** + * @param array $words + * + * @return Collection + */ + public function searchTransactions(array $words) + { + return \Auth::user()->transactionjournals()->withRelevantData()->where( + function ($q) use ($words) { + foreach ($words as $word) { + $q->orWhere('description', 'LIKE', '%' . e($word) . '%'); + } + } + )->get(); + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Facade/DateKit.php b/app/lib/FireflyIII/Shared/Facade/DateKit.php new file mode 100644 index 0000000000..387d3c7372 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Facade/DateKit.php @@ -0,0 +1,16 @@ + 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; - } -} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Mail/Registration.php b/app/lib/FireflyIII/Shared/Mail/Registration.php new file mode 100644 index 0000000000..eecfd4a135 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Mail/Registration.php @@ -0,0 +1,80 @@ +password = $password; + $user->reset = \Str::random(32); // new one. + $user->forceSave(); + $email = $user->email; + + + $data = ['password' => $password]; + try { + \Mail::send( + ['emails.user.register-html', 'emails.user.register-text'], $data, function ($message) use ($email) { + $message->to($email, $email)->subject('Welcome to Firefly!'); + } + ); + } catch(Swift_RfcComplianceException $e) {} + } + + /** + * @param \User $user + * + * @return mixed|void + */ + public function sendResetVerification(\User $user) + { + $reset = \Str::random(32); + $user->reset = $reset; + $user->forceSave(); + $email = $user->email; + + $data = ['reset' => $reset]; + \Mail::send( + ['emails.user.remindme-html', 'emails.user.remindme-text'], $data, function ($message) use ($email) { + $message->to($email, $email)->subject('Forgot your password?'); + } + ); + + + } + + /** + * @param \User $user + * + * @return mixed|void + */ + public function sendVerificationMail(\User $user) + { + + $reset = \Str::random(32); + $user->reset = $reset; + $user->forceSave(); + $email = $user->email; + $data = ['reset' => $reset]; + + \Mail::send( + ['emails.user.verify-html', 'emails.user.verify-text'], $data, function ($message) use ($email) { + $message->to($email, $email)->subject('Verify your e-mail address.'); + } + ); + } + +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Mail/RegistrationInterface.php b/app/lib/FireflyIII/Shared/Mail/RegistrationInterface.php new file mode 100644 index 0000000000..0741757b4e --- /dev/null +++ b/app/lib/FireflyIII/Shared/Mail/RegistrationInterface.php @@ -0,0 +1,34 @@ +mapData((array)$attributes)->newInstance([], true); + $instance->setRawAttributes((array)$attributes, true); + + return $instance; + } + + /** + * + * instead of using $this->newInstance(), call + * newInstance() on the object from mapData + * + * @param bool $excludeDeleted + * + * @return \Illuminate\Database\Eloquent\Builder|static + */ + public function newQuery($excludeDeleted = true) + { + // If using Laravel 4.0.x then use the following commented version of this command + // $builder = new Builder($this->newBaseQueryBuilder()); + // newEloquentBuilder() was added in 4.1 + $builder = $this->newEloquentBuilder($this->newBaseQueryBuilder()); + + // Once Firefly has the query builders, it will set the model instances so the + // builder can easily access any information it may need from the model + // while it is constructing and executing various queries against it. + $builder->setModel($this)->with($this->with); + + if ($excludeDeleted && $this->softDelete) { + $builder->whereNull($this->getQualifiedDeletedAtColumn()); + } + + if ($this->subclassField && $this->isSubclass()) { + $builder->where($this->subclassField, '=', get_class($this)); + } + + return $builder; + } + + /** + * ensure that the subclass field is assigned on save + * + * @param array $rules + * @param array $customMessages + * @param array $options + * @param callable $beforeSave + * @param callable $afterSave + * + * @return bool + */ + public function save( + array $rules = [], array $customMessages = [], array $options = [], \Closure $beforeSave = null, \Closure $afterSave = null + ) { + if ($this->subclassField) { + $this->attributes[$this->subclassField] = get_class($this); + } + + return parent::save($rules, $customMessages, $options, $beforeSave, $afterSave); + } + + /** + * if no subclass is defined, function as normal + * + * @param array $attributes + * + * @return \Illuminate\Database\Eloquent\Model|static + */ + public function mapData(array $attributes) + { + if (!$this->subclassField) { + return $this->newInstance(); + } + + return new $attributes[$this->subclassField]; + } + + /** + * @return bool + */ + public function isSubclass() + { + return $this->isSubclass; + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Toolkit/Date.php b/app/lib/FireflyIII/Shared/Toolkit/Date.php index 1abf90e321..90152551ae 100644 --- a/app/lib/FireflyIII/Shared/Toolkit/Date.php +++ b/app/lib/FireflyIII/Shared/Toolkit/Date.php @@ -3,7 +3,7 @@ namespace FireflyIII\Shared\Toolkit; use Carbon\Carbon; -use Firefly\Exception\FireflyException; +use FireflyIII\Exception\FireflyException; /** * Class Date @@ -13,49 +13,17 @@ use Firefly\Exception\FireflyException; 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 Carbon $theDate * @param $repeatFreq * @param $skip * * @return Carbon * @throws FireflyException */ - public function addPeriod(Carbon $date, $repeatFreq, $skip) + public function addPeriod(Carbon $theDate, $repeatFreq, $skip) { - $add = ($skip + 1); + $date = clone $theDate; + $add = ($skip + 1); switch ($repeatFreq) { default: throw new FireflyException('Cannot do addPeriod for $repeat_freq ' . $repeatFreq); @@ -63,12 +31,15 @@ class Date case 'daily': $date->addDays($add); break; + case 'week': case 'weekly': $date->addWeeks($add); break; + case 'month': case 'monthly': $date->addMonths($add); break; + case 'quarter': case 'quarterly': $months = $add * 3; $date->addMonths($months); @@ -77,10 +48,213 @@ class Date $months = $add * 6; $date->addMonths($months); break; + case 'year': case 'yearly': $date->addYears($add); break; } + return $date; } -} \ No newline at end of file + + /** + * @param Carbon $theCurrentEnd + * @param $repeatFreq + * + * @return mixed + * @throws FireflyException + */ + public function endOfPeriod(Carbon $theCurrentEnd, $repeatFreq) + { + $currentEnd = clone $theCurrentEnd; + switch ($repeatFreq) { + default: + throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq); + break; + case 'daily': + $currentEnd->addDay(); + break; + case 'week': + case 'weekly': + $currentEnd->addWeek()->subDay(); + break; + case 'month': + case 'monthly': + $currentEnd->addMonth()->subDay(); + break; + case 'quarter': + case 'quarterly': + $currentEnd->addMonths(3)->subDay(); + break; + case 'half-year': + $currentEnd->addMonths(6)->subDay(); + break; + case 'year': + case 'yearly': + $currentEnd->addYear()->subDay(); + break; + } + + return $currentEnd; + } + + /** + * @param Carbon $theCurrentEnd + * @param $repeatFreq + * + * @return mixed + * @throws FireflyException + */ + public function endOfX(Carbon $theCurrentEnd, $repeatFreq) + { + $currentEnd = clone $theCurrentEnd; + switch ($repeatFreq) { + default: + throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq); + break; + case 'daily': + $currentEnd->endOfDay(); + break; + case 'week': + case 'weekly': + $currentEnd->endOfWeek(); + break; + case 'month': + case 'monthly': + $currentEnd->endOfMonth(); + break; + case 'quarter': + case 'quarterly': + $currentEnd->lastOfQuarter(); + break; + case 'half-year': + $month = intval($theCurrentEnd->format('m')); + $currentEnd->endOfYear(); + if($month <= 6) { + $currentEnd->subMonths(6); + } + break; + case 'year': + case 'yearly': + $currentEnd->endOfYear(); + break; + } + + return $currentEnd; + } + + public function periodShow(Carbon $date, $repeatFrequency) + { + switch ($repeatFrequency) { + default: + throw new FireflyException('No date formats for frequency "' . $repeatFrequency . '"!'); + break; + case 'daily': + return $date->format('j F Y'); + break; + case 'week': + case 'weekly': + return $date->format('\W\e\e\k W, Y'); + break; + case 'quarter': + return $date->format('F Y'); + break; + case 'monthly': + case 'month': + return $date->format('F Y'); + break; + case 'year': + case 'yearly': + return $date->format('Y'); + break; + } + } + + /** + * @param Carbon $theDate + * @param $repeatFreq + * + * @return Carbon + * @throws FireflyException + */ + public function startOfPeriod(Carbon $theDate, $repeatFreq) + { + $date = clone $theDate; + switch ($repeatFreq) { + default: + throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq); + break; + case 'daily': + $date->startOfDay(); + break; + case 'week': + case 'weekly': + $date->startOfWeek(); + break; + case 'month': + case 'monthly': + $date->startOfMonth(); + break; + case 'quarter': + case 'quarterly': + $date->firstOfQuarter(); + break; + case 'half-year': + $month = intval($date->format('m')); + $date->startOfYear(); + if ($month >= 7) { + $date->addMonths(6); + } + break; + case 'year': + case 'yearly': + $date->startOfYear(); + break; + } + + return $date; + } + + /** + * @param Carbon $date + * @param $repeatFreq + * @param int $subtract + * + * @return Carbon + * @throws FireflyException + */ + public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1) + { + $date = clone $theDate; + switch ($repeatFreq) { + default: + throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq); + break; + case 'daily': + $date->subDays($subtract); + break; + case 'weekly': + $date->subWeeks($subtract); + break; + case 'monthly': + $date->subMonths($subtract); + break; + case 'quarter': + case 'quarterly': + $months = $subtract * 3; + $date->subMonths($months); + break; + case 'half-year': + $months = $subtract * 6; + $date->subMonths($months); + break; + case 'year': + case 'yearly': + $date->subYears($subtract); + break; + } + + return $date; + } +} + diff --git a/app/lib/FireflyIII/Shared/Toolkit/Filter.php b/app/lib/FireflyIII/Shared/Toolkit/Filter.php new file mode 100644 index 0000000000..f9051252d2 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Toolkit/Filter.php @@ -0,0 +1,296 @@ +setSessionRangeValue(); + $start = \Session::has('start') ? \Session::get('start') : new Carbon; + + /* + * Force start date to at the start of the $range. + * Ie. the start of the week, month, year. This also to protect against nefarious users + * who change their session data (I just wanted to use the word "nefarious"). + */ + $start = $this->updateStartDate($range, $start); + + /* + * Force end date to at the END of the $range. Always based on $start. + * Ie. the END of the week, month, year. + */ + $end = $this->updateEndDate($range, $start); + #\Log::debug('After update, session end is : ' . $end->format('Y-m-d')); + + /* + * get the name of the month, depending on the range. Purely for astetics + */ + $period = $this->periodName($range, $start); + + /* + * Get the date for the previous and next period. + * Ie. next week, next month, etc. + */ + $prev = $this->previous($range, clone $start); + $next = $this->next($range, clone $start); + + /* + * Save everything in the session: + */ + \Session::put('start', $start); + \Session::put('end', $end); + \Session::put('range', $range); + \Session::put('period', $period); + \Session::put('prev', $this->periodName($range, $prev)); + \Session::put('next', $this->periodName($range, $next)); + + return null; + + } + + /** + * Checks and sets the currently set 'range' or defaults to a session + * and if that fails, defaults to 1M. Always returns the final value. + * + * @return string + */ + public function setSessionRangeValue() + { + if (!is_null(\Session::get('range'))) { + $range = \Session::get('range'); + } else { + /** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */ + $preferences = \App::make('FireflyIII\Shared\Preferences\PreferencesInterface'); + $viewRange = $preferences->get('viewRange', '1M'); + + // default range: + $range = $viewRange->data; + \Session::put('range', $range); + } + + return $range; + + } + + /** + * @param $range + * @param Carbon $start + * + * @return Carbon + * @throws FireflyException + */ + protected function updateStartDate($range, Carbon $start) + { + switch ($range) { + default: + throw new FireflyException('updateStartDate cannot handle $range ' . $range); + break; + case '1D': + $start->startOfDay(); + break; + case '1W': + $start->startOfWeek(); + break; + case '1M': + $start->startOfMonth(); + break; + case '3M': + $start->firstOfQuarter(); + break; + case '6M': + if (intval($start->format('m')) >= 7) { + $start->startOfYear()->addMonths(6); + } else { + $start->startOfYear(); + } + break; + case '1Y': + $start->startOfYear(); + break; + } + + return $start; + + } + + /** + * @param $range + * @param Carbon $start + * + * @return Carbon + * @throws FireflyException + */ + protected function updateEndDate($range, Carbon $start) + { + $end = clone $start; + switch ($range) { + default: + throw new FireflyException('updateEndDate cannot handle $range ' . $range); + break; + case '1D': + $end->endOfDay(); + break; + case '1W': + $end->endOfWeek(); + break; + case '1M': + $end->endOfMonth(); + break; + case '3M': + $end->lastOfQuarter(); + break; + case '6M': + if (intval($start->format('m')) >= 7) { + $end->endOfYear(); + } else { + $end->startOfYear()->addMonths(6); + } + break; + case '1Y': + $end->endOfYear(); + break; + + } + + return $end; + } + + /** + * @param $range + * @param Carbon $date + * + * @return string + * @throws FireflyException + */ + protected function periodName($range, Carbon $date) + { + switch ($range) { + 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; + + + } + } + + /** + * @param $range + * @param Carbon $date + * + * @return Carbon + * @throws FireflyException + */ + public function previous($range, Carbon $date) + { + switch ($range) { + default: + throw new FireflyException('Cannot do _previous() on ' . $range); + break; + case '1D': + $date->startOfDay()->subDay(); + break; + case '1W': + $date->startOfWeek()->subWeek(); + break; + case '1M': + $date->startOfMonth()->subMonth(); + break; + case '3M': + $date->firstOfQuarter()->subMonths(3)->firstOfQuarter(); + break; + case '6M': + $month = intval($date->format('m')); + if ($month <= 6) { + $date->startOfYear()->subMonths(6); + } else { + $date->startOfYear(); + } + break; + case '1Y': + $date->startOfYear()->subYear(); + break; + + } + + return $date; + } + + /** + * @param $range + * @param Carbon $date + * + * @return Carbon + * @throws FireflyException + */ + public function next($range, Carbon $date) + { + switch ($range) { + case '1D': + $date->endOfDay()->addDay(); + break; + case '1W': + $date->endOfWeek()->addDay()->startOfWeek(); + break; + case '1M': + $date->endOfMonth()->addDay()->startOfMonth(); + break; + case '3M': + $date->lastOfQuarter()->addDay(); + break; + case '6M': + if (intval($date->format('m')) >= 7) { + $date->startOfYear()->addYear(); + } else { + $date->startOfYear()->addMonths(6); + } + break; + case '1Y': + $date->startOfYear()->addYear(); + break; + default: + throw new FireflyException('Cannot do _next() on ' . $range); + break; + } + + return $date; + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Toolkit/Form.php b/app/lib/FireflyIII/Shared/Toolkit/Form.php index 7383a0e7a2..2fa690a427 100644 --- a/app/lib/FireflyIII/Shared/Toolkit/Form.php +++ b/app/lib/FireflyIII/Shared/Toolkit/Form.php @@ -9,12 +9,13 @@ use Illuminate\Support\Collection; * * @package FireflyIII\Shared\Toolkit */ -class Form { +class Form +{ /** * 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 */ @@ -44,6 +45,7 @@ class Form { } $selectList[$id] = $title; } + return $selectList; } diff --git a/app/lib/FireflyIII/Shared/Toolkit/Navigation.php b/app/lib/FireflyIII/Shared/Toolkit/Navigation.php new file mode 100644 index 0000000000..701bfead28 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Toolkit/Navigation.php @@ -0,0 +1,67 @@ +setSessionRangeValue(); + $start = \Session::get('start'); + + /* + * Add some period to $start. + */ + $next = $filter->next($range, clone $start); + + /* + * Save in session: + */ + \Session::put('start', $next); + + return true; + } + + /** + * @return bool + * @throws FireflyException + */ + public function prev() + { + /* + * Get the start date and the range from the session + */ + $filter = new Filter; + + $range = $filter->setSessionRangeValue(); + $start = \Session::get('start'); + + /* + * Substract some period to $start. + */ + $prev = $filter->previous($range, clone $start); + + /* + * Save in session: + */ + \Session::put('start', $prev); + + return true; + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Toolkit/Reminders.php b/app/lib/FireflyIII/Shared/Toolkit/Reminders.php new file mode 100644 index 0000000000..ce330b1d42 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Toolkit/Reminders.php @@ -0,0 +1,123 @@ +remindersable)) { + + case 'Piggybank': + $start = new Carbon; + $end = !is_null($reminder->remindersable->targetdate) ? clone $reminder->remindersable->targetdate : new Carbon; + $reminders = 0; + while ($start <= $end) { + $reminders++; + $start = \DateKit::addPeriod($start, $reminder->remindersable->reminder, $reminder->remindersable->reminder_skip); + } + /* + * Now find amount yet to save. + */ + $repetition = $reminder->remindersable->currentRelevantRep(); + $leftToSave = floatval($reminder->remindersable->targetamount) - floatval($repetition->currentamount); + $reminders = $reminders == 0 ? 1 : $reminders; + + return $leftToSave / $reminders; + break; + default: + throw new FireflyException('Cannot handle class ' . get_class($reminder->remindersable) . ' in amountForReminder.'); + break; + } + } + + /** + * + */ + public function getReminders() + { + $reminders = \Auth::user()->reminders() + ->where('active', 1) + ->where('startdate', '<=', Carbon::now()->format('Y-m-d')) + ->where('enddate', '>=', Carbon::now()->format('Y-m-d')) + ->get(); + + return $reminders; + } + + public function updateReminders() + { + + /* + * Reminder capable objects are (so far) only piggy banks. + */ + /** @var \FireflyIII\Database\Piggybank $repository */ + $repository = \App::make('FireflyIII\Database\Piggybank'); + + /** @var \FireflyIII\Database\Piggybank $repeatedRepository */ + $repeatedRepository = \App::make('FireflyIII\Database\RepeatedExpense'); + + /** @var Collection $piggybanks */ + $piggybanks = $repository->get()->merge($repeatedRepository->get()); + + + $set = $piggybanks->filter( + function (\Piggybank $piggybank) { + if (!is_null($piggybank->reminder)) { + return $piggybank; + } + return null; + } + ); + $today = Carbon::now(); + //$today = new Carbon('14-12-2014'); + + /** @var \Piggybank $piggybank */ + foreach ($set as $piggybank) { + /* + * Try to find a reminder that is valid in the current [period] + * aka between [start of period] and [end of period] as denoted + * by the piggy's repeat_freq. + */ + /** @var \PiggybankRepetition $repetition */ + $repetition = $piggybank->currentRelevantRep(); + $start = \DateKit::startOfPeriod($today, $piggybank->reminder); + if ($repetition->targetdate && $repetition->targetdate <= $today) { + // break when no longer relevant: + continue; + } + $end = \DateKit::endOfPeriod(clone $start, $piggybank->reminder); + // should have a reminder for this period: + /** @var Collection $reminders */ + $reminders = $piggybank->reminders()->dateIs($start, $end)->get(); + if ($reminders->count() == 0) { + // create new! + $reminder = new \Reminder; + $reminder->startdate = $start; + $reminder->enddate = $end; + $reminder->active = 1; + $reminder->user()->associate($repository->getUser()); + $reminder->remindersable_id= $piggybank->id; + $reminder->remindersable_type = 'Piggybank'; + $reminder->save(); + } + } + } +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Toolkit/Steam.php b/app/lib/FireflyIII/Shared/Toolkit/Steam.php new file mode 100644 index 0000000000..9da5fc7b40 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Toolkit/Steam.php @@ -0,0 +1,63 @@ +id . '.latestBalance')) { + + return \Cache::get('account.' . $account->id . '.latestBalance'); + } + } + $date = is_null($date) ? Carbon::now() : $date; + $balance = floatval( + $account->transactions()->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' + )->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount') + ); + if ($latest === true) { + \Cache::forever('account.' . $account->id . '.latestBalance', $balance); + } + + return $balance; + } + + /** + * @param \Piggybank $piggyBank + * @param \PiggybankRepetition $repetition + * + * @return int + */ + public function percentage(\Piggybank $piggyBank, \PiggybankRepetition $repetition) + { + $pct = $repetition->currentamount / $piggyBank->targetamount * 100; + if ($pct > 100) { + return 100; + } else { + return floor($pct); + } + } + +} \ No newline at end of file diff --git a/app/lib/FireflyIII/Shared/Validation/FireflyValidator.php b/app/lib/FireflyIII/Shared/Validation/FireflyValidator.php new file mode 100644 index 0000000000..1f26103651 --- /dev/null +++ b/app/lib/FireflyIII/Shared/Validation/FireflyValidator.php @@ -0,0 +1,22 @@ +app->validator->resolver( + function ($translator, $data, $rules, $messages) { + return new FireflyValidator($translator, $data, $rules, $messages); + } + ); + } + + public function register() + { + } +} \ No newline at end of file diff --git a/app/models/Account.php b/app/models/Account.php index 2346f5501d..96c07b06f0 100644 --- a/app/models/Account.php +++ b/app/models/Account.php @@ -1,88 +1,32 @@ ['required', 'between:1,100', 'alphabasic'], + = [ + 'name' => ['required', 'between:1,100'], 'user_id' => 'required|exists:users,id', 'account_type_id' => 'required|exists:account_types,id', 'active' => 'required|boolean' ]; - + protected $dates = ['deleted_at', 'created_at', 'updated_at']; + /** + * Fillable fields. + * + * @var array + */ protected $fillable = ['name', 'user_id', 'account_type_id', 'active']; - /** - * Get an accounts current balance. - * - * @param \Carbon\Carbon $date - * - * @return float - */ - public function balance(\Carbon\Carbon $date = null) - { - $date = is_null($date) ? new \Carbon\Carbon : $date; - - return floatval( - $this->transactions() - ->leftJoin( - 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' - ) - ->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount') - ); - } - - /** - * @param TransactionJournal $journal - * - * @return float - */ - public function balanceBeforeJournal(TransactionJournal $journal) - { - return floatval( - $this->transactions() - ->leftJoin( - 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' - ) - ->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d')) - ->where('transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s')) - ->where('transaction_journals.id', '!=', $journal->id) - ->sum('transactions.amount') - ); - } - /** * Account type. * @@ -94,13 +38,21 @@ class Account extends Ardent } /** - * Transactions. * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @param $fieldName + * + * @return mixed */ - public function transactions() + public function getMeta($fieldName) { - return $this->hasMany('Transaction'); + foreach ($this->accountMeta as $meta) { + if ($meta->name == $fieldName) { + return $meta->data; + } + } + + return null; + } /** @@ -112,28 +64,7 @@ class Account extends Ardent } /** - * @param \Carbon\Carbon $date * - * @return null - */ - public function predict( - /** @noinspection PhpUnusedParameterInspection */ - \Carbon\Carbon $date - ) { - return null; - } - - /** - * User - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo('User'); - } - - /** * @param Builder $query * @param array $types */ @@ -146,5 +77,60 @@ class Account extends Ardent $query->whereIn('account_types.type', $types); } + /** + * + * @param Builder $query + */ + public function scopeWithMeta(Builder $query) + { + $query->with(['accountmeta']); + } + + /** + * Transactions. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function transactions() + { + return $this->hasMany('Transaction'); + } + + public function updateMeta($fieldName, $fieldValue) + { + $meta = $this->accountMeta()->get(); + /** @var AccountMeta $entry */ + foreach ($meta as $entry) { + if ($entry->name == $fieldName) { + $entry->data = $fieldValue; + $entry->save(); + + return $entry; + } + } + $meta = new AccountMeta; + $meta->account()->associate($this); + $meta->name = $fieldName; + $meta->data = $fieldValue; + $meta->save(); + + return $meta; + } + + public function accountMeta() + { + return $this->hasMany('AccountMeta'); + } + + /** + * User + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('User'); + } + } \ No newline at end of file diff --git a/app/models/AccountMeta.php b/app/models/AccountMeta.php index 12b09102b9..78dbc159dd 100644 --- a/app/models/AccountMeta.php +++ b/app/models/AccountMeta.php @@ -1,13 +1,9 @@ 'required|between:1,250', 'data' => 'required' ]; - /** * @var array */ protected $fillable = ['account_id', 'name', 'date']; - + protected $table = 'account_meta'; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -32,4 +27,22 @@ class AccountMeta extends Ardent return $this->belongsTo('Account'); } + /** + * @param $value + * + * @return mixed + */ + public function getDataAttribute($value) + { + return json_decode($value); + } + + /** + * @param $value + */ + public function setDataAttribute($value) + { + $this->attributes['data'] = json_encode($value); + } + } \ No newline at end of file diff --git a/app/models/AccountType.php b/app/models/AccountType.php index 77a4ec8585..b5857f1fc2 100644 --- a/app/models/AccountType.php +++ b/app/models/AccountType.php @@ -1,24 +1,11 @@ ['required', 'between:1,50', 'alphabasic'], diff --git a/app/models/Budget.php b/app/models/Budget.php index 18bf3d069c..ba7ca58652 100644 --- a/app/models/Budget.php +++ b/app/models/Budget.php @@ -1,29 +1,14 @@ hasManyThrough('LimitRepetition', 'Limit', 'component_id'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -32,12 +17,5 @@ class Budget extends Component return $this->hasMany('Limit', 'component_id'); } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|\TransactionJournal - */ - public function transactionjournals() - { - return $this->belongsToMany('TransactionJournal', 'component_transaction_journal', 'component_id'); - } } \ No newline at end of file diff --git a/app/models/Category.php b/app/models/Category.php index 2469907cdc..50dda8a466 100644 --- a/app/models/Category.php +++ b/app/models/Category.php @@ -1,34 +1,8 @@ belongsToMany('TransactionJournal', 'component_transaction_journal', 'component_id'); - } -} \ No newline at end of file + +} \ No newline at end of file diff --git a/app/models/Component.php b/app/models/Component.php index 9337fcced7..a67d457a53 100644 --- a/app/models/Component.php +++ b/app/models/Component.php @@ -1,46 +1,37 @@ 'exists:users,id|required', - 'name' => ['required', 'between:1,100','min:1', 'alphabasic'], + 'name' => 'required|between:1,100|alphabasic', 'class' => 'required', ]; - protected $table = 'components'; - protected $subclassField = 'class'; - protected $fillable = ['name','user_id']; - + protected $dates = ['deleted_at', 'created_at', 'updated_at']; + protected $fillable = ['name', 'user_id']; + protected $subclassField = 'class'; + protected $table = 'components'; + use SoftDeletingTrait, ValidatingTrait; /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @return Carbon */ - public function limits() + public function lastActionDate() { - return $this->hasMany('Limit'); + $transaction = $this->transactionjournals()->orderBy('updated_at', 'DESC')->first(); + if (is_null($transaction)) { + return null; + } + + return $transaction->date; } /** @@ -48,7 +39,7 @@ class Component extends SingleTableInheritanceEntity */ public function transactionjournals() { - return $this->belongsToMany('TransactionJournal'); + return $this->belongsToMany('TransactionJournal', 'component_transaction_journal', 'component_id'); } /** diff --git a/app/models/Importentry.php b/app/models/Importentry.php deleted file mode 100644 index 9bf832b31a..0000000000 --- a/app/models/Importentry.php +++ /dev/null @@ -1,29 +0,0 @@ -belongsTo('Importmap'); - } -} \ No newline at end of file diff --git a/app/models/Importmap.php b/app/models/Importmap.php deleted file mode 100644 index ab81ed683a..0000000000 --- a/app/models/Importmap.php +++ /dev/null @@ -1,52 +0,0 @@ - 'required|exists:users,id', - 'file' => 'required', - 'totaljobs' => 'numeric|required|min:0', - 'jobsdone' => 'numeric|required|min:0', - - ]; - - /** - * User - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo('User'); - } - - public function pct() - { - if ($this->jobsdone == 0 || $this->totaljobs == 0) { - return 0; - } else { - return round((($this->jobsdone / $this->totaljobs) * 100), 1); - } - } -} \ No newline at end of file diff --git a/app/models/Limit.php b/app/models/Limit.php index 23f893ed1e..a215c98623 100644 --- a/app/models/Limit.php +++ b/app/models/Limit.php @@ -2,34 +2,12 @@ use Carbon\Carbon; use Illuminate\Database\QueryException; -use LaravelBook\Ardent\Ardent as Ardent; +use Watson\Validating\ValidatingTrait; -/** - * Limit - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $component_id - * @property \Carbon\Carbon $startdate - * @property float $amount - * @property boolean $repeats - * @property string $repeat_freq - * @property-read \Budget $budget - * @property-read \Component $component - * @property-read \Illuminate\Database\Eloquent\Collection|\LimitRepetition[] $limitrepetitions - * @method static \Illuminate\Database\Query\Builder|\Limit whereId($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereComponentId($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereStartdate($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereAmount($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereRepeats($value) - * @method static \Illuminate\Database\Query\Builder|\Limit whereRepeatFreq($value) - */ -class Limit extends Ardent +class Limit extends Eloquent { + use ValidatingTrait; public static $rules = [ 'component_id' => 'required|exists:components,id', @@ -50,14 +28,7 @@ class Limit extends Ardent } /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function component() - { - return $this->belongsTo('Component', 'component_id'); - } - - /** + * TODO see if this method is still used. * Create a new repetition for this limit, starting on * the given date. * @@ -90,15 +61,15 @@ class Limit extends Ardent } $end->subDay(); $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')); + \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) { - $repetition = new \LimitRepetition(); + $repetition = new \LimitRepetition(); $repetition->startdate = $start; - $repetition->enddate = $end; - $repetition->amount = $this->amount; + $repetition->enddate = $end; + $repetition->amount = $this->amount; $repetition->limit()->associate($this); try { @@ -111,14 +82,16 @@ class Limit extends Ardent \Log::error($e->getMessage()); } if (isset($repetition->id)) { - \Event::fire('limits.repetition', [$repetition]); + \Event::fire('limits.repetition', [$repetition]); // not used, I guess? } - } 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(); + } 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(); + } } } diff --git a/app/models/LimitRepetition.php b/app/models/LimitRepetition.php index c61aed4364..07e9da433b 100644 --- a/app/models/LimitRepetition.php +++ b/app/models/LimitRepetition.php @@ -1,28 +1,11 @@ 'required|exists:limits,id', @@ -39,29 +22,6 @@ 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() - { - return floatval($this->amount - $this->spentInRepetition()); - - } - /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ @@ -71,6 +31,39 @@ class LimitRepetition extends Ardent } /** + * TODO see if this scope is still used. + * + * How much money is left in this? + */ + public function leftInRepetition() + { + return floatval($this->amount - $this->spentInRepetition()); + + } + + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @return float + */ + 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); + } + + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * * Returns a string used to sort this particular repetition * based on the date and period it falls into. Ie. the limit * repeats monthly and the start date is 12 dec 2012, this will @@ -83,10 +76,7 @@ class LimitRepetition extends Ardent } switch ($this->repeat_freq) { default: - throw new \Firefly\Exception\FireflyException( - 'No date formats for frequency "' . $this->repeat_freq - . '"!' - ); + throw new FireflyException('No date formats for frequency "' . $this->repeat_freq . '"!'); break; case 'daily': return $this->startdate->format('Ymd') . '-5'; @@ -109,39 +99,4 @@ class LimitRepetition extends Ardent } } - /** - * Same as above, just with a more natural view. So "March 2012". - */ - public function periodShow() - { - if (is_null($this->repeat_freq)) { - $this->repeat_freq = $this->limit->repeat_freq; - } - switch ($this->repeat_freq) { - default: - throw new \Firefly\Exception\FireflyException( - 'No date formats for frequency "' . $this->repeat_freq - . '"!' - ); - break; - case 'daily': - return $this->startdate->format('j F Y'); - break; - case 'weekly': - return $this->startdate->format('\W\e\e\k W, Y'); - break; - case 'monthly': - return $this->startdate->format('F Y'); - break; - case 'half-year': - case 'quarterly': - return $this->startdate->format('M Y') . ' - ' . $this->enddate->format('M Y'); - break; - case 'yearly': - return $this->startdate->format('Y'); - break; - } - } - - } \ No newline at end of file diff --git a/app/models/Piggybank.php b/app/models/Piggybank.php index c364bcf0d2..a35f60e67d 100644 --- a/app/models/Piggybank.php +++ b/app/models/Piggybank.php @@ -1,81 +1,27 @@ 'required|exists:accounts,id', // link to Account - 'name' => 'required|between:1,255', // name - 'targetamount' => 'required|min:0', // amount you want to save - 'startdate' => 'date', // when you started - 'targetdate' => 'date', // when its due - 'repeats' => 'required|boolean', // does it repeat? - 'rep_length' => 'in:day,week,month,year', // how long is the period? - 'rep_every' => 'required|min:1|max:100', // how often does it repeat? every 3 years. - '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 - = [ - 'account_id', - 'name', - 'targetamount', - 'startdate', - 'targetdate', - 'repeats', - 'rep_length', - 'rep_every', - 'rep_times', - 'reminder', - 'reminder_skip', - 'remind_me', - 'order' + = ['account_id' => 'required|exists:accounts,id', // link to Account + 'name' => 'required|between:1,255', // name + 'targetamount' => 'required|min:0', // amount you want to save + 'startdate' => 'date', // when you started + 'targetdate' => 'date', // when its due + 'repeats' => 'required|boolean', // does it repeat? + 'rep_length' => 'in:day,week,month,quarter,year', // how long is the period? + 'rep_every' => 'required|min:1|max:100', // how often does it repeat? every 3 years. + 'rep_times' => 'min:1|max:100', // how many times do you want to save this amount? eg. 3 times + 'reminder' => 'in:day,week,quarter,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 + = ['account_id', 'name', 'targetamount', 'startdate', 'targetdate', 'repeats', 'rep_length', 'rep_every', 'rep_times', 'reminder', 'reminder_skip', + 'remind_me', 'order']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -85,63 +31,102 @@ class Piggybank extends Ardent return $this->belongsTo('Account'); } + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @return int + */ + public function amountPerReminder() + { + return 0; + + } + + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @return int + */ + public function countFutureReminders() + { + return 0; + } + + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @param Carbon $start + * @param Carbon $target + * + * @return PiggybankRepetition + */ public function createRepetition(Carbon $start = null, Carbon $target = null) { $rep = new \PiggybankRepetition; $rep->piggybank()->associate($this); - $rep->startdate = $start; - $rep->targetdate = $target; + $rep->startdate = $start; + $rep->targetdate = $target; $rep->currentamount = 0; $rep->save(); - \Event::fire('piggybanks.repetition', [$rep]); return $rep; } /** - * @return array - */ - public function getDates() - { - return ['created_at', 'updated_at', 'targetdate', 'startdate']; - } - - /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * * Grabs the PiggyBankRepetition that's currently relevant / active * * @returns \PiggybankRepetition */ public function currentRelevantRep() { - $query = $this->piggybankrepetitions() - ->where( + if ($this->currentRep) { + return $this->currentRep; + } + if ($this->repeats == 0) { + $rep = $this->piggybankrepetitions()->first(['piggybank_repetitions.*']); + $this->currentRep = $rep; + + return $rep; + } else { + $query = $this->piggybankrepetitions()->where( function ($q) { $q->where( function ($q) { - $today = new Carbon; - $q->whereNull('startdate'); - $q->orWhere('startdate', '<=', $today->format('Y-m-d')); - } - ) - ->where( - function ($q) { - $today = new Carbon; - $q->whereNull('targetdate'); - $q->orWhere('targetdate', '>=', $today->format('Y-m-d')); - } - ); - } - )->orWhere( - function ($q) { - $today = new Carbon; - $q->where('startdate', '>=', $today->format('Y-m-d')); - $q->where('targetdate', '>=', $today->format('Y-m-d')); - } - )->orderBy('startdate', 'ASC'); - $result = $query->first(); - return $result; + $q->where( + function ($q) { + $today = new Carbon; + $q->whereNull('startdate'); + $q->orWhere('startdate', '<=', $today->format('Y-m-d')); + } + )->where( + function ($q) { + $today = new Carbon; + $q->whereNull('targetdate'); + $q->orWhere('targetdate', '>=', $today->format('Y-m-d')); + } + ); + } + )->orWhere( + function ($q) { + $today = new Carbon; + $q->where('startdate', '>=', $today->format('Y-m-d')); + $q->where('targetdate', '>=', $today->format('Y-m-d')); + } + ); + + } + ) + ->orderBy('startdate', 'ASC'); + $result = $query->first(['piggybank_repetitions.*']); + $this->currentRep = $result; + \Log::debug('Found relevant rep in currentRelevantRep(): ' . $result->id); + + return $result; + } } @@ -154,6 +139,14 @@ class Piggybank extends Ardent return $this->hasMany('PiggybankRepetition'); } + /** + * @return array + */ + public function getDates() + { + return ['created_at', 'updated_at', 'targetdate', 'startdate']; + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -162,7 +155,14 @@ class Piggybank extends Ardent return $this->hasMany('PiggybankEvent'); } + public function reminders() + { + return $this->morphMany('Reminder', 'remindersable'); + } + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * * Same but for specific date. * * @param Carbon $date @@ -171,29 +171,27 @@ class Piggybank extends Ardent */ public function repetitionForDate(Carbon $date) { - $query = $this->piggybankrepetitions() - ->where( - function ($q) use ($date) { + $query = $this->piggybankrepetitions()->where( + function ($q) use ($date) { - $q->where( - function ($q) use ($date) { - $q->whereNull('startdate'); - $q->orWhere('startdate', '<=', $date->format('Y-m-d')); - } - ) - ->where( - function ($q) use ($date) { - $q->whereNull('targetdate'); - $q->orWhere('targetdate', '>=', $date->format('Y-m-d')); - } - ); - } - )->orWhere( - function ($q) use ($date) { - $q->where('startdate', '>=', $date->format('Y-m-d')); - $q->where('targetdate', '>=', $date->format('Y-m-d')); - } - )->orderBy('startdate', 'ASC'); + $q->where( + function ($q) use ($date) { + $q->whereNull('startdate'); + $q->orWhere('startdate', '<=', $date->format('Y-m-d')); + } + )->where( + function ($q) use ($date) { + $q->whereNull('targetdate'); + $q->orWhere('targetdate', '>=', $date->format('Y-m-d')); + } + ); + } + )->orWhere( + function ($q) use ($date) { + $q->where('startdate', '>=', $date->format('Y-m-d')); + $q->where('targetdate', '>=', $date->format('Y-m-d')); + } + )->orderBy('startdate', 'ASC'); $result = $query->first(); return $result; diff --git a/app/models/PiggybankEvent.php b/app/models/PiggybankEvent.php index 5c6cc88fb7..ecb85a8654 100644 --- a/app/models/PiggybankEvent.php +++ b/app/models/PiggybankEvent.php @@ -1,27 +1,10 @@ 'required|exists:piggybanks,id', @@ -45,4 +28,12 @@ class PiggybankEvent extends Ardent return $this->belongsTo('Piggybank'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function transactionJournal() + { + return $this->belongsTo('TransactionJournal'); + } + } \ No newline at end of file diff --git a/app/models/PiggybankRepetition.php b/app/models/PiggybankRepetition.php index 9f66ef2820..a7cfe38bb5 100644 --- a/app/models/PiggybankRepetition.php +++ b/app/models/PiggybankRepetition.php @@ -1,34 +1,17 @@ 'required|exists:piggybanks,id', 'targetdate' => 'date', 'startdate' => 'date', - 'currentamount' => 'required|numeric' - ]; + 'currentamount' => 'required|numeric']; /** * @return array @@ -38,6 +21,11 @@ class PiggybankRepetition extends Ardent return ['created_at', 'updated_at', 'targetdate', 'startdate']; } + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @return float|int + */ public function pct() { $total = $this->piggybank->targetamount; @@ -58,5 +46,15 @@ class PiggybankRepetition extends Ardent return $this->belongsTo('Piggybank'); } + public function scopeStarts(Builder $query, Carbon $date) + { + $query->where('startdate', $date->format('Y-m-d')); + } + + public function scopeTargets(Builder $query, Carbon $date) + { + $query->where('targetdate', $date->format('Y-m-d')); + } + } \ No newline at end of file diff --git a/app/models/Preference.php b/app/models/Preference.php index ba8ac43bf9..0f3c9700cf 100644 --- a/app/models/Preference.php +++ b/app/models/Preference.php @@ -1,48 +1,11 @@ 'required|exists:users,id', - 'name' => 'required|between:1,255', - 'data' => 'required' - ]; - - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo('User'); - } - - /** - * @param $value - */ - public function setDataAttribute($value) - { - $this->attributes['data'] = json_encode($value); - } + = ['user_id' => 'required|exists:users,id', 'name' => 'required|between:1,255', 'data' => 'required']; /** * @param $value @@ -54,4 +17,20 @@ class Preference extends Ardent return json_decode($value); } + /** + * @param $value + */ + public function setDataAttribute($value) + { + $this->attributes['data'] = json_encode($value); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('User'); + } + } \ No newline at end of file diff --git a/app/models/RecurringTransaction.php b/app/models/RecurringTransaction.php index c2781e1252..b3f5c4e9ae 100644 --- a/app/models/RecurringTransaction.php +++ b/app/models/RecurringTransaction.php @@ -1,41 +1,11 @@ 'required|exists:users,id', @@ -47,10 +17,9 @@ class RecurringTransaction extends Ardent 'active' => 'required|between:0,1', 'automatch' => 'required|between:0,1', 'repeat_freq' => 'required|in:daily,weekly,monthly,quarterly,half-year,yearly', - 'skip' => 'required|between:0,31', - ]; + 'skip' => 'required|between:0,31',]; - protected $fillable = ['user_id','name','match','amount_min','amount_max','date','repeat_freq','skip','active','automatch']; + protected $fillable = ['user_id', 'name', 'match', 'amount_min', 'amount_max', 'date', 'repeat_freq', 'skip', 'active', 'automatch']; /** * @return array @@ -60,44 +29,82 @@ class RecurringTransaction extends Ardent return ['created_at', 'updated_at', 'date']; } + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * @return null + */ + public function lastFoundMatch() + { + $last = $this->transactionjournals()->orderBy('date', 'DESC')->first(); + if ($last) { + return $last->date; + } + + return null; + } /** - * @return Carbon + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function next() + public function transactionjournals() { - $today = new Carbon; - $start = clone $this->date; - $skip = $this->skip == 0 ? 1 : $this->skip; - if ($today < $start) { - return $start; - } + return $this->hasMany('TransactionJournal'); + } - while ($start <= $this->date) { - switch ($this->repeat_freq) { - case 'daily': - $start->addDays($skip); - break; - case 'weekly': - $start->addWeeks($skip); - break; - case 'monthly': - $start->addMonths($skip); - break; - case 'quarterly': - $start->addMonths($skip * 3); - break; - case 'half-year': - $start->addMonths($skip * 6); - break; - case 'yearly': - $start->addYears($skip); - break; + /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * + * Find the next expected match based on the set journals and the date stuff from the recurring + * transaction. + */ + public function nextExpectedMatch() + { + + /* + * The date Firefly tries to find. If this stays null, it's "unknown". + */ + $finalDate = null; + + /* + * $today is the start of the next period, to make sure FF3 won't miss anything + * when the current period has a transaction journal. + */ + $today = DateKit::addPeriod(new Carbon, $this->repeat_freq, 0); + + /* + * FF3 loops from the $start of the recurring transaction, and to make sure + * $skip works, it adds one (for modulo). + */ + $skip = $this->skip + 1; + $start = DateKit::startOfPeriod(new Carbon, $this->repeat_freq); + /* + * go back exactly one month/week/etc because FF3 does not care about 'next' + * recurring transactions if they're too far into the past. + */ + // echo 'Repeat freq is: ' . $recurringTransaction->repeat_freq . '
'; + + // echo 'Start: ' . $start . '
'; + + $counter = 0; + while ($start <= $today) { + if (($counter % $skip) == 0) { + // do something. + $end = DateKit::endOfPeriod(clone $start, $this->repeat_freq); + $journalCount = $this->transactionjournals()->before($end)->after($start)->count(); + if ($journalCount == 0) { + $finalDate = clone $start; + break; + } } + + // add period for next round! + $start = DateKit::addPeriod($start, $this->repeat_freq, 0); + $counter++; } - return $start; + return $finalDate; } /** diff --git a/app/models/Reminder.php b/app/models/Reminder.php index c2fb74936c..8b61d63db2 100644 --- a/app/models/Reminder.php +++ b/app/models/Reminder.php @@ -1,34 +1,18 @@ morphTo(); + } + + public function scopeDateIs($query, Carbon $start, Carbon $end) + { + return $query->where('startdate', $start->format('Y-m-d'))->where('enddate', $end->format('Y-m-d')); + } + + /** + * @param $value + */ + public function setDataAttribute($value) + { + $this->attributes['data'] = json_encode($value); + } + /** * User * @@ -47,4 +54,6 @@ class Reminder extends Ardent { return $this->belongsTo('User'); } + + } \ No newline at end of file diff --git a/app/models/Transaction.php b/app/models/Transaction.php index c09704ac54..6e549cee8b 100644 --- a/app/models/Transaction.php +++ b/app/models/Transaction.php @@ -1,52 +1,19 @@ 'numeric|required|exists:accounts,id', - 'piggybank_id' => 'numeric|exists:piggybanks,id', - 'transaction_journal_id' => 'numeric|required|exists:transaction_journals,id', - 'description' => 'between:1,255', - 'amount' => 'required|between:-65536,65536|not_in:0,0.00', - ]; + = ['account_id' => 'numeric|required|exists:accounts,id', + 'piggybank_id' => 'numeric|exists:piggybanks,id', + 'transaction_journal_id' => 'numeric|required|exists:transaction_journals,id', + 'description' => 'between:1,255', + 'amount' => 'required|between:-65536,65536|not_in:0,0.00',]; /** @@ -57,35 +24,6 @@ class Transaction extends Ardent return $this->belongsTo('Account'); } - /** - * @param Piggybank $piggybank - * - * @return bool - */ - public function connectPiggybank(\Piggybank $piggybank = null) - { - if (is_null($piggybank)) { - return true; - } - /** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */ - $piggyRepository = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface'); - if ($this->account_id == $piggybank->account_id) { - $this->piggybank()->associate($piggybank); - $this->save(); - \Event::fire('piggybanks.createRelatedTransfer', [$piggybank, $this->transactionJournal, $this]); - return true; - } - return false; - } - - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function piggybank() - { - return $this->belongsTo('Piggybank'); - } - /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ @@ -110,6 +48,14 @@ class Transaction extends Ardent return $this->belongsToMany('Component'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function piggybank() + { + return $this->belongsTo('Piggybank'); + } + public function scopeAccountIs(Builder $query, Account $account) { $query->where('transactions.account_id', $account->id); @@ -119,8 +65,7 @@ class Transaction extends Ardent { if (is_null($this->joinedJournals)) { $query->leftJoin( - 'transaction_journals', 'transaction_journals.id', '=', - 'transactions.transaction_journal_id' + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' ); $this->joinedJournals = true; } @@ -131,8 +76,7 @@ class Transaction extends Ardent { if (is_null($this->joinedJournals)) { $query->leftJoin( - 'transaction_journals', 'transaction_journals.id', '=', - 'transactions.transaction_journal_id' + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' ); $this->joinedJournals = true; } @@ -153,15 +97,13 @@ class Transaction extends Ardent { if (is_null($this->joinedJournals)) { $query->leftJoin( - 'transaction_journals', 'transaction_journals.id', '=', - 'transactions.transaction_journal_id' + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' ); $this->joinedJournals = true; } if (is_null($this->joinedTransactionTypes)) { $query->leftJoin( - 'transaction_types', 'transaction_types.id', '=', - 'transaction_journals.transaction_type_id' + 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' ); $this->joinedTransactionTypes = true; } diff --git a/app/models/TransactionCurrency.php b/app/models/TransactionCurrency.php index 23ee95ad1f..9fded03565 100644 --- a/app/models/TransactionCurrency.php +++ b/app/models/TransactionCurrency.php @@ -1,26 +1,17 @@ hasMany('TransactionJournal'); } diff --git a/app/models/TransactionGroup.php b/app/models/TransactionGroup.php new file mode 100644 index 0000000000..f689c97b49 --- /dev/null +++ b/app/models/TransactionGroup.php @@ -0,0 +1,41 @@ + 'required|in:balance' + ]; + + /** + * @return array + */ + public function getDates() + { + return ['created_at', 'updated_at']; + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function transactionjournals() + { + return $this->belongsToMany('TransactionJournal'); + } + + /** + * User + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('User'); + } + + +} \ No newline at end of file diff --git a/app/models/TransactionJournal.php b/app/models/TransactionJournal.php index 89513a0d46..61dd4424c9 100644 --- a/app/models/TransactionJournal.php +++ b/app/models/TransactionJournal.php @@ -1,64 +1,20 @@ 'required|exists:transaction_types,id', - 'transaction_currency_id' => 'required|exists:transaction_currencies,id', - 'description' => 'required|between:1,255', - 'date' => 'required|date', - 'completed' => 'required|between:0,1' - ]; - + = ['transaction_type_id' => 'required|exists:transaction_types,id', + 'transaction_currency_id' => 'required|exists:transaction_currencies,id', + 'description' => 'required|between:1,255', + 'date' => 'required|date', + 'completed' => 'required|between:0,1']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany @@ -89,6 +45,8 @@ class TransactionJournal extends Ardent } /** + * TODO remove this method in favour of something in the FireflyIII libraries. + * * @return float */ public function getAmount() @@ -99,15 +57,8 @@ class TransactionJournal extends Ardent return floatval($t->amount); } } - return -0.01; - } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function recurringTransaction() - { - return $this->belongsTo('RecurringTransaction'); + return -0.01; } /** @@ -118,6 +69,22 @@ class TransactionJournal extends Ardent return ['created_at', 'updated_at', 'date']; } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function piggybankevents() + { + return $this->hasMany('PiggybankEvent'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function recurringTransaction() + { + return $this->belongsTo('RecurringTransaction'); + } + /** * @param Builder $query * @param Account $account @@ -158,25 +125,11 @@ class TransactionJournal extends Ardent $query->orderBy('date', 'DESC')->orderBy('transaction_journals.id', 'DESC'); } - public function scopeMoreThan(Builder $query, $amount) - { - if (is_null($this->joinedTransactions)) { - $query->leftJoin( - 'transactions', 'transactions.transaction_journal_id', '=', - 'transaction_journals.id' - ); - $this->joinedTransactions = true; - } - - $query->where('transactions.amount', '>=', $amount); - } - public function scopeLessThan(Builder $query, $amount) { if (is_null($this->joinedTransactions)) { $query->leftJoin( - 'transactions', 'transactions.transaction_journal_id', '=', - 'transaction_journals.id' + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' ); $this->joinedTransactions = true; } @@ -184,6 +137,18 @@ class TransactionJournal extends Ardent $query->where('transactions.amount', '<=', $amount); } + public function scopeMoreThan(Builder $query, $amount) + { + if (is_null($this->joinedTransactions)) { + $query->leftJoin( + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' + ); + $this->joinedTransactions = true; + } + + $query->where('transactions.amount', '>=', $amount); + } + /** * @param $query * @param Carbon $date @@ -199,8 +164,7 @@ class TransactionJournal extends Ardent { if (is_null($this->joinedTransactionTypes)) { $query->leftJoin( - 'transaction_types', 'transaction_types.id', '=', - 'transaction_journals.transaction_type_id' + 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' ); $this->joinedTransactionTypes = true; } @@ -220,7 +184,7 @@ class TransactionJournal extends Ardent $q->orderBy('amount', 'ASC'); }, 'transactiontype', 'components' => function ($q) { $q->orderBy('class'); - }, 'transactions.account.accounttype', 'recurringTransaction'] + }, 'transactions.account.accounttype', 'recurringTransaction', 'budgets', 'categories'] ); } @@ -240,6 +204,11 @@ class TransactionJournal extends Ardent return $this->belongsTo('TransactionType'); } + public function transactiongroups() + { + return $this->belongsToMany('TransactionGroup'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ diff --git a/app/models/TransactionRelation.php b/app/models/TransactionRelation.php new file mode 100644 index 0000000000..93109b2490 --- /dev/null +++ b/app/models/TransactionRelation.php @@ -0,0 +1,6 @@ + 'required|email|unique:users,email', 'migrated' => 'required|boolean', 'password' => 'required|between:60,60', 'reset' => 'between:32,32', ]; - /** - * The database table used by the model. - * - * @var string - */ - protected $table = 'users'; + protected $fillable = ['email']; /** * The attributes excluded from the model's JSON form. * * @var array */ protected $hidden = ['remember_token']; + /** + * The database table used by the model. + * + * @var string + */ + protected $table = 'users'; /** * @return \Illuminate\Database\Eloquent\Relations\HasMany @@ -91,6 +65,11 @@ class User extends Ardent implements UserInterface, RemindableInterface return $this->hasMany('Component'); } + public function piggybanks() + { + return $this->hasManyThrough('Piggybank', 'Account'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -107,6 +86,14 @@ class User extends Ardent implements UserInterface, RemindableInterface return $this->hasMany('RecurringTransaction'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function reminders() + { + return $this->hasMany('Reminder'); + } + /** * @param $value */ @@ -115,11 +102,6 @@ class User extends Ardent implements UserInterface, RemindableInterface $this->attributes['password'] = Hash::make($value); } - public function piggybanks() - { - return $this->hasManyThrough('Piggybank', 'Account'); - } - /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ @@ -128,4 +110,9 @@ class User extends Ardent implements UserInterface, RemindableInterface return $this->hasMany('TransactionJournal'); } + public function transactions() + { + return $this->hasManyThrough('TransactionJournal', 'Transaction'); + } + } \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index 4d85b37703..cb18fcd3cf 100644 --- a/app/routes.php +++ b/app/routes.php @@ -1,11 +1,9 @@ where('account_types.editable', 1)->where('accounts.id', $value) @@ -26,6 +24,7 @@ Route::bind( 'user_id', Auth::user()->id )->first(); } + return null; } ); @@ -37,6 +36,7 @@ Route::bind( return RecurringTransaction:: where('id', $value)->where('user_id', Auth::user()->id)->first(); } + return null; } ); @@ -46,6 +46,18 @@ Route::bind( return Budget:: where('id', $value)->where('user_id', Auth::user()->id)->first(); } + + return null; + } +); + +Route::bind( + 'component', function ($value, $route) { + if (Auth::check()) { + return Component:: + where('id', $value)->where('user_id', Auth::user()->id)->first(); + } + return null; } ); @@ -56,6 +68,7 @@ Route::bind( return Reminder:: where('id', $value)->where('user_id', Auth::user()->id)->first(); } + return null; } ); @@ -66,6 +79,7 @@ Route::bind( return Category:: where('id', $value)->where('user_id', Auth::user()->id)->first(); } + return null; } ); @@ -76,18 +90,7 @@ Route::bind( return TransactionJournal:: where('id', $value)->where('user_id', Auth::user()->id)->first(); } - return null; - } -); -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.*']); - } return null; } ); @@ -100,6 +103,7 @@ Route::bind( 'components', 'components.id', '=', 'limits.component_id' )->where('components.class', 'Budget')->where('components.user_id', Auth::user()->id)->first(['limit_repetitions.*']); } + return null; } ); @@ -108,121 +112,104 @@ 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) + ->where('repeats', 0)->first(['piggybanks.*']); } + return null; } ); +Route::bind( + 'repeated', 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) + ->where('repeats', 1)->first(['piggybanks.*']); + } -// a development route: -Route::get('/dev', ['uses' => 'HomeController@jobDev']); + return null; + } +); // protected routes: Route::group( ['before' => 'auth'], function () { - // some date routes: + // some date routes used for (well duh) date-based navigation. 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/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']); + Route::get('/accounts/show/{account}/{view?}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']); + // budget controller: 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/date', ['uses' => 'BudgetController@indexByDate', 'as' => 'budgets.index.date']); - #Route::get('/budgets/budget', ['uses' => 'BudgetController@indexByBudget', 'as' => 'budgets.index.budget']); + Route::get('/budgets/income', ['uses' => 'BudgetController@updateIncome', 'as' => 'budgets.income']); # extra. Route::get('/budgets/create', ['uses' => 'BudgetController@create', 'as' => 'budgets.create']); - #Route::get('/budgets/nobudget/{period}', ['uses' => 'BudgetController@nobudget', 'as' => 'budgets.nobudget']); - 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/show/{budget}/{limitrepetition?}', ['uses' => 'BudgetController@show', 'as' => 'budgets.show']); // 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/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']); // 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/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']); + Route::get('/chart/sankey/{account}/out/{view?}', ['uses' => 'GoogleChartController@accountSankeyOutChart']); + Route::get('/chart/sankey/{account}/in/{view?}', ['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/recurring/{recurring}', ['uses' => 'GoogleChartController@recurringOverview']); Route::get('/chart/reports/budgets/{year}', ['uses' => 'GoogleChartController@budgetsReportChart']); - Route::get('/chart/budgets/{budget}/spending/{year}', ['uses' => 'GoogleChartController@budgetsAndSpending']); + Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']); + Route::get('/chart/piggyhistory/{piggybank}', ['uses' => 'GoogleChartController@piggyBankHistory']); - // 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']); + // google chart for components (categories + budgets combined) + Route::get('/chart/component/{component}/spending/{year}', ['uses' => 'GoogleChartController@componentsAndSpending']); - - 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']); - - // (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']); - Route::get('chart/budget/{budget}/session', ['uses' => 'ChartController@budgetSession', 'as' => 'chart.budget.session']); - Route::get('chart/budget/envelope/{limitrepetition}', ['uses' => 'ChartController@budgetLimit', 'as' => 'chart.budget.limit']); + // help controller + Route::get('/help/{route}', ['uses' => 'HelpController@show', 'as' => 'help.show']); // home controller Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']); - Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']); + Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']); # even though nothing is cached. - // JSON controller: + // JSON controller Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']); Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']); Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']); - Route::get('/json/expenses', ['uses' => 'JsonController@expenses', 'as' => 'json.expenses']); - 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('/migrate', ['uses' => 'MigrateController@index', 'as' => 'migrate.index']); // piggy bank controller 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/add/{piggybank}', ['uses' => 'PiggybankController@add']); # add money + Route::get('/piggybanks/remove/{piggybank}', ['uses' => 'PiggybankController@remove']); #remove money + Route::get('/piggybanks/create', ['uses' => 'PiggybankController@create', 'as' => 'piggybanks.create']); + Route::get('/piggybanks/edit/{piggybank}', ['uses' => 'PiggybankController@edit', 'as' => 'piggybanks.edit']); 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']); + Route::get('/piggybanks/show/{piggybank}', ['uses' => 'PiggybankController@show', 'as' => 'piggybanks.show']); // preferences controller Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']); @@ -233,34 +220,47 @@ Route::group( // 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/rescan/{recurring}', ['uses' => 'RecurringController@rescan', 'as' => 'recurring.rescan']); + Route::get('/recurring/rescan/{recurring}', ['uses' => 'RecurringController@rescan', 'as' => 'recurring.rescan']); # rescan for matching. 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']); + Route::get('/recurring/show/{recurring}', ['uses' => 'RecurringController@show', 'as' => 'recurring.show']); + + // repeated expenses controller: + Route::get('/repeatedexpenses', ['uses' => 'RepeatedExpenseController@index', 'as' => 'repeated.index']); + Route::get('/repeatedexpenses/create', ['uses' => 'RepeatedExpenseController@create', 'as' => 'repeated.create']); + Route::get('/repeatedexpenses/show/{repeated}', ['uses' => 'RepeatedExpenseController@show', 'as' => 'repeated.show']); // report controller: Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']); + Route::get('/reports/budgets/{year}/{month}', ['uses' => 'ReportController@budgets', 'as' => 'reports.budgets']); + Route::get('/reports/unbalanced/{year}/{month}', ['uses' => 'ReportController@unbalanced', 'as' => 'reports.unbalanced']); + + // reminder controller + Route::get('/reminders/{reminder}', ['uses' => 'ReminderController@show', 'as' => 'reminders.show']); + Route::get('/reminders/{reminder}/dismiss', ['uses' => 'ReminderController@dismiss', 'as' => 'reminders.dismiss']); + Route::get('/reminders/{reminder}/notnow', ['uses' => 'ReminderController@notnow', 'as' => 'reminders.notnow']); + Route::get('/reminders/{reminder}/act', ['uses' => 'ReminderController@act', 'as' => 'reminders.act']); // search controller: 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('/transactions/{what}', ['uses' => 'TransactionController@index', 'as' => 'transactions.index'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] + ); + Route::get('/transactions/create/{what}', ['uses' => 'TransactionController@create', 'as' => 'transactions.create'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] ); - 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('/transaction/show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'transactions.show']); + Route::get('/transaction/relate/{tj}', ['uses' => 'TransactionController@relate', 'as' => 'transactions.relate']); + Route::post('/transactions/relatedSearch/{tj}', ['uses' => 'TransactionController@relatedSearch', 'as' => 'transactions.relatedSearch']); + Route::post('/transactions/alreadyRelated/{tj}', ['uses' => 'TransactionController@alreadyRelated', 'as' => 'transactions.alreadyRelated']); + Route::post('/transactions/doRelate', ['uses' => 'TransactionController@doRelate', 'as' => 'transactions.doRelate']); + Route::any('/transactions/unrelate/{tj}', ['uses' => 'TransactionController@unrelate', 'as' => 'transactions.unrelate']); // user controller Route::get('/logout', ['uses' => 'UserController@logout', 'as' => 'logout']); @@ -281,8 +281,8 @@ Route::group( // budget controller: 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/update/{budget}', ['uses' => 'BudgetController@update', 'as' => 'budgets.update']); Route::post('/budgets/destroy/{budget}', ['uses' => 'BudgetController@destroy', 'as' => 'budgets.destroy']); // category controller @@ -290,23 +290,15 @@ Route::group( 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('/migrate/upload', ['uses' => 'MigrateController@upload', 'as' => 'migrate.upload']); - - // piggy bank controller 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']); + Route::post('/piggybanks/add/{piggybank}', ['uses' => 'PiggybankController@postAdd', 'as' => 'piggybanks.add']); # add money + Route::post('/piggybanks/remove/{piggybank}', ['uses' => 'PiggybankController@postRemove', 'as' => 'piggybanks.remove']); # remove money. + // repeated expense controller + Route::post('/repeatedexpense/store', ['uses' => 'RepeatedExpenseController@store', 'as' => 'repeated.store']); // preferences controller Route::post('/preferences', ['uses' => 'PreferencesController@postIndex']); @@ -321,7 +313,7 @@ Route::group( // transaction controller: Route::post('/transactions/store/{what}', ['uses' => 'TransactionController@store', 'as' => 'transactions.store'])->where( - ['what' => 'withdrawal|deposit|transfer'] + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] ); Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']); Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']); diff --git a/app/start/artisan.php b/app/start/artisan.php index 1df850bc95..3b223f16ec 100644 --- a/app/start/artisan.php +++ b/app/start/artisan.php @@ -11,3 +11,4 @@ | */ +Artisan::add(new Cleanup); \ No newline at end of file diff --git a/app/start/global.php b/app/start/global.php index 10109df498..5604f492bb 100644 --- a/app/start/global.php +++ b/app/start/global.php @@ -14,10 +14,7 @@ ClassLoader::addDirectories( [ - app_path() . '/commands', - app_path() . '/controllers', - app_path() . '/models', - app_path() . '/database/seeds', + app_path() . '/commands', app_path() . '/controllers', app_path() . '/models', app_path() . '/database/seeds', ] ); @@ -74,47 +71,47 @@ App::down( // forms: \Form::macro( 'ffText', function ($name, $value = null, array $options = []) { - return \Firefly\Form\Form::ffText($name, $value, $options); + return \FireflyIII\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); + return \FireflyIII\Form\Form::ffSelect($name, $list, $selected, $options); } ); \Form::macro( 'ffInteger', function ($name, $value = null, array $options = []) { - return \Firefly\Form\Form::ffInteger($name, $value, $options); + return \FireflyIII\Form\Form::ffInteger($name, $value, $options); } ); \Form::macro( 'ffAmount', function ($name, $value = null, array $options = []) { - return \Firefly\Form\Form::ffAmount($name, $value, $options); + return \FireflyIII\Form\Form::ffAmount($name, $value, $options); } ); \Form::macro( 'ffBalance', function ($name, $value = null, array $options = []) { - return \Firefly\Form\Form::ffBalance($name, $value, $options); + return \FireflyIII\Form\Form::ffBalance($name, $value, $options); } ); \Form::macro( 'ffDate', function ($name, $value = null, array $options = []) { - return \Firefly\Form\Form::ffDate($name, $value, $options); + return \FireflyIII\Form\Form::ffDate($name, $value, $options); } ); \Form::macro( 'ffTags', function ($name, $value = null, array $options = []) { - return \Firefly\Form\Form::ffTags($name, $value, $options); + return \FireflyIII\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); + return \FireflyIII\Form\Form::ffCheckbox($name, $value, $checked, $options); } ); \Form::macro( 'ffOptionsList', function ($type, $name) { - return \Firefly\Form\Form::ffOptionsList($type, $name); + return \FireflyIII\Form\Form::ffOptionsList($type, $name); } ); diff --git a/app/tests/TestCase.php b/app/tests/TestCase.php index f6230dd2ef..c0d01d3355 100644 --- a/app/tests/TestCase.php +++ b/app/tests/TestCase.php @@ -1,24 +1,19 @@ seed(); + + + //$this-> } - /** - * @param $class - * - * @return \Mockery\MockInterface - */ public function mock($class) { $mock = Mockery::mock($class); @@ -44,4 +38,13 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase return $mock; } -} + static public function setupBeforeClass() + { + League\FactoryMuffin\Facade::loadFactories(__DIR__ . '/factories'); + } + + public function tearDown() + { + m::close(); + } +} \ No newline at end of file diff --git a/app/tests/factories/TransactionCurrency.php b/app/tests/factories/TransactionCurrency.php new file mode 100644 index 0000000000..9804dd56c2 --- /dev/null +++ b/app/tests/factories/TransactionCurrency.php @@ -0,0 +1,7 @@ + 'word', + ] +); diff --git a/app/tests/factories/TransactionJournal.php b/app/tests/factories/TransactionJournal.php new file mode 100644 index 0000000000..58fd89d200 --- /dev/null +++ b/app/tests/factories/TransactionJournal.php @@ -0,0 +1,12 @@ + 'factory|TransactionType', + 'transaction_currency_id' => 'factory|TransactionCurrency', + 'description' => 'text', + 'date' => 'date|Y-m-d', + 'completed' => 'boolean', + 'user_id' => 'factory|User' + ] +); diff --git a/app/tests/factories/TransactionType.php b/app/tests/factories/TransactionType.php new file mode 100644 index 0000000000..0876277d2c --- /dev/null +++ b/app/tests/factories/TransactionType.php @@ -0,0 +1,7 @@ + 'word', + ] +); diff --git a/app/tests/factories/User.php b/app/tests/factories/User.php new file mode 100644 index 0000000000..c7a74e8e82 --- /dev/null +++ b/app/tests/factories/User.php @@ -0,0 +1,8 @@ + 'email', + 'migrated' => 1, + 'password' => 'empty' + ] +); diff --git a/app/views/accounts/create.blade.php b/app/views/accounts/create.blade.php index edf174bce6..ac7ec268f8 100644 --- a/app/views/accounts/create.blade.php +++ b/app/views/accounts/create.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $what) }} {{Form::open(['class' => 'form-horizontal','route' => 'accounts.store'])}} {{Form::hidden('what',$what)}}
@@ -32,6 +33,7 @@ {{Form::ffDate('openingbalancedate', date('Y-m-d'))}} @endif {{Form::ffCheckbox('active','1',true)}} + {{Form::ffSelect('account_role',Config::get('firefly.accountRoles'))}}
diff --git a/app/views/accounts/delete.blade.php b/app/views/accounts/delete.blade.php index 6f2059a15e..5140108559 100644 --- a/app/views/accounts/delete.blade.php +++ b/app/views/accounts/delete.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) }} {{Form::open(['class' => 'form-horizontal','url' => route('accounts.destroy',$account->id)])}}
diff --git a/app/views/accounts/edit.blade.php b/app/views/accounts/edit.blade.php index 39e14486ca..6a596f87ba 100644 --- a/app/views/accounts/edit.blade.php +++ b/app/views/accounts/edit.blade.php @@ -1,13 +1,6 @@ @extends('layouts.default') @section('content') -
-
-

- Bla text here. -

-
-
- +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) }} {{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update',$account->id)])}}
@@ -35,6 +28,7 @@ @if($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account') {{Form::ffBalance('openingbalance')}} {{Form::ffDate('openingbalancedate')}} + {{Form::ffSelect('account_role',Config::get('firefly.accountRoles'))}} @endif
diff --git a/app/views/accounts/index.blade.php b/app/views/accounts/index.blade.php index 2410bc6c8e..c98c6e38b1 100644 --- a/app/views/accounts/index.blade.php +++ b/app/views/accounts/index.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $what) }}
@@ -21,9 +22,7 @@
-
-
-
+ @include('list.accounts')
@@ -39,8 +38,5 @@ {{HTML::script('assets/javascript/firefly/gcharts.js')}} - -@stop - -@section('styles') -@endsection \ No newline at end of file +{{HTML::script('assets/javascript/firefly/accounts.js')}} +@stop \ No newline at end of file diff --git a/app/views/accounts/show.blade.php b/app/views/accounts/show.blade.php index 8d5842fd67..9445421f28 100644 --- a/app/views/accounts/show.blade.php +++ b/app/views/accounts/show.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) }}
@@ -14,6 +15,20 @@
@include('partials.date_nav') +
+
+ View options for {{{$account->name}}} +
+
+

+ @if($view == 'all') + Stick to date-range + @else + Show all transactions + @endif +

+
+
@@ -48,7 +63,7 @@ Transactions
-
+ @include('list.journals-full')
@@ -59,6 +74,7 @@ @section('scripts') diff --git a/app/views/budgets/create.blade.php b/app/views/budgets/create.blade.php index 0b20da4663..28e28e4712 100644 --- a/app/views/budgets/create.blade.php +++ b/app/views/budgets/create.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) }} {{Form::open(['class' => 'form-horizontal','url' => route('budgets.store')])}}
diff --git a/app/views/budgets/delete.blade.php b/app/views/budgets/delete.blade.php index 96775a8357..bf16e4881a 100644 --- a/app/views/budgets/delete.blade.php +++ b/app/views/budgets/delete.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $budget) }} {{Form::open(['class' => 'form-horizontal','url' => route('budgets.destroy',$budget->id)])}}
diff --git a/app/views/budgets/edit.blade.php b/app/views/budgets/edit.blade.php index baeeb1cc8c..8fefee9bfd 100644 --- a/app/views/budgets/edit.blade.php +++ b/app/views/budgets/edit.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $budget) }}

Use budgets to organize and limit your expenses.

diff --git a/app/views/budgets/index.blade.php b/app/views/budgets/index.blade.php index 9dd76b21e6..dbcdb2ff41 100644 --- a/app/views/budgets/index.blade.php +++ b/app/views/budgets/index.blade.php @@ -1,5 +1,6 @@ @extends('layouts.default') @section('content') +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) }}
@@ -19,10 +20,10 @@
-
-
-
-
+
+
+
+
@@ -33,12 +34,12 @@
-
+
@if($overspent)
@else -
+
@endif
@@ -130,55 +131,6 @@
- - -@foreach($budgets as $budget) -{{-- -
-
-
-
- {{$budget->name}} -
-
- -
-
- -
-
-
-
- @if($budget->pct > 0) - -
- -
- @else - - - @endif - -
-
-
-
---}} -@endforeach - diff --git a/app/views/budgets/show.blade.php b/app/views/budgets/show.blade.php index 2cdc0f3e14..80e24ff00a 100644 --- a/app/views/budgets/show.blade.php +++ b/app/views/budgets/show.blade.php @@ -1,14 +1,14 @@ @extends('layouts.default') @section('content') - +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $budget, $repetition) }}
- Some stuff? + Overview
-
+
@@ -16,9 +16,7 @@
Transactions
-
-
-
+ @include('list.journals-full')
@@ -73,7 +71,7 @@ @stop @section('scripts') +{{HTML::script('assets/javascript/firefly/gcharts.options.js')}} +{{HTML::script('assets/javascript/firefly/gcharts.js')}} +{{HTML::script('assets/javascript/firefly/categories.js')}} @stop \ No newline at end of file diff --git a/app/views/categories/show.blade.php b/app/views/categories/show.blade.php index f6f4678b6d..16555e7a9e 100644 --- a/app/views/categories/show.blade.php +++ b/app/views/categories/show.blade.php @@ -1,44 +1,44 @@ @extends('layouts.default') @section('content') - - - +{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $category) }}
-
-

Use categories to group your expenses

-

- Use categories to group expenses by hobby, for certain types of groceries or what bills are for. - Expenses grouped in categories do not have to reoccur every month or every week, like budgets. -

-

- This overview will show you the expenses you've made in each [period] and show you the actual - transactions for the currently selected period. -

-
-
+
+
+
+ Overview +
+
+
+
+
- -@include('partials.date_nav') -
-
-
+
+
+ Transactions +
+
+ @include('list.journals-full') +
- - -
-
-

Transactions in current range

- @include('lists.transactions',['journals' => $journals,'sum' => true]) -
+
+ + BLa bla something here.
- - +
@stop @section('scripts') + + + +{{HTML::script('assets/javascript/firefly/gcharts.options.js')}} +{{HTML::script('assets/javascript/firefly/gcharts.js')}} {{HTML::script('assets/javascript/firefly/categories.js')}} + @stop \ No newline at end of file diff --git a/app/views/charts/info.blade.php b/app/views/charts/info.blade.php deleted file mode 100644 index 00e590e129..0000000000 --- a/app/views/charts/info.blade.php +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - @foreach($rows as $name => $entry) - - - - - @endforeach -
Total{{mf($sum*-1)}}
{{{$name}}}{{mf($entry['amount']*-1)}}
\ No newline at end of file diff --git a/app/views/emails/auth/reminder.blade.php b/app/views/emails/auth/reminder.blade.php deleted file mode 100644 index 276f8c59f2..0000000000 --- a/app/views/emails/auth/reminder.blade.php +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - -

Password Reset

- -
- To reset your password, complete this form: {{ URL::to('password/reset', array($token)) }}.
- This link will expire in {{ Config::get('auth.reminder.expire', 60) }} minutes. -
- - - \ No newline at end of file diff --git a/app/views/help/show.blade.php b/app/views/help/show.blade.php new file mode 100644 index 0000000000..e864585fd5 --- /dev/null +++ b/app/views/help/show.blade.php @@ -0,0 +1,14 @@ + diff --git a/app/views/index.blade.php b/app/views/index.blade.php index 66857d7cfd..587f3a6f34 100644 --- a/app/views/index.blade.php +++ b/app/views/index.blade.php @@ -1,30 +1,19 @@ @extends('layouts.default') @section('content') -{{ Breadcrumbs::render('home') }} +{{ Breadcrumbs::renderIfExists() }} @if($count == 0)

Welcome to Firefly III.

- To get get started, choose below: + Create a new asset account to get started.

-
-

Migrate from Firefly II

- -

- Use this option if you have a JSON file from your current Firefly II installation. -

-

Start from scratch

- -

- Use this option if you are new to Firefly (III). -

@else @@ -112,7 +101,7 @@
- @include('transactions.journals-small-index',['transactions' => $data[0],'account' => $data[1]]) + @include('list.journals-tiny',['transactions' => $data[0],'account' => $data[1]])
@endforeach @@ -121,6 +110,7 @@ @endif + @stop @section('scripts') diff --git a/app/views/layouts/default.blade.php b/app/views/layouts/default.blade.php index 346742569b..ffad378639 100644 --- a/app/views/layouts/default.blade.php +++ b/app/views/layouts/default.blade.php @@ -1,5 +1,6 @@ +getName();?> @@ -17,13 +18,15 @@ {{HTML::style('assets/stylesheets/metisMenu/metisMenu.min.css')}} {{HTML::style('assets/stylesheets/sbadmin/sb.css')}} {{HTML::style('assets/stylesheets/fa/css/font-awesome.min.css')}} - {{HTML::style('http://fonts.googleapis.com/css?family=Roboto2')}} + {{HTML::style('https://fonts.googleapis.com/css?family=Roboto2')}} @yield('styles') + +
@@ -47,31 +50,47 @@ {{$subTitle}} @endif + +
@include('partials.flashes') - @yield('content') + + +
- {{HTML::script('assets/javascript/jquery/jquery-2.1.1.min.js')}} {{HTML::script('assets/javascript/bootstrap/bootstrap.min.js')}} {{HTML::script('assets/javascript/metisMenu/jquery.metisMenu.min.js')}} {{HTML::script('assets/javascript/sb-admin/sb-admin-2.js')}} +{{HTML::script('assets/javascript/firefly/help.js')}} @yield('scripts') \ No newline at end of file diff --git a/app/views/layouts/guest.blade.php b/app/views/layouts/guest.blade.php index df2a721548..85966199ce 100644 --- a/app/views/layouts/guest.blade.php +++ b/app/views/layouts/guest.blade.php @@ -5,7 +5,7 @@ - Firefly + Firefly III {{HTML::style('assets/stylesheets/bootstrap/bootstrap.min.css')}} {{HTML::style('assets/stylesheets/metisMenu/metisMenu.min.css')}} @@ -18,6 +18,8 @@ + +
@@ -25,4 +27,4 @@ @yield('content')
- \ No newline at end of file + \ No newline at end of file diff --git a/app/views/limits/create.blade.php b/app/views/limits/create.blade.php deleted file mode 100644 index a3a13c4dcf..0000000000 --- a/app/views/limits/create.blade.php +++ /dev/null @@ -1,98 +0,0 @@ -@extends('layouts.default') -@section('content') -
-
-

- Firefly uses an "envelope - system" for your budgets, which means that for each period of time (for example a month) a virtual - "envelope" can be created containing a certain amount of money. Money spent within a budget is removed from - the envelope. -

- -

- Firefly gives you the opportunity to create such an envelope when you create a budget. However, you can - always add more of them. -

-
-
- -{{Form::open(['class' => 'form-horizontal','url' => route('budgets.limits.store',$prefilled['budget_id'])])}} -{{Form::hidden('from',e(Input::get('from')))}} - - -
-
-

Mandatory fields

- -
- {{ Form::label('budget_id', 'Budget', ['class' => 'col-sm-4 control-label'])}} -
- {{Form::select('budget_id',$budgets,Input::old('budget_id') ?: $prefilled['budget_id'], ['class' => - 'form-control'])}} - @if($errors->has('budget_id')) -

{{$errors->first('name')}}

- @else - Select one of your existing budgets. - @endif -
-
- -
- {{ Form::label('startdate', 'Start date', ['class' => 'col-sm-4 control-label'])}} -
- - This date indicates when the envelope "starts". The date you select - here will correct itself to the nearest [period] you select below. -
-
- -
- - -
- {{Form::select('period',$periods,Input::old('period') ?: $prefilled['repeat_freq'],['class' => 'form-control'])}} - How long will the envelope last? A week, a month, or even longer? -
-
-
- - -
-
- -
- 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 fall back to old habits. - Instead, you should recreate the envelope yourself each [period]. -
-
- - -
- {{ Form::label('amount', 'Amount', ['class' => 'col-sm-4 control-label'])}} -
- - Of course, there needs to be money in the envelope. -
-
- -
- {{ Form::label('submit', ' ', ['class' => 'col-sm-4 control-label'])}} -
- - -
-
- -
-
- -{{Form::close()}} - - -@stop \ No newline at end of file diff --git a/app/views/limits/delete.blade.php b/app/views/limits/delete.blade.php deleted file mode 100644 index 4c3bd9b8b7..0000000000 --- a/app/views/limits/delete.blade.php +++ /dev/null @@ -1,62 +0,0 @@ -@extends('layouts.default') -@section('content') -
-
-

Remember that deleting something is permanent.

- -
- -
- -{{Form::open(['class' => 'form-horizontal','url' => route('budgets.limits.destroy',$limit->id)])}} - -
-
-

 

-

- This form allows you to delete the envelope for budget {{{$limit->budget->name}}}, with a content of - {{mf($limit->amount,false)}} - @if($limit->repeats == 0) - in {{$limit->limitrepetitions[0]->startdate->format('M Y')}} ({{$limit->repeat_freq}}). - @endif -

-

- Destroying an envelope does not remove any transactions from the budget. -

-

- Are you sure? -

- -
-
- - @if(Input::get('from') == 'date') - Cancel - @else - Cancel - @endif -
-
-
- @if($limit->repeats == 1) -
-

Auto repeating

-

- This envelope is set to repeat itself; creating a new period whenever the previous period - has passed. If you change this envelope, you'll also change the following (automatically created) - envelopes. - {{$limit->limitrepetitions()->count() }} -

-
    - @foreach($limit->limitrepetitions()->orderBy('startdate','DESC')->get() as $rep) -
  • Evenlope for {{$rep->periodShow()}}, {{mf($rep->amount,false)}}
  • - @endforeach -
-
- @endif -
- - -{{Form::close()}} - -@stop diff --git a/app/views/limits/edit.blade.php b/app/views/limits/edit.blade.php deleted file mode 100644 index 7ba95b132b..0000000000 --- a/app/views/limits/edit.blade.php +++ /dev/null @@ -1,115 +0,0 @@ -@extends('layouts.default') -@section('content') -
-
-

- Firefly uses an "envelope - system" for your budgets, which means that for each period of time (for example a month) a virtual - "envelope" can be created containing a certain amount of money. Money spent within a budget is removed from - the envelope. -

- -

- This form allows you to edit the envelope for budget {{{$limit->budget->name}}}, with a content of - {{mf($limit->amount,false)}} - @if($limit->repeats == 0) - in {{$limit->limitrepetitions[0]->startdate->format('M Y')}} ({{$limit->repeat_freq}}). - @endif -

-
-
- -{{Form::open(['class' => 'form-horizontal','url' => route('budgets.limits.update',$limit->id)])}} -{{Form::hidden('from',e(Input::get('from')))}} -
-
-

Mandatory fields

- -
- {{ Form::label('budget_id', 'Budget', ['class' => 'col-sm-4 control-label'])}} -
- {{Form::select('budget_id',$budgets,Input::old('budget_id') ?: $limit->component_id, ['class' => - 'form-control'])}} - @if($errors->has('budget_id')) -

{{$errors->first('name')}}

- @else - Select one of your existing budgets. - @endif -
-
- -
- {{ Form::label('startdate', 'Start date', ['class' => 'col-sm-4 control-label'])}} -
- - This date indicates when the envelope "starts". The date you select - here will correct itself to the nearest [period] you select below. -
-
- -
- - -
- {{Form::select('period',$periods,Input::old('period') ?: $limit->repeat_freq,['class' => 'form-control'])}} - How long will the envelope last? A week, a month, or even longer? -
-
-
- - -
-
- -
- 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 fall back to old habits. - Instead, you should recreate the envelope yourself each [period]. -
-
- - -
- {{ Form::label('amount', 'Amount', ['class' => 'col-sm-4 control-label'])}} -
- - Of course, there needs to be money in the envelope. -
-
- -
- {{ Form::label('submit', ' ', ['class' => 'col-sm-4 control-label'])}} -
- - -
-
- -
- @if($limit->repeats == 1) -
-

Auto repeating

-

- This envelope is set to repeat itself; creating a new period whenever the previous period - has passed. If you change this envelope, you'll also change the following (automatically created) - envelopes. - {{$limit->limitrepetitions()->count() }} -

-
    - @foreach($limit->limitrepetitions()->orderBy('startdate','DESC')->get() as $rep) -
  • Evenlope for {{$rep->periodShow()}}, {{mf($rep->amount,false)}}
  • - @endforeach -
-
- @endif -
- -{{Form::close()}} - - -@stop diff --git a/app/views/list/accounts.blade.php b/app/views/list/accounts.blade.php new file mode 100644 index 0000000000..0bbf841fc6 --- /dev/null +++ b/app/views/list/accounts.blade.php @@ -0,0 +1,38 @@ + + + + + + + + + + @foreach($accounts as $account) + + + + + + + + + + @endforeach +
 NameRoleCurrent balanceActiveLast activity
+
+ + +
+
{{{$account->name}}}{{{$account->accountRole}}}{{mf(Steam::balance($account))}} + @if($account->active) + + @else + + @endif + + @if($account->lastActionDate) + {{{$account->lastActionDate->format('j F Y')}}} + @else + Never + @endif +
\ No newline at end of file diff --git a/app/views/list/categories.blade.php b/app/views/list/categories.blade.php new file mode 100644 index 0000000000..6cfdb19eb2 --- /dev/null +++ b/app/views/list/categories.blade.php @@ -0,0 +1,28 @@ + + + + + + + @foreach($categories as $category) + + + + + + @endforeach +
 NameLast activity
+
+ + +
+
+ {{{$category->name}}} + + lastActionDate(); ?> + @if($active) + {{{$active->format('j F Y')}}} + @else + Never + @endif +
\ No newline at end of file diff --git a/app/views/list/journals-full.blade.php b/app/views/list/journals-full.blade.php new file mode 100644 index 0000000000..61929f43d9 --- /dev/null +++ b/app/views/list/journals-full.blade.php @@ -0,0 +1,120 @@ +@if(is_object($journals) && method_exists($journals, 'links')) +{{$journals->links()}} +@endif + + + + + + + + + @if(!isset($hideBudget) || (isset($hideBudget) && $hideBudget=== false)) + + @endif + @if(!isset($hideCategory) || (isset($hideCategory) && $hideCategory=== false)) + + @endif + @if(!isset($hideRecurring) || (isset($hideRecurring) && $hideRecurring=== false)) + + @endif + + @foreach($journals as $journal) + @if(!isset($journal->transactions[1]) || !isset($journal->transactions[0])) + + + + + + + @else + + + + + + + + + @if(!isset($hideBudget) || (isset($hideBudget) && $hideBudget=== false)) + + @endif + @if(!isset($hideCategory) || (isset($hideCategory) && $hideCategory=== false)) + + @endif + @if(!isset($hideRecurring) || (isset($hideRecurring) && $hideRecurring=== false)) + + @endif + + + + @endif + + @endforeach +
 DescriptionAmountDateFromTo
+
+ +
+
 {{{$journal->description}}}Invalid journal: Found {{$journal->transactions()->count()}} transaction(s)
+
+ + +
+
+ @if($journal->transactiontype->type == 'Withdrawal') + + @endif + @if($journal->transactiontype->type == 'Deposit') + + @endif + @if($journal->transactiontype->type == 'Transfer') + + @endif + @if($journal->transactiontype->type == 'Opening balance') + + @endif + + {{{$journal->description}}} + + @if($journal->transactiontype->type == 'Withdrawal') + {{mf($journal->transactions[1]->amount,false)}} + @endif + @if($journal->transactiontype->type == 'Deposit') + {{mf($journal->transactions[1]->amount,false)}} + @endif + @if($journal->transactiontype->type == 'Transfer') + {{mf($journal->transactions[1]->amount,false)}} + @endif + + {{$journal->date->format('j F Y')}} + + @if($journal->transactions[0]->account->accounttype->description == 'Cash account') + (cash) + @else + {{{$journal->transactions[0]->account->name}}} + @endif + + @if($journal->transactions[1]->account->accounttype->description == 'Cash account') + (cash) + @else + {{{$journal->transactions[1]->account->name}}} + @endif + + budgets[0]) ? $journal->budgets[0] : null; ?> + @if($budget) + {{{$budget->name}}} + @endif + + categories[0]) ? $journal->categories[0] : null; ?> + @if($category) + {{{$category->name}}} + @endif + + @if($journal->recurringTransaction) + {{{$journal->recurringTransaction->name}}} + @endif +
+ +@if(is_object($journals) && method_exists($journals, 'links')) +{{$journals->links()}} +@endif \ No newline at end of file diff --git a/app/views/transactions/journals-small-index.blade.php b/app/views/list/journals-tiny.blade.php similarity index 63% rename from app/views/transactions/journals-small-index.blade.php rename to app/views/list/journals-tiny.blade.php index e8e5be5a4f..91c2b6f2d3 100644 --- a/app/views/transactions/journals-small-index.blade.php +++ b/app/views/list/journals-tiny.blade.php @@ -3,10 +3,10 @@ @if($journal->transactiontype->type == 'Withdrawal') - + @endif @if($journal->transactiontype->type == 'Deposit') - + @endif @if($journal->transactiontype->type == 'Transfer') @@ -15,11 +15,19 @@ {{{$journal->description}}} - @foreach($journal->transactions as $t) +@if(isset($account)) + @foreach($journal->transactions as $index => $t) @if($t->account_id == $account->id) {{mf($t->amount)}} @endif @endforeach +@else + @foreach($journal->transactions as $index => $t) + @if($index == 0) + {{mf($t->amount)}} + @endif + @endforeach +@endif diff --git a/app/views/list/piggybank-events.blade.php b/app/views/list/piggybank-events.blade.php new file mode 100644 index 0000000000..e80ddc4c61 --- /dev/null +++ b/app/views/list/piggybank-events.blade.php @@ -0,0 +1,33 @@ + + + @if(isset($showPiggybank) && $showPiggybank === true) + + @endif + + + + @foreach($events as $event) + + @if(isset($showPiggybank) && $showPiggybank === true) + + @endif + + + + + @endforeach +
Piggy bankDateAmount
+ {{{$event->piggybank->name}}} + + @if(!is_null($event->transaction_journal_id)) + {{$event->date->format('j F Y')}} + @else + {{$event->date->format('j F Y')}} + @endif + + @if($event->amount < 0) + Removed {{mf($event->amount*-1,false)}} + @else + Added {{mf($event->amount,false)}} + @endif +
\ No newline at end of file diff --git a/app/views/list/recurring.blade.php b/app/views/list/recurring.blade.php new file mode 100644 index 0000000000..cab075f3b0 --- /dev/null +++ b/app/views/list/recurring.blade.php @@ -0,0 +1,73 @@ + + + + + + + + + + + + + @foreach($recurring as $entry) + + + + + + + + + + + + + @endforeach +
 NameMatches onMatching amountLast seen matchNext expected matchIs activeWill be automatchedRepeats every
+
+ + +
+
+ {{{$entry->name}}} + + @foreach(explode(',',$entry->match) as $match) + {{{$match}}} + @endforeach + + {{mf($entry->amount_min)}} + — + {{mf($entry->amount_max)}} + + lastFoundMatch();?> + @if($lastMatch) + {{$lastMatch->format('j F Y')}} + @else + Unknown + @endif + + nextExpectedMatch();?> + @if($nextExpectedMatch) + {{$nextExpectedMatch->format('j F Y')}} + @else + Unknown + @endif + + @if($entry->active) + + @else + + @endif + + @if($entry->automatch) + + @else + + @endif + + {{{$entry->repeat_freq}}} + @if($entry->skip > 0) + skips over {{$entry->skip}} + @endif +
\ No newline at end of file diff --git a/app/views/lists/transactions.blade.php b/app/views/lists/transactions.blade.php deleted file mode 100644 index ceb0f54757..0000000000 --- a/app/views/lists/transactions.blade.php +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - @foreach($journals as $journal) - @if(isset($journal->transactions[0]) && isset($journal->transactions[1])) - id) - class="success" - @endif - > - - - - - - - - - - @else - - @endif - @endforeach - @if(isset($sum) && $sum == true) - @if($expenses != 0) - - - - - @endif - @if($incomes != 0) - - - - - @endif - @if($transfers != 0) - - - - - @endif - @endif - - -
ADateDescriptionAmount (€)FromToB
- @if($journal->transactiontype->type == 'Withdrawal') - - @endif - @if($journal->transactiontype->type == 'Deposit') - - @endif - @if($journal->transactiontype->type == 'Transfer') - - @endif - @if($journal->transactiontype->type == 'Opening balance') - - @endif - - @foreach($journal->components as $component) - @if($component->class == 'Budget') - - @endif - @if($component->class == 'Category') - - @endif - @endforeach - - @if(!is_null($journal->recurringTransaction)) - - @endif - - {{$journal->date->format('d F Y')}} - {{{$journal->description}}} - @if($journal->transactiontype->type == 'Withdrawal') - {{mf($journal->transactions[1]->amount,false)}} - transactions[1]->amount;?> - @endif - @if($journal->transactiontype->type == 'Deposit') - {{mf($journal->transactions[1]->amount,false)}} - transactions[1]->amount;?> - @endif - @if($journal->transactiontype->type == 'Transfer') - {{mf($journal->transactions[1]->amount,false)}} - transactions[1]->amount;?> - @endif - - {{{$journal->transactions[0]->account->name}}} - - {{{$journal->transactions[1]->account->name}}} - - @if($journal->transactiontype->type != 'Opening balance') - - @endif -
Expenses:{{mf($expenses)}}
Incomes:{{mf($incomes)}}
Transfers:{{mf($transfers,false)}}
\ No newline at end of file diff --git a/app/views/migrate/index.blade.php b/app/views/migrate/index.blade.php deleted file mode 100644 index dec6859517..0000000000 --- a/app/views/migrate/index.blade.php +++ /dev/null @@ -1,24 +0,0 @@ -@extends('layouts.default') -@section('content') -
-
-

- Read the wiki to read more about how data migration. -

-
    -
  1. Upload firefly-export-****-**-**.json
  2. -
  3. Wait..
  4. -
  5. Done!
  6. -
- {{Form::open(['files' => true,'url' => route('migrate.upload')])}} -
- - -

Upload the export file here.

-
- - {{Form::close()}} -
-
- -@stop \ No newline at end of file diff --git a/app/views/migrate/result.blade.php b/app/views/migrate/result.blade.php deleted file mode 100644 index 0e41f4ab59..0000000000 --- a/app/views/migrate/result.blade.php +++ /dev/null @@ -1,21 +0,0 @@ -@extends('layouts.default') -@section('content') -
-
-

Firefly
- Migration results -

-

- The migration was successful! You can now return to the home page - and start using Firefly. -

- -
    - @foreach($messages as $m) -
  • {{$m}}
  • - @endforeach -
-
-
- -@stop \ No newline at end of file diff --git a/app/views/migrate/select-user.blade.php b/app/views/migrate/select-user.blade.php deleted file mode 100644 index 9ff7198ca9..0000000000 --- a/app/views/migrate/select-user.blade.php +++ /dev/null @@ -1,35 +0,0 @@ -@extends('layouts.default') -@section('content') -
-
-

Firefly
- Select a user for migration. -

-

- Select a user from the list below. Then press import. -

- - {{Form::open(['class' => 'form-horizontal'])}} - -
- -
- -
-
-
-
-
- Please be patient; importing data may take some time. -
-
- {{Form::close()}} -
-
- -@stop \ No newline at end of file diff --git a/app/views/paginated/transactions.blade.php b/app/views/paginated/transactions.blade.php deleted file mode 100644 index 223541781e..0000000000 --- a/app/views/paginated/transactions.blade.php +++ /dev/null @@ -1,3 +0,0 @@ -{{$journals->links()}} -@include('lists.transactions') -{{$journals->links()}} \ No newline at end of file diff --git a/app/views/partials/menu.blade.php b/app/views/partials/menu.blade.php index 21a5068f89..c68c2251a5 100644 --- a/app/views/partials/menu.blade.php +++ b/app/views/partials/menu.blade.php @@ -1,222 +1,183 @@
- - - - - - @endif + + + @if($index+1 != count($reminders)) +
  • + @endif + @endforeach + + + + @endif + + + - - - - - getName(); - - ?> - -