Merge branch 'release/3.2.1'

This commit is contained in:
James Cole 2014-12-24 14:34:51 +01:00
commit 9f04854902
258 changed files with 10684 additions and 17460 deletions

View File

@ -1,2 +1,3 @@
src_dir: .
coverage_clover: tests/_output/coverage.xml
coverage_clover: tests/_output/coverage.xml
json_path: tests/_output/coveralls-upload.json

10
.gitignore vendored
View File

@ -1,7 +1,6 @@
/bootstrap/compiled.php
/vendor
composer.phar
composer.lock
.env.*.php
.env.php
.DS_Store
@ -14,8 +13,13 @@ index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json
tests/_output/*
testing.sqlite
c3.php
_ide_helper_models.php
clean.sqlite
tests/acceptance/AcceptanceTester.php
tests/functional/FunctionalTester.php
tests/unit/UnitTester.php
pi.php
tests/_data/db.sqlite
tests/_data/dump.sql

View File

@ -3,13 +3,14 @@ language: php
php:
- 5.5
- 5.6
- hhvm
install:
- composer install
script:
- php vendor/bin/codecept run
- ./tests/_data/db.sh
- php vendor/bin/codecept build
- php vendor/bin/codecept run --coverage --coverage-xml
after_script:
- php vendor/bin/coveralls

View File

@ -1,8 +1,9 @@
Firefly III
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii)
![Still maintained?](http://stillmaintained.com/JC5/firefly-iii.png)
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.png?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=master)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
@ -17,13 +18,15 @@ laptop and [Firefly II](https://github.com/JC5/Firefly) is live.
## Current features
- [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system).
- [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove withdrawals, deposits and transfers. This allows you full financial management;
- It's possible to create, change and manage money using _budgets_;
- Organize transactions using categories;
- Save towards a goal using piggy banks;
- Predict and anticipate large expenses using "repeated expenses" (ie. yearly taxes);
- Predict and anticipate bills using "recurring transactions" (rent for example).
- Predict and anticipate bills using "recurring transactions" (rent for example);
- View basic income / expense reports.
- Lots of help text in case you don't get it;
Everything is organised:
@ -37,15 +40,13 @@ Everything is organised:
Firefly III will feature, but does not feature yet:
- Financial reporting showing you how well you are doing;
- Lots of help text in case you don't get it;
- More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts
- Credit cards
- More test-coverage (aka: actual test coverage);
- More test-coverage;
- Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control.
- Firefly will be able to join transactions.
- Transfers and transactions are combined into one internal datatype which is more consistent with what you're actually doing: moving money from A to B. The fact that A or B or both are yours should not matter.
- Any other features I might not have thought of.
Some stuff has been removed:
@ -64,13 +65,9 @@ Some stuff has been removed:
![Reports](http://i.imgur.com/EnEIyQI.png)
## Current state
I have the basics up and running. Test coverage is currently non-existent.
I have the basics up and running. Test coverage is currently coming, slowly.
Although I have not checked extensively, some forms and views have CSRF vulnerabilities. This is because not all
views escape all characters by default. Will be fixed.
The current layout / look & feel is a pretty basic Bootstrap3 template. I am currently working on a more consistent,
expanded layout which will feature shiny AJAX things and data tables and all the Web 3.0 goodies you've come to expect
from social media sites.
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!

View File

@ -90,7 +90,7 @@ Breadcrumbs::register(
$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)
DateKit::periodShow($repetition->startdate, $repetition->budgetlimit->repeat_freq), route('budgets.show', $budget->id, $repetition->id)
);
}
}

View File

@ -1,9 +1,12 @@
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
//use Symfony\Component\Console\Input\InputArgument;
//use Symfony\Component\Console\Input\InputOption;
/**
* Class Cleanup
*/
class Cleanup extends Command
{
@ -21,9 +24,7 @@ class Cleanup extends Command
protected $name = 'firefly:cleanup';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
@ -42,7 +43,7 @@ class Cleanup extends Command
$this->info('Cleared compiled...');
Artisan::call('ide-helper:generate');
$this->info('IDE helper, done...');
Artisan::call('ide-helper:models', ['write']);
Artisan::call('ide-helper:models', ['nowrite']);
$this->info('IDE models, done...');
Artisan::call('optimize');
$this->info('Optimized...');

View File

@ -2,22 +2,7 @@
return [
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'default' => 'mysql',
'connections' => [
'mysql' => [
@ -30,6 +15,11 @@ return [
'collation' => 'utf8_unicode_ci',
'prefix' => '',
],
'sqlite' => [
'driver' => 'sqlite',
'database' => realpath(__DIR__.'/../../../tests/_data/testing.sqlite'),
'prefix' => ''
],
'pgsql' => [
'driver' => 'pgsql',

View File

@ -1,134 +0,0 @@
<?php
/**
* This file is part of the TwigBridge package.
*
* @copyright Robert Crowe <hello@vivalacrowe.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Configuration options for the built-in extensions.
*/
return [
/*
|--------------------------------------------------------------------------
| Extensions
|--------------------------------------------------------------------------
|
| Enabled extensions.
|
| `Twig_Extension_Debug` is enabled automatically if twig.debug is TRUE.
|
*/
'enabled' => [
'TwigBridge\Extension\Loader\Facades',
'TwigBridge\Extension\Loader\Filters',
'TwigBridge\Extension\Loader\Functions',
'TwigBridge\Extension\Laravel\Auth',
'TwigBridge\Extension\Laravel\Config',
'TwigBridge\Extension\Laravel\Form',
'TwigBridge\Extension\Laravel\Html',
'TwigBridge\Extension\Laravel\Input',
'TwigBridge\Extension\Laravel\Session',
'TwigBridge\Extension\Laravel\String',
'TwigBridge\Extension\Laravel\Translator',
'TwigBridge\Extension\Laravel\Url',
// 'TwigBridge\Extension\Laravel\Legacy\Facades',
],
/*
|--------------------------------------------------------------------------
| Facades
|--------------------------------------------------------------------------
|
| Available facades. Access like `{{ Config.get('foo.bar') }}`.
|
| Each facade can take an optional array of options. To mark the whole facade
| as safe you can set the option `'is_safe' => true`. Setting the facade as
| safe means that any HTML returned will not be escaped.
|
| It is advisable to not set the whole facade as safe and instead mark the
| each appropriate method as safe for security reasons. You can do that with
| the following syntax:
|
| <code>
| 'Form' => [
| 'is_safe' => [
| 'open'
| ]
| ]
| </code>
|
| The values of the `is_safe` array must match the called method on the facade
| in order to be marked as safe.
|
*/
'facades' => [],
/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| Available functions. Access like `{{ secure_url(...) }}`.
|
| Each function can take an optional array of options. These options are
| passed directly to `Twig_SimpleFunction`.
|
| So for example, to mark a function as safe you can do the following:
|
| <code>
| 'link_to' => [
| 'is_safe' => ['html']
| ]
| </code>
|
| The options array also takes a `callback` that allows you to name the
| function differently in your Twig templates than what it's actually called.
|
| <code>
| 'link' => [
| 'callback' => 'link_to'
| ]
| </code>
|
*/
'functions' => [],
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Available filters. Access like `{{ variable|filter }}`.
|
| Each filter can take an optional array of options. These options are
| passed directly to `Twig_SimpleFilter`.
|
| So for example, to mark a filter as safe you can do the following:
|
| <code>
| 'studly_case' => [
| 'is_safe' => ['html']
| ]
| </code>
|
| The options array also takes a `callback` that allows you to name the
| filter differently in your Twig templates than what is actually called.
|
| <code>
| 'snake' => [
| 'callback' => 'snake_case'
| ]
| </code>
|
*/
'filters' => [],
];

View File

@ -1,88 +0,0 @@
<?php
/**
* This file is part of the TwigBridge package.
*
* @copyright Robert Crowe <hello@vivalacrowe.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Support\Facades\Config;
/**
* Configuration options for Twig.
*/
return [
/*
|--------------------------------------------------------------------------
| Extension
|--------------------------------------------------------------------------
|
| File extension for Twig view files.
|
*/
'extension' => 'twig',
/*
|--------------------------------------------------------------------------
| Accepts all Twig environment configuration options
|--------------------------------------------------------------------------
|
| http://twig.sensiolabs.org/doc/api.html#environment-options
|
*/
'environment' => [
// When set to true, the generated templates have a __toString() method
// that you can use to display the generated nodes.
// default: false
'debug' => Config::get('app.debug', false),
// The charset used by the templates.
// default: utf-8
'charset' => 'utf-8',
// The base template class to use for generated templates.
// default: TwigBridge\Twig\Template
'base_template_class' => 'TwigBridge\Twig\Template',
// An absolute path where to store the compiled templates, or false to disable caching. If null
// then the cache file path is used.
// default: cache file storage path
'cache' => null,
// When developing with Twig, it's useful to recompile the template
// whenever the source code changes. If you don't provide a value
// for the auto_reload option, it will be determined automatically based on the debug value.
'auto_reload' => true,
// If set to false, Twig will silently ignore invalid variables
// (variables and or attributes/methods that do not exist) and
// replace them with a null value. When set to true, Twig throws an exception instead.
// default: false
'strict_variables' => false,
// If set to true, auto-escaping will be enabled by default for all templates.
// default: true
'autoescape' => true,
// A flag that indicates which optimizations to apply
// (default to -1 -- all optimizations are enabled; set it to 0 to disable)
'optimizations' => -1,
],
/*
|--------------------------------------------------------------------------
| Global variables
|--------------------------------------------------------------------------
|
| These will always be passed in and can be accessed as Twig variables.
| NOTE: these will be overwritten if you pass data into the view with the same key.
|
*/
'globals' => [],
];

View File

@ -1,20 +1,3 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Cache Driver
|--------------------------------------------------------------------------
|
| This option controls the default cache "driver" that will be used when
| using the Caching library. Of course, you may use other drivers any
| time you wish. This is the default when another is not specified.
|
| Supported: "file", "database", "apc", "memcached", "redis", "array"
|
*/
'driver' => 'array',
];
return ['driver' => 'array',];

View File

@ -4,7 +4,7 @@ return [
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => 'tests/_data/testing.sqlite',
'database' => realpath(__DIR__.'/../../../tests/_data/db.sqlite'),
'prefix' => ''
]

View File

@ -1,21 +1,3 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "native", "cookie", "database", "apc",
| "memcached", "redis", "array"
|
*/
'driver' => 'array',
];
return ['driver' => 'array',];

View File

@ -1,6 +1,6 @@
<?php
use FireflyIII\Database\Account as AccountRepository;
use FireflyIII\Database\Account\Account as AccountRepository;
use FireflyIII\Exception\FireflyException;
/**
@ -101,7 +101,7 @@ class AccountController extends BaseController
Session::flash('success', 'The ' . $typeName . ' account "' . e($name) . '" was deleted.');
return Redirect::route('accounts.index', $type);
return Redirect::route('accounts.index', $typeName);
}
/**
@ -191,12 +191,8 @@ class AccountController extends BaseController
if ($data['post_submit_action'] == 'store') {
return Redirect::route('accounts.index', $data['what']);
}
// create another.
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('accounts.create', $data['what'])->withInput();
}
return Redirect::route('accounts.index', $data['what']);
return Redirect::route('accounts.create', $data['what'])->withInput();
}
/**
@ -210,6 +206,7 @@ class AccountController extends BaseController
$data = Input::except('_token');
$data['what'] = $this->_shortNamesByFullName[$account->accountType->type];
// always validate:
$messages = $this->_repository->validate($data);
@ -234,11 +231,8 @@ class AccountController extends BaseController
if ($data['post_submit_action'] == 'update') {
return Redirect::route('accounts.index', $data['what']);
}
// go back to update screen.
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('accounts.edit', $account->id);
}
return Redirect::route('accounts.index', $data['what']);
// go back to update screen.
return Redirect::route('accounts.edit', $account->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}

View File

@ -1,24 +1,34 @@
<?php
use FireflyIII\Database\Budget as BudgetRepository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
use Carbon\Carbon;
use FireflyIII\Database\Budget\Budget as BudgetRepository;
use FireflyIII\Shared\Preferences\PreferencesInterface as Pref;
/**
* Class BudgetController
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
*/
class BudgetController extends BaseController
{
/** @var Pref */
protected $_preferences;
/** @var BudgetRepository */
protected $_repository;
/**
* @param BudgetRepository $repository
* @param Pref $preferences
*/
public function __construct(BudgetRepository $repository)
public function __construct(BudgetRepository $repository, Pref $preferences)
{
$this->_repository = $repository;
$this->_repository = $repository;
$this->_preferences = $preferences;
View::share('title', 'Budgets');
View::share('mainTitleIcon', 'fa-tasks');
}
@ -31,17 +41,11 @@ class BudgetController extends BaseController
*/
public function amount(Budget $budget)
{
$amount = intval(Input::get('amount'));
$date = Session::get('start');
$limit = $this->_repository->updateLimitAmount($budget, $date, $amount);
$amount = intval(Input::get('amount'));
$date = Session::get('start', Carbon::now()->startOfMonth());
$limitRepetition = $this->_repository->updateLimitAmount($budget, $date, $amount);
// try to find the limit repetition for this limit:
$repetition = $limit->limitrepetitions()->first();
if ($repetition) {
return Response::json(['name' => $budget->name, 'repetition' => $repetition->id]);
} else {
return Response::json(['name' => $budget->name, 'repetition' => null]);
}
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition->id]);
}
@ -72,8 +76,9 @@ class BudgetController extends BaseController
*/
public function destroy(Budget $budget)
{
Session::flash('success', 'Budget "' . e($budget->name) . '" was deleted.');
$this->_repository->destroy($budget);
Session::flash('success', 'The budget was deleted.');
return Redirect::route('budgets.index');
@ -86,67 +91,38 @@ class BudgetController extends BaseController
*/
public function edit(Budget $budget)
{
$subTitle = 'Edit budget "' . $budget->name . '"';
$subTitle = 'Edit budget "' . e($budget->name) . '"';
return View::make('budgets.edit', compact('budget', 'subTitle'));
}
/**
* The index of the budget controller contains all budgets and the current relevant limit repetition.
* TODO move currentRep to the repository.
*
* @return $this
*/
public function index()
{
$budgets = $this->_repository->get();
/** @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');
$spent = 0;
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$budget->spent = $repos->spentInMonth($budget, $date);
$spent += $budget->spent;
$budget->pct = 0;
$budget->limit = 0;
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
if ($repetition->startdate == $date) {
$budget->currentRep = $repetition;
$budget->limit = floatval($repetition->amount);
if ($budget->limit > $budget->spent) {
// not overspent:
$budget->pct = 30;
} else {
$budget->pct = 50;
}
}
}
// loop the budgets:
$budgets->each(
function (Budget $budget) {
$budget->spent = $this->_repository->spentInMonth($budget, \Session::get('start', Carbon::now()->startOfMonth()));
$budget->currentRep = $this->_repository->getRepetitionByDate($budget, \Session::get('start', Carbon::now()->startOfMonth()));
}
}
);
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
$amount = floatval($budgetAmount->data);
$overspent = $spent > $amount;
if ($overspent) {
// overspent on total amount
$spentPCT = ceil($amount / $spent * 100);
} else {
// not overspent on total amount.
$spentPCT = ceil($spent / $amount * 100);
}
$spent = $budgets->sum('spent');
$amount = $this->_preferences->get('budgetIncomeTotal' . \Session::get('start', Carbon::now()->startOfMonth())->format('FY'), 1000)->data;
$overspent = $spent > $amount;
$spentPCT = $overspent ? ceil($amount / $spent * 100) : ceil($spent / $amount * 100);
$budgetMax = $this->_preferences->get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return View::make('budgets.index', compact('budgets', 'spent', 'spentPCT', 'overspent'))->with('budgetAmount', $budgetAmount);
return View::make('budgets.index', compact('budgetMaximum', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount'));
}
/**
@ -154,12 +130,7 @@ class BudgetController extends BaseController
*/
public function postUpdateIncome()
{
/** @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);
$this->_preferences->set('budgetIncomeTotal' . Session::get('start', Carbon::now()->startOfMonth())->format('FY'), intval(Input::get('amount')));
return Redirect::route('budgets.index');
}
@ -172,124 +143,91 @@ class BudgetController extends BaseController
*/
public function show(Budget $budget, LimitRepetition $repetition = null)
{
if (!is_null($repetition) && $repetition->limit->budget->id != $budget->id) {
App::abort(500);
if (!is_null($repetition) && $repetition->budgetLimit->budget->id != $budget->id) {
return View::make('error')->with('message', 'Invalid selection.');
}
/** @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;
$hideBudget = true; // used in transaction list.
$journals = $this->_repository->getJournals($budget, $repetition);
$limits = $repetition ? [$repetition->budgetLimit] : $budget->budgetLimits()->orderBy('startdate', 'DESC')->get();
$subTitle = $repetition ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name);
return View::make('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget'));
}
/**
* @return $this
* @throws FireflyException
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store()
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('budgets.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New budget stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('budgets.create');
} else {
return Redirect::route('budgets.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.create')->withInput();
break;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not validate budget: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('budgets.create')->withInput();
}
// store:
$this->_repository->store($data);
Session::flash('success', 'Budget "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('budgets.index');
}
// create another.
return Redirect::route('budgets.create')->withInput();
}
/**
* @param Budget $budget
*
* @return $this
* @throws FireflyException
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(Budget $budget)
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
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 budget: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
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'] == 'return_to_edit') {
return Redirect::route('budgets.edit', $budget->id);
} else {
return Redirect::route('budgets.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.edit', $budget->id)->withInput();
break;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update budget: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('budgets.edit', $budget->id)->withInput();
}
// update
$this->_repository->update($budget, $data);
Session::flash('success', 'Budget "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('budgets.index');
}
return Redirect::route('budgets.edit', $budget->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
/**
@ -297,11 +235,8 @@ class BudgetController extends BaseController
*/
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);
$budgetAmount = $this->_preferences->get('budgetIncomeTotal' . Session::get('start', Carbon::now()->startOfMonth())->format('FY'), 1000);
return View::make('budgets.income')->with('amount', $budgetAmount)->with('date', $date);
return View::make('budgets.income')->with('amount', $budgetAmount);
}
}
}

View File

@ -1,19 +1,29 @@
<?php
use FireflyIII\Database\Category\Category as CategoryRepository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class CategoryController
*/
class CategoryController extends BaseController
{
/** @var CategoryRepository */
protected $_repository;
/**
*
* @param CategoryRepository $repository
*/
public function __construct()
public function __construct(CategoryRepository $repository)
{
View::share('title', 'Categories');
View::share('mainTitleIcon', 'fa-bar-chart');
$this->_repository = $repository;
}
/**
@ -41,11 +51,9 @@ class CategoryController extends BaseController
*/
public function destroy(Category $category)
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
Session::flash('success', 'Category "' . e($category->name) . '" was deleted.');
$this->_repository->destroy($category);
$repos->destroy($category);
Session::flash('success', 'The category was deleted.');
return Redirect::route('categories.index');
}
@ -65,9 +73,7 @@ class CategoryController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$categories = $repos->get();
$categories = $this->_repository->get();
return View::make('categories.index', compact('categories'));
}
@ -79,12 +85,8 @@ class CategoryController extends BaseController
*/
public function show(Category $category)
{
$hideCategory = true;
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$journals = $repos->getTransactionJournals($category, 50);
$hideCategory = true; // used in list.
$journals = $this->_repository->getTransactionJournals($category, 50);
return View::make('categories.show', compact('category', 'journals', 'hideCategory'));
}
@ -95,44 +97,33 @@ class CategoryController extends BaseController
*/
public function store()
{
$data = Input::all();
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
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());
// always validate:
$messages = $this->_repository->validate($data);
return Redirect::route('categories.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New category stored!');
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;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store category: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('categories.create')->withInput();
}
// store:
$this->_repository->store($data);
Session::flash('success', 'Category "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('categories.index');
}
return Redirect::route('categories.create')->withInput();
}
/**
@ -143,44 +134,37 @@ class CategoryController extends BaseController
*/
public function update(Category $category)
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$data = Input::except('_token');
$data = Input::except('_token');
$data['user_id'] = Auth::user()->id;
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());
// always validate:
$messages = $this->_repository->validate($data);
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;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update category: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('categories.edit', $category->id)->withInput();
}
// update
$this->_repository->update($category, $data);
Session::flash('success', 'Category "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('categories.index');
}
// go back to update screen.
return Redirect::route('categories.edit', $category->id)->withInput(['post_submit_action' => 'return_to_edit']);
}

View File

@ -0,0 +1,179 @@
<?php
use FireflyIII\Database\TransactionCurrency\TransactionCurrency as Repository;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class CurrencyController
*/
class CurrencyController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
* @param Repository $repository
*/
public function __construct(Repository $repository)
{
$this->_repository = $repository;
View::share('title', 'Currencies');
View::share('mainTitleIcon', 'fa-usd');
}
/**
* @return \Illuminate\View\View
*/
public function create()
{
$subTitleIcon = 'fa-plus';
$subTitle = 'Create a new currency';
return View::make('currency.create', compact('subTitleIcon', 'subTitle'));
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse
*/
public function defaultCurrency(TransactionCurrency $currency)
{
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$currencyPreference = $preferences->get('currencyPreference', 'EUR');
$currencyPreference->data = $currency->code;
$currencyPreference->save();
Session::flash('success', $currency->name.' is now the default currency.');
Cache::forget('FFCURRENCYSYMBOL');
Cache::forget('FFCURRENCYCODE');
return Redirect::route('currency.index');
}
/**
* @param TransactionCurrency $currency
*/
public function delete(TransactionCurrency $currency)
{
if ($currency->transactionJournals()->count() > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.');
return Redirect::route('currency.index');
}
return View::make('currency.delete', compact('currency'));
}
public function destroy(TransactionCurrency $currency)
{
Session::flash('success', 'Currency "' . e($currency->name) . '" deleted');
$this->_repository->destroy($currency);
return Redirect::route('currency.index');
}
/**
* @param TransactionCurrency $currency
*
* @return \Illuminate\View\View
*/
public function edit(TransactionCurrency $currency)
{
$subTitleIcon = 'fa-pencil';
$subTitle = 'Edit currency "' . e($currency->name) . '"';
$currency->symbol = htmlentities($currency->symbol);
return View::make('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
}
public function index()
{
$currencies = $this->_repository->get();
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$currencyPreference = $preferences->get('currencyPreference', 'EUR');
$defaultCurrency = $this->_repository->findByCode($currencyPreference->data);
return View::make('currency.index', compact('currencies', 'defaultCurrency'));
}
public function store()
{
$data = Input::except('_token');
// always validate:
$messages = $this->_repository->validate($data);
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store currency: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('currency.create')->withInput();
}
// store:
$this->_repository->store($data);
Session::flash('success', 'Currency "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('currency.index');
}
return Redirect::route('currency.create')->withInput();
}
public function update(TransactionCurrency $currency)
{
$data = Input::except('_token');
// always validate:
$messages = $this->_repository->validate($data);
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update currency: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('currency.edit', $currency->id)->withInput();
}
// update
$this->_repository->update($currency, $data);
Session::flash('success', 'Currency "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('currency.index');
}
return Redirect::route('currency.edit', $currency->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}

View File

@ -1,12 +1,41 @@
<?php
use Carbon\Carbon;
use FireflyIII\Chart\ChartInterface;
use Grumpydictator\Gchart\GChart as GChart;
/**
* Class GoogleChartController
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("MethodLength") // There is one with 45 lines and im gonna move it.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*/
class GoogleChartController extends BaseController
{
/** @var GChart */
protected $_chart;
/** @var Carbon */
protected $_end;
/** @var ChartInterface */
protected $_repository;
/** @var Carbon */
protected $_start;
/**
* @param GChart $chart
* @param ChartInterface $repository
*/
public function __construct(GChart $chart, ChartInterface $repository)
{
$this->_chart = $chart;
$this->_repository = $repository;
$this->_start = Session::get('start', Carbon::now()->startOfMonth());
$this->_end = Session::get('end', Carbon::now()->endOfMonth());
}
/**
* @param Account $account
* @param string $view
@ -15,199 +44,37 @@ class GoogleChartController extends BaseController
*/
public function accountBalanceChart(Account $account, $view = 'session')
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$this->_chart->addColumn('Day of month', 'date');
$this->_chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
// TODO this can be combined in some method, it's coming up quite often, is it?
$start = $this->_start;
$end = $this->_end;
$count = $account->transactions()->count();
/*
* 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;
if ($view == 'all' && $count > 0) {
$first = $account->transactions()->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->orderBy(
'date', 'ASC'
)->first(['transaction_journals.date']);
$last = $account->transactions()->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->orderBy(
'date', 'DESC'
)->first(['transaction_journals.date']);
$start = new Carbon($first->date);
$end = new Carbon($last->date);
}
// todo until this part.
$current = clone $start;
while ($end >= $current) {
$row = [clone $current];
if ($current > Carbon::now()) {
$row[] = null;
} else {
$row[] = Steam::balance($account, $current);
}
$chart->addRowArray($row);
$this->_chart->addRow(clone $current, Steam::balance($account, $current));
$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->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
@ -215,51 +82,34 @@ class GoogleChartController extends BaseController
*/
public function allAccountsBalanceChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day of the month', 'date');
$this->_chart->addColumn('Day of the month', 'date');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$pref = $preferences->get('frontpageAccounts', []);
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
if (count($pref->data) > 0) {
$accounts = $acct->getByIds($pref->data);
} else {
$accounts = $acct->getAssetAccounts();
}
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$accounts = count($pref->data) > 0 ? $acct->getByIds($pref->data) : $acct->getAssetAccounts();
/*
* Add a column for each account.
*/
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
$this->_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;
$current = clone $this->_start;
while ($end >= $current) {
while ($this->_end >= $current) {
$row = [clone $current];
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
}
$chart->addRowArray($row);
$this->_chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
@ -268,68 +118,49 @@ class GoogleChartController extends BaseController
*/
public function allBudgetsHomeChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$this->_chart->addColumn('Budget', 'string');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
Log::debug('Now in allBudgetsHomeChart()');
/** @var \FireflyIII\Database\Budget\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget\Budget');
$budgets = $bdt->get();
/*
* Loop budgets:
*/
/** @var Budget $budget */
foreach ($budgets as $budget) {
/*
* Is there a repetition starting on this particular date? We can use that.
*/
Log::debug('Now working budget #'.$budget->id.', '.$budget->name);
/** @var \LimitRepetition $repetition */
$repetition = $bdt->repetitionOnStartingOnDate($budget, Session::get('start'));
/*
* If there is, use it. Otherwise, forget it.
*/
$repetition = $bdt->repetitionOnStartingOnDate($budget, $this->_start);
if (is_null($repetition)) {
\Log::debug('Budget #'.$budget->id.' has no repetition on ' . $this->_start->format('Y-m-d'));
// use the session start and end for our search query
$searchStart = Session::get('start');
$searchEnd = Session::get('end');
// the limit is zero:
$limit = 0;
$searchStart = $this->_start;
$searchEnd = $this->_end;
$limit = 0; // the limit is zero:
} else {
\Log::debug('Budget #'.$budget->id.' has a repetition on ' . $this->_start->format('Y-m-d').'!');
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
// the limit is the repetitions limit:
$limit = floatval($repetition->amount);
$limit = floatval($repetition->amount); // the limit is the repetitions limit:
}
/*
* No matter the result of the search for the repetition, get all the transactions associated
* with the budget, and sum up the expenses made.
*/
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name, $limit, $expenses);
$this->_chart->addRow($budget->name, $limit, $expenses);
}
}
/*
* Finally, get all transactions WITHOUT a budget and add those as well.
* (yes this method is oddly specific).
*/
$noBudgetSet = $bdt->transactionsWithoutBudgetInDateRange(Session::get('start'), Session::get('end'));
$noBudgetSet = $bdt->transactionsWithoutBudgetInDateRange($this->_start, $this->_end);
$sum = $noBudgetSet->sum('amount') * -1;
$chart->addRow('No budget', 0, $sum);
$this->_chart->addRow('No budget', 0, $sum);
$this->_chart->generate();
$chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
@ -337,48 +168,26 @@ class GoogleChartController extends BaseController
*/
public function allCategoriesHomeChart()
{
$data = [];
$this->_chart->addColumn('Category', 'string');
$this->_chart->addColumn('Spent', 'number');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
// query!
$set = $this->_repository->getCategorySummary($this->_start, $this->_end);
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/*
* Get the journals:
*/
$journals = $tj->getInDateRange(Session::get('start'), Session::get('end'));
/** @var \TransactionJournal $journal */
foreach ($journals as $journal) {
if ($journal->transactionType->type == 'Withdrawal') {
$amount = $journal->getAmount();
$category = $journal->categories()->first();
if (!is_null($category)) {
if (isset($data[$category->name])) {
$data[$category->name] += $amount;
} else {
$data[$category->name] = $amount;
}
}
}
}
arsort($data);
foreach ($data as $key => $entry) {
$chart->addRow($key, $entry);
foreach ($set as $entry) {
$entry->name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$this->_chart->addRow($entry->name, floatval($entry->sum));
}
$this->_chart->generate();
$chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* TODO still in use?
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
@ -389,10 +198,8 @@ class GoogleChartController extends BaseController
$start = clone $repetition->startdate;
$end = $repetition->enddate;
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$this->_chart->addColumn('Day', 'date');
$this->_chart->addColumn('Left', 'number');
$amount = $repetition->amount;
@ -403,105 +210,42 @@ class GoogleChartController extends BaseController
*/
$sum = floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($start)->sum('amount'));
$amount += $sum;
$chart->addRow(clone $start, $amount);
$this->_chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* @param $year
* TODO still in use?
*
* @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 Budget $component
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function componentsAndSpending(Component $component, $year)
public function budgetsAndSpending(Budget $component, $year)
{
try {
$start = new Carbon('01-01-' . $year);
new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid year.');
}
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 \FireflyIII\Database\Budget\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget\Budget');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
$end = clone $start;
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$spent = $repos->spentInMonth($component, $start);
$repetition = $repos->repetitionOnStartingOnDate($component, $start);
if ($repetition) {
@ -510,15 +254,59 @@ class GoogleChartController extends BaseController
$budgeted = null;
}
$chart->addRow(clone $start, $budgeted, $spent);
$this->_chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* TODO still in use?
*
* @param Category $component
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoriesAndSpending(Category $component, $year)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \FireflyIII\Database\Category\Category $repos */
$repos = App::make('FireflyIII\Database\Category\Category');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$spent = $repos->spentInMonth($component, $start);
$budgeted = null;
$this->_chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
@ -530,20 +318,18 @@ class GoogleChartController extends BaseController
*/
public function piggyBankHistory(\Piggybank $piggybank)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
$this->_chart->addColumn('Date', 'date');
$this->_chart->addColumn('Balance', 'number');
$set = \DB::table('piggybank_events')->where('piggybank_id', $piggybank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
$set = \DB::table('piggy_bank_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));
$this->_chart->addRow(new Carbon($entry->date), floatval($entry->sum));
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
@ -555,12 +341,10 @@ class GoogleChartController extends BaseController
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');
$this->_chart->addColumn('Date', 'date');
$this->_chart->addColumn('Max amount', 'number');
$this->_chart->addColumn('Min amount', 'number');
$this->_chart->addColumn('Current entry', 'number');
// get first transaction or today for start:
$first = $recurring->transactionjournals()->orderBy('date', 'ASC')->first();
@ -578,98 +362,50 @@ class GoogleChartController extends BaseController
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $recurring->amount_max, $recurring->amount_min, $amount);
$this->_chart->addRow(clone $start, $recurring->amount_max, $recurring->amount_min, $amount);
$start = DateKit::addPeriod($start, $recurring->repeat_freq, 0);
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* TODO query move to helper.
*
* @return \Illuminate\Http\JsonResponse
* @throws \FireflyIII\Exception\FireflyException
*/
public function recurringTransactionsOverview()
{
/*
* Set of paid transaction journals.
* Set of unpaid recurring transactions.
*/
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
$this->_chart->addColumn('Name', 'string');
$this->_chart->addColumn('Amount', 'number');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
/** @var \FireflyIII\Database\Recurring $rcr */
$rcr = App::make('FireflyIII\Database\Recurring');
$recurring = $rcr->get();
/** @var \RecurringTransaction $entry */
foreach ($recurring as $entry) {
/*
* Start another loop starting at the $date.
*/
$start = clone $entry->date;
$end = Carbon::now();
/*
* The jump we make depends on the $repeat_freq
*/
$current = clone $start;
while ($current <= $end) {
/*
* Get end of period for $current:
*/
$currentEnd = DateKit::endOfPeriod($current, $entry->repeat_freq);
/*
* In the current session range?
*/
if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) {
/*
* Lets see if we've already spent money on this recurring transaction (it hath recurred).
*/
/** @var TransactionJournal $set */
$journal = $rcr->getJournalForRecurringInRange($entry, $current, $currentEnd);
if (is_null($journal)) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += (($entry->amount_max + $entry->amount_min) / 2);
} else {
$amount = $journal->getAmount();
$paid['items'][] = $journal->description;
$paid['amount'] += $amount;
}
}
/*
* Add some time for the next loop!
*/
$current = DateKit::addPeriod($current, $entry->repeat_freq, intval($entry->skip));
$set = $this->_repository->getRecurringSummary($this->_start, $this->_end);
foreach ($set as $entry) {
if (intval($entry->journalId) == 0) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += floatval($entry->averageAmount);
} else {
$paid['items'][] = $entry->description;
$paid['amount'] += floatval($entry->actualAmount);
}
}
/** @var \RecurringTransaction $entry */
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();
return Response::json($chart->getData());
$this->_chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$this->_chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
* TODO see reports for better way to do this.
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
@ -679,37 +415,37 @@ class GoogleChartController extends BaseController
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Income', 'number');
$this->_chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
// total income:
$income = $tj->getSumOfIncomesByMonth($start);
$expense = $tj->getSumOfExpensesByMonth($start);
$income = $repository->getSumOfIncomesByMonth($start);
$expense = $repository->getSumOfExpensesByMonth($start);
$chart->addRow(clone $start, $income, $expense);
$this->_chart->addRow(clone $start, $income, $expense);
$start->addMonth();
}
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
/**
* TODO see reports for better way to do this.
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
@ -719,16 +455,14 @@ class GoogleChartController extends BaseController
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$this->_chart->addColumn('Summary', 'string');
$this->_chart->addColumn('Income', 'number');
$this->_chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$end = clone $start;
$end->endOfYear();
@ -738,20 +472,20 @@ class GoogleChartController extends BaseController
while ($start < $end) {
// total income:
$income += $tj->getSumOfIncomesByMonth($start);
$expense += $tj->getSumOfExpensesByMonth($start);
$income += $repository->getSumOfIncomesByMonth($start);
$expense += $repository->getSumOfExpensesByMonth($start);
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$this->_chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$this->_chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
$this->_chart->generate();
return Response::json($chart->getData());
return Response::json($this->_chart->getData());
}
}

View File

@ -12,11 +12,9 @@ class HelpController extends BaseController
*/
public function show($route)
{
// no valid route
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
if (!Route::has($route)) {
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
@ -28,25 +26,17 @@ class HelpController extends BaseController
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';
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
try {
$content = file_get_contents($URL);
$content = file_get_contents($uri);
} catch (ErrorException $e) {
$content = '<p>There is no help for this route.</p>';
}
if (strlen($content) > 0) {
$helpText = \Michelf\Markdown::defaultTransform($content);
$helpTitle = $route;
$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 = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
Cache::put('help.' . $route . '.text', $helpText, 10080); // a week.
Cache::put('help.' . $route . '.title', $helpTitle, 10080);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);

View File

@ -1,4 +1,5 @@
<?php
use Carbon\Carbon;
/**
* Class HomeController
@ -22,19 +23,19 @@ class HomeController extends BaseController
public function index()
{
// count, maybe Firefly needs some introducing text to show:
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
/** @var \FireflyIII\Database\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal\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');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
// get the preference for the home accounts to show:
@ -47,7 +48,7 @@ class HomeController extends BaseController
$transactions = [];
foreach ($accounts as $account) {
$set = $jrnls->getInDateRangeAccount($account, 10, $start, $end);
$set = $jrnls->getInDateRangeAccount($account, $start, $end, 10);
if (count($set) > 0) {
$transactions[] = [$set, $account];
}
@ -76,7 +77,7 @@ class HomeController extends BaseController
Session::forget('range');
}
return Redirect::back();
return Redirect::intended('/');
}
/**
@ -86,7 +87,7 @@ class HomeController extends BaseController
{
Navigation::next();
return Redirect::back();
return Redirect::intended('/');
}
/**
@ -96,6 +97,6 @@ class HomeController extends BaseController
{
Navigation::prev();
return Redirect::back();
return Redirect::intended('/');
}
}

View File

@ -14,8 +14,8 @@ class JsonController extends BaseController
*/
public function categories()
{
/** @var \FireflyIII\Database\Category $categories */
$categories = App::make('FireflyIII\Database\Category');
/** @var \FireflyIII\Database\Category\Category $categories */
$categories = App::make('FireflyIII\Database\Category\Category');
$list = $categories->get();
$return = [];
foreach ($list as $entry) {
@ -34,8 +34,8 @@ class JsonController extends BaseController
*/
public function expenseAccounts()
{
/** @var \FireflyIII\Database\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account\Account');
$list = $accounts->getExpenseAccounts();
$return = [];
foreach ($list as $entry) {
@ -51,8 +51,8 @@ class JsonController extends BaseController
*/
public function revenueAccounts()
{
/** @var \FireflyIII\Database\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account\Account');
$list = $accounts->getRevenueAccounts();
$return = [];
foreach ($list as $entry) {

View File

@ -1,21 +1,35 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\PiggyBank\PiggyBank as Repository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
*
* Class PiggybankController
*
*/
class PiggybankController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
*
* @param Repository $repository
*/
public function __construct()
public function __construct(Repository $repository)
{
$this->_repository = $repository;
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
/**
@ -27,16 +41,23 @@ class PiggybankController extends BaseController
*/
public function add(Piggybank $piggybank)
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
\Log::debug('Now in add() for piggy bank #' . $piggybank->id . ' (' . $piggybank->name . ')');
\Log::debug('Z');
\Log::debug('currentRelevantRep is null: ' . boolstr($piggybank->currentRelevantRep()));
$leftOnAccount = $this->_repository->leftOnAccount($piggybank->account);
\Log::debug('A');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$amount = min($leftOnAccount, $leftToSave);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
\Log::debug('B');
$leftToSave = $piggybank->targetamount - $savedSoFar;
\Log::debug('C');
$maxAmount = min($leftOnAccount, $leftToSave);
\Log::debug('D');
return View::make('piggybanks.add', compact('piggybank'))->with('maxAmount', $amount);
\Log::debug('Now going to view for piggy bank #' . $piggybank->id . ' (' . $piggybank->name . ')');
return View::make('piggybanks.add', compact('piggybank', 'maxAmount'));
}
/**
@ -45,17 +66,15 @@ class PiggybankController extends BaseController
public function create()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggybank_periods');
$periods = Config::get('firefly.piggybank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus';
$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');
return View::make('piggybanks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon'));
}
/**
@ -65,9 +84,9 @@ class PiggybankController extends BaseController
*/
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');
$subTitle = 'Delete "' . e($piggybank->name) . '"';
return View::make('piggybanks.delete', compact('piggybank', 'subTitle'));
}
/**
@ -77,10 +96,9 @@ class PiggybankController extends BaseController
*/
public function destroy(Piggybank $piggyBank)
{
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$repos->destroy($piggyBank);
Session::flash('success', 'Piggy bank deleted.');
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$this->_repository->destroy($piggyBank);
return Redirect::route('piggybanks.index');
}
@ -93,28 +111,33 @@ class PiggybankController extends BaseController
public function edit(Piggybank $piggybank)
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggybank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$periods = Config::get('firefly.piggybank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$subTitle = 'Edit piggy bank "' . e($piggybank->name) . '"';
$subTitleIcon = 'fa-pencil';
/*
* Flash some data to fill the form.
*/
if (is_null($piggybank->targetdate) || $piggybank->targetdate == '') {
$targetDate = null;
} else {
$targetDate = new Carbon($piggybank->targetdate);
$targetDate = $targetDate->format('Y-m-d');
}
$preFilled = ['name' => $piggybank->name,
'account_id' => $piggybank->account_id,
'targetamount' => $piggybank->targetamount,
'targetdate' => !is_null($piggybank->targetdate) ? $piggybank->targetdate->format('Y-m-d') : null,
'targetdate' => $targetDate,
'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');
return View::make('piggybanks.edit', compact('subTitle', 'subTitleIcon', 'piggybank', 'accounts', 'periods', 'preFilled'));
}
/**
@ -122,11 +145,8 @@ class PiggybankController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
/** @var Collection $piggybanks */
$piggybanks = $repos->get();
$piggybanks = $this->_repository->get();
$accounts = [];
/** @var Piggybank $piggybank */
@ -140,9 +160,14 @@ class PiggybankController extends BaseController
*/
$account = $piggybank->account;
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = ['name' => $account->name, 'balance' => Steam::balance($account),
'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' => $this->_repository->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);
@ -150,7 +175,7 @@ class PiggybankController extends BaseController
}
}
return View::make('piggybanks.index', compact('piggybanks', 'accounts'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.index', compact('piggybanks', 'accounts'));
}
/**
@ -164,8 +189,8 @@ class PiggybankController extends BaseController
{
$amount = round(floatval(Input::get('amount')), 2);
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
/** @var \FireflyIII\Database\PiggyBank\PiggyBank $acct */
$repos = App::make('FireflyIII\Database\PiggyBank\PiggyBank');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
@ -226,7 +251,7 @@ class PiggybankController extends BaseController
*/
public function remove(Piggybank $piggybank)
{
return View::make('piggybanks.remove', compact('piggybank'));
return View::make('piggybanks.remove')->with('piggybank', $piggybank);
}
/**
@ -245,12 +270,9 @@ class PiggybankController extends BaseController
$amountPerReminder = $piggybank->amountPerReminder();
$remindersCount = $piggybank->countFutureReminders();
$subTitle = e($piggybank->name);
return View::make('piggybanks.show', compact('amountPerReminder', 'remindersCount', 'piggybank', 'events'))->with('title', 'Piggy banks')->with(
'mainTitleIcon', 'fa-sort-amount-asc'
)->with(
'subTitle', $piggybank->name
);
return View::make('piggybanks.show', compact('amountPerReminder', 'remindersCount', 'piggybank', 'events', 'subTitle'));
}
@ -261,49 +283,35 @@ class PiggybankController extends BaseController
{
$data = Input::all();
$data['repeats'] = 0;
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data['user_id'] = Auth::user()->id;
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggybanks.create')->withInput()->withErrors($messages['errors']);
}
// store!
$piggyBank = $repos->store($data);
// always validate:
$messages = $this->_repository->validate($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')->withInput();
} else {
return Redirect::route('piggybanks.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.create')->withInput();
break;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store piggy bank: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('piggybanks.create')->withInput();
}
// store:
$piggyBank = $this->_repository->store($data);
Event::fire('piggybank.store', [$piggyBank]); // new and used.
Session::flash('success', 'Piggy bank "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('piggybanks.index');
}
return Redirect::route('piggybanks.create')->withInput();
}
/**
@ -315,45 +323,40 @@ class PiggybankController extends BaseController
public function update(Piggybank $piggyBank)
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data = Input::except('_token');
$data = Input::except('_token');
$data['rep_every'] = 0;
$data['reminder_skip'] = 0;
$data['order'] = 0;
$data['remind_me'] = isset($data['remind_me']) ? 1 : 0;
$data['user_id'] = Auth::user()->id;
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 piggy bank: ' . $messages['errors']->first());
// always validate:
$messages = $this->_repository->validate($data);
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'] == 'return_to_edit') {
return Redirect::route('piggybanks.edit', $piggyBank->id);
} else {
return Redirect::route('piggybanks.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput();
break;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update piggy bank: ' . $messages['errors']->first());
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput();
}
// update
$this->_repository->update($piggyBank, $data);
Session::flash('success', 'Piggy bank "' . e($data['name']) . '" updated.');
// go back to list
if ($data['post_submit_action'] == 'update') {
return Redirect::route('piggybanks.index');
}
// go back to update screen.
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput(['post_submit_action' => 'return_to_edit']);
}
}

View File

@ -3,6 +3,8 @@
/**
* Class PreferencesController
*
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
*/
class PreferencesController extends BaseController
{
@ -21,8 +23,8 @@ class PreferencesController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
@ -30,9 +32,13 @@ class PreferencesController extends BaseController
$accounts = $acct->getAssetAccounts();
$viewRange = $preferences->get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
$frontpage = $preferences->get('frontpageAccounts', []);
$frontPage = $preferences->get('frontpageAccounts', []);
$budgetMax = $preferences->get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return View::make('preferences.index')->with('accounts', $accounts)->with('frontpageAccounts', $frontpage)->with('viewRange', $viewRangeValue);
return View::make('preferences.index', compact('budgetMaximum'))->with('accounts', $accounts)->with('frontpageAccounts', $frontPage)->with(
'viewRange', $viewRangeValue
);
}
/**
@ -40,7 +46,6 @@ class PreferencesController extends BaseController
*/
public function postIndex()
{
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
@ -58,6 +63,11 @@ class PreferencesController extends BaseController
Session::forget('end');
Session::forget('range');
// budget maximum:
$budgetMaximum = intval(Input::get('budgetMaximum'));
$preferences->set('budgetMaximum', $budgetMaximum);
Session::flash('success', 'Preferences saved!');
return Redirect::route('preferences');

View File

@ -32,7 +32,6 @@ class ProfileController extends BaseController
{
// old, new1, new2
/** @noinspection PhpUndefinedFieldInspection */
if (!Hash::check(Input::get('old'), Auth::user()->password)) {
Session::flash('error', 'Invalid current password!');
@ -56,8 +55,8 @@ class ProfileController extends BaseController
}
// update the user with the new password.
/** @var \FireflyIII\Database\User $repository */
$repository = \App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = \App::make('FireflyIII\Database\User\User');
$repository->updatePassword(Auth::user(), Input::get('new1'));
Session::flash('success', 'Password changed!');

View File

@ -49,8 +49,8 @@ class RecurringController extends BaseController
{
//Event::fire('recurring.destroy', [$recurringTransaction]);
/** @var \FireflyIII\Database\Recurring $repository */
$repository = App::make('FireflyIII\Database\Recurring');
/** @var \FireflyIII\Database\RecurringTransaction\RecurringTransaction $repository */
$repository = App::make('FireflyIII\Database\RecurringTransaction\RecurringTransaction');
$result = $repository->destroy($recurringTransaction);
if ($result === true) {
@ -82,8 +82,8 @@ class RecurringController extends BaseController
*/
public function index()
{
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
/** @var \FireflyIII\Database\RecurringTransaction\RecurringTransaction $repos */
$repos = App::make('FireflyIII\Database\RecurringTransaction\RecurringTransaction');
$recurring = $repos->get();
@ -103,8 +103,8 @@ class RecurringController extends BaseController
return Redirect::back();
}
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
/** @var \FireflyIII\Database\RecurringTransaction\RecurringTransaction $repos */
$repos = App::make('FireflyIII\Database\RecurringTransaction\RecurringTransaction');
$repos->scanEverything($recurringTransaction);
Session::flash('success', 'Rescanned everything.');
@ -135,8 +135,8 @@ class RecurringController extends BaseController
public function store()
{
$data = Input::except('_token');
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
/** @var \FireflyIII\Database\RecurringTransaction\RecurringTransaction $repos */
$repos = App::make('FireflyIII\Database\RecurringTransaction\RecurringTransaction');
switch ($data['post_submit_action']) {
default:
@ -183,9 +183,11 @@ class RecurringController extends BaseController
*/
public function update(RecurringTransaction $recurringTransaction)
{
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$data = Input::except('_token');
/** @var \FireflyIII\Database\RecurringTransaction\RecurringTransaction $repos */
$repos = App::make('FireflyIII\Database\RecurringTransaction\RecurringTransaction');
$data = Input::except('_token');
$data['active'] = isset($data['active']) ? 1 : 0;
$data['automatch'] = isset($data['automatch']) ? 1 : 0;
switch (Input::get('post_submit_action')) {
default:

View File

@ -1,21 +1,25 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\PiggyBank\RepeatedExpense as Repository;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class RepeatedExpenseController
*/
class RepeatedExpenseController extends BaseController
{
/** @var Repository */
protected $_repository;
/**
*
* @param Repository $repository
*/
public function __construct()
public function __construct(Repository $repository)
{
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
$this->_repository = $repository;
}
/**
@ -23,8 +27,8 @@ class RepeatedExpenseController extends BaseController
*/
public function create()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggybank_periods');
@ -44,8 +48,8 @@ class RepeatedExpenseController extends BaseController
$subTitle = 'Overview';
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
/** @var \FireflyIII\Database\PiggyBank\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\PiggyBank\RepeatedExpense');
$expenses = $repository->get();
$expenses->each(
@ -67,13 +71,13 @@ class RepeatedExpenseController extends BaseController
$subTitle = $piggyBank->name;
$today = Carbon::now();
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
/** @var \FireflyIII\Database\PiggyBank\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\PiggyBank\RepeatedExpense');
$repetitions = $piggyBank->piggybankrepetitions()->get();
$repetitions->each(
function (PiggybankRepetition $repetition) use ($repository) {
$repository->calculateParts($repetition);
$repetition->bars = $repository->calculateParts($repetition);
}
);
@ -86,50 +90,36 @@ class RepeatedExpenseController extends BaseController
*/
public function store()
{
$data = Input::all();
$data = Input::except('_token');
$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());
// always validate:
$messages = $this->_repository->validate($data);
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;
// flash messages:
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not validate repeated expense: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('repeated.create')->withInput();
}
// store:
$this->_repository->store($data);
Session::flash('success', 'Budget "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
return Redirect::route('repeated.index');
}
// create another.
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('repeated.create')->withInput();
}
return Redirect::route('repeated.index');
}
}

View File

@ -1,11 +1,11 @@
<?php
use Carbon\Carbon;
use FireflyIII\Database\Account as AccountRepository;
use FireflyIII\Database\Report as ReportRepository;
use FireflyIII\Database\TransactionJournal as TransactionJournalRepository;
use FireflyIII\Report\ReportInterface as ReportHelper;
use FireflyIII\Database\Account\Account as AccountRepository;
use FireflyIII\Database\TransactionJournal\TransactionJournal as TransactionJournalRepository;
use FireflyIII\Report\ReportInterface as Report;
/**
*
* Class ReportController
*/
class ReportController extends BaseController
@ -16,138 +16,30 @@ class ReportController extends BaseController
/** @var TransactionJournalRepository */
protected $_journals;
/** @var ReportHelper */
protected $_reports;
/** @var ReportRepository */
/** @var Report */
protected $_repository;
/**
* @param AccountRepository $accounts
* @param TransactionJournalRepository $journals
* @param ReportHelper $reports
* @param ReportRepository $repository
* @param Report $repository
*/
public function __construct(AccountRepository $accounts, TransactionJournalRepository $journals, ReportHelper $reports, ReportRepository $repository)
public function __construct(AccountRepository $accounts, TransactionJournalRepository $journals, Report $repository)
{
$this->_accounts = $accounts;
$this->_journals = $journals;
$this->_reports = $reports;
$this->_repository = $repository;
}
/**
* @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'));
}
/**
*
*/
public function index()
{
$start = $this->_journals->firstDate();
$months = $this->_reports->listOfMonths(clone $start);
$years = $this->_reports->listOfYears(clone $start);
$months = $this->_repository->listOfMonths(clone $start);
$years = $this->_repository->listOfYears(clone $start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
@ -163,7 +55,7 @@ class ReportController extends BaseController
public function unbalanced($year, $month)
{
try {
$date = new Carbon($year . '-' . $month . '-01');
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
App::abort(500);
}
@ -175,61 +67,37 @@ class ReportController extends BaseController
$subTitleIcon = 'fa-bar-chart';
$end->endOfMonth();
/** @var \FireflyIII\Database\TransactionJournal $journalRepository */
$journalRepository = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $journalRepository */
$journalRepository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$journals = $journalRepository->getInDateRange($start, $end);
/*
* 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;
}
$relations = $journal->transactiongroups()->where('relation', 'balance')->count();
$budgets = $journal->budgets()->count();
$type = $journal->transactionType->type;
if ($type == 'Withdrawal' && $budgets == 0 && $relations == 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;
}
$relations = $journal->transactiongroups()->where('relation', 'balance')->count();
$budgets = $journal->budgets()->count();
$type = $journal->transactionType->type;
if ($type == 'Deposit' && $budgets == 0 && $relations == 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'));
}
@ -245,74 +113,21 @@ class ReportController extends BaseController
} catch (Exception $e) {
App::abort(500);
}
$date = new Carbon('01-01-' . $year);
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $this->_reports->yearBalanceReport($date);
$groupedIncomes = $this->_reports->groupByRevenue($date, 'income');
$groupedExpenses = $this->_reports->groupByRevenue($date, 'expense');
$balances = $this->_repository->yearBalanceReport($date);
$groupedIncomes = $this->_repository->revenueGroupedByAccount($date, $end, 15);
$groupedExpenses = $this->_repository->expensesGroupedByAccount($date, $end, 15);
return View::make(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
);
/*
* For this year, get:
* - the sum of all expenses.
* - the sum of all incomes
* - per month, the sum of all expenses
* - per month, the sum of all incomes
* - 2x for shared and not-shared alike.
*
* - balance difference for all accounts.
*/
$accounts = $accountRepository->getAssetAccounts();
// get some sums going
$summary = [];
$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->accountRole == 'sharedExpense') {
$incomeShared += $reportRepository->getIncomeByMonth($account, $date);
$expenseShared += $reportRepository->getExpenseByMonth($account, $date);
} else {
$income += $reportRepository->getIncomeByMonth($account, $date);
$expense += $reportRepository->getExpenseByMonth($account, $date);
}
}
$summary[] = [
'month' => $month,
'income' => $income,
'expense' => $expense,
'incomeShared' => $incomeShared,
'expenseShared' => $expenseShared,
];
$date->addMonth();
}
// draw some charts etc.
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);
}
}

View File

@ -45,11 +45,11 @@ class TransactionController extends BaseController
}
}
$unique = array_unique($ids);
if (count($ids) > 0) {
if (count($unique) > 0) {
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$set = $repository->getByIds($ids);
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$set = $repository->getByIds($unique);
$set->each(
function (TransactionJournal $journal) {
$journal->amount = mf($journal->getAmount());
@ -71,21 +71,17 @@ class TransactionController extends BaseController
*/
public function create($what = 'deposit')
{
/*
* The repositories we need:
*/
/** @var \FireflyIII\Database\Account\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account\Account');
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Budget\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget\Budget');
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\PiggyBank\PiggyBank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\PiggyBank\PiggyBank');
/** @var \FireflyIII\Database\Piggybank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\Piggybank');
/** @var \FireflyIII\Database\RepeatedExpense $repRepository */
$repRepository = App::make('FireflyIII\Database\RepeatedExpense');
/** @var \FireflyIII\Database\PiggyBank\RepeatedExpense $repRepository */
$repRepository = App::make('FireflyIII\Database\PiggyBank\RepeatedExpense');
// get asset accounts with names and id's .
$assetAccounts = FFForm::makeSelectList($accountRepository->getAssetAccounts());
@ -100,9 +96,6 @@ class TransactionController extends BaseController
$piggies[0] = '(no piggy bank)';
asort($piggies);
/*
* respond to a possible given values in the URL.
*/
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id'];
foreach ($respondTo as $r) {
@ -143,8 +136,8 @@ class TransactionController extends BaseController
{
$type = $transactionJournal->transactionType->type;
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$repository->destroy($transactionJournal);
$return = 'withdrawal';
@ -172,8 +165,8 @@ class TransactionController extends BaseController
$id = intval(Input::get('id'));
$sister = intval(Input::get('relateTo'));
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$journal = $repository->find($id);
$sis = $repository->find($sister);
@ -207,39 +200,25 @@ class TransactionController extends BaseController
* All the repositories we need:
*/
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Account\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account\Account');
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\Budget\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget\Budget');
/** @var \FireflyIII\Database\Piggybank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\Piggybank');
/** @var \FireflyIII\Database\PiggyBank\PiggyBank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\PiggyBank\PiggyBank');
// type is useful for display:
$what = strtolower($journal->transactiontype->type);
// get asset accounts with names and id's.
$budgets = FFForm::makeSelectList($budgetRepository->get(), true);
$accounts = FFForm::makeSelectList($accountRepository->getAssetAccounts());
$piggies = FFForm::makeSelectList($piggyRepository->get(), true);
// get budgets as a select list.
$budgets = FFForm::makeSelectList($budgetRepository->get());
$budgets[0] = '(no budget)';
/*
* Get all piggy banks plus (if any) the relevant piggy bank. Since just one
* of the transactions in the journal has this field, it should all fill in nicely.
*/
// get the piggy banks.
$piggies = FFForm::makeSelectList($piggyRepository->get());
$piggies[0] = '(no piggy bank)';
$piggyBankId = 0;
foreach ($journal->transactions as $t) {
if (!is_null($t->piggybank_id)) {
$piggyBankId = $t->piggybank_id;
}
}
/*
* Data to properly display the edit form.
@ -248,7 +227,7 @@ class TransactionController extends BaseController
'date' => $journal->date->format('Y-m-d'),
'category' => '',
'budget_id' => 0,
'piggybank_id' => $piggyBankId
'piggybank_id' => 0
];
/*
@ -335,8 +314,8 @@ class TransactionController extends BaseController
public function index($what)
{
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
switch ($what) {
case 'expenses':
@ -396,8 +375,8 @@ class TransactionController extends BaseController
{
$search = e(trim(Input::get('searchValue')));
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$result = $repository->searchRelated($search, $journal);
$result->each(
@ -456,8 +435,8 @@ class TransactionController extends BaseController
$data['what'] = $what;
$data['currency'] = 'EUR';
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
switch ($data['post_submit_action']) {
default:
@ -482,11 +461,7 @@ class TransactionController extends BaseController
* Trigger a search for the related (if selected)
* piggy bank and store an event.
*/
$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.
Event::fire('transactionJournal.store', [$journal, Input::get('piggybank_id')]); // new and used.
/*
* Also trigger on both transactions.
*/
@ -544,12 +519,13 @@ class TransactionController extends BaseController
/**
* @param TransactionJournal $journal
*
* @return $this
* @throws FireflyException
*/
public function update(TransactionJournal $journal)
{
/** @var \FireflyIII\Database\TransactionJournal $repos */
$repos = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $repos */
$repos = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$data = Input::except('_token');
$data['currency'] = 'EUR';

View File

@ -71,8 +71,8 @@ class UserController extends BaseController
return View::make('error')->with('message', 'Not possible');
}
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = App::make('FireflyIII\Database\User\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
@ -105,8 +105,8 @@ class UserController extends BaseController
public function postRemindme()
{
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = App::make('FireflyIII\Database\User\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
@ -163,8 +163,8 @@ class UserController extends BaseController
public function reset($reset)
{
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Database\User\User $repository */
$repository = App::make('FireflyIII\Database\User\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');

View File

@ -5,8 +5,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateUsersTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateUsersTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateAccountTypesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateAccountTypesTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateAccountsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateAccountsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePiggybanksTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePiggybanksTable extends Migration
{
@ -46,7 +45,7 @@ class CreatePiggybanksTable extends Migration
$table->boolean('remind_me');
$table->integer('order')->unsigned();
// connect account to piggybank.
// connect account to piggy bank.
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
// for an account, the name must be unique.

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionCurrenciesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionCurrenciesTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionTypesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionTypesTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateRecurringTransactionsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateRecurringTransactionsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionJournalsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionJournalsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentTransactionTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentTransactionTable extends Migration
{
@ -18,7 +17,7 @@ class CreateComponentTransactionTable extends Migration
*/
public function down()
{
Schema::drop('component_transaction');
Schema::dropIfExists('component_transaction');
}
/**

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentTransactionJournalTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentTransactionJournalTable extends Migration
{
@ -30,19 +29,19 @@ class CreateComponentTransactionJournalTable extends Migration
{
Schema::create(
'component_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
// link components with component_id
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link components with component_id
$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');
// link transaction journals with transaction_journal_id
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
// combo must be unique:
$table->unique(['component_id', 'transaction_journal_id'],'cid_tjid_unique');
}
// combo must be unique:
$table->unique(['component_id', 'transaction_journal_id'], 'cid_tjid_unique');
}
);
}

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePreferencesTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePreferencesTable extends Migration
{

View File

@ -5,7 +5,6 @@ use Illuminate\Database\Migrations\Migration;
/**
* Class CreateSessionTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateSessionTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateLimitsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateLimitsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateLimitRepeatTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateLimitRepeatTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateComponentRecurringTransactionTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateComponentRecurringTransactionTable extends Migration
{
@ -18,7 +17,7 @@ class CreateComponentRecurringTransactionTable extends Migration
*/
public function down()
{
Schema::drop('component_recurring_transaction');
Schema::dropIfExists('component_recurring_transaction');
}
/**
@ -30,21 +29,21 @@ class CreateComponentRecurringTransactionTable extends Migration
{
Schema::create(
'component_recurring_transaction', function (Blueprint $table) {
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('recurring_transaction_id')->unsigned();
$table->boolean('optional');
$table->increments('id');
$table->integer('component_id')->unsigned();
$table->integer('recurring_transaction_id')->unsigned();
$table->boolean('optional');
// link components with component_id
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link components with component_id
$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');
// link transaction journals with transaction_journal_id
$table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('cascade');
// component and recurring transaction must be unique.
$table->unique(['component_id','recurring_transaction_id'],'cid_rtid_unique');
// component and recurring transaction must be unique.
$table->unique(['component_id', 'recurring_transaction_id'], 'cid_rtid_unique');
}
}
);
}

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePiggyInstance
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePiggybankRepetitionsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreatePiggybankEventsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreatePiggybankEventsTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateRemindersTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateRemindersTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateAccountMetaTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateAccountMetaTable extends Migration
{

View File

@ -6,7 +6,6 @@ use Illuminate\Database\Schema\Blueprint;
/**
* Class CreateTransactionGroupsTable
*
* @SuppressWarnings(PHPMD.ShortMethodName)
*/
class CreateTransactionGroupsTable extends Migration
{

View File

@ -0,0 +1,634 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Down:
* 1. Create new Components based on Budgets.
* 2. Create new Components based on Categories
* 3. Recreate component_id in limits
* 4. Update all budget_limits entries (component_id).
* 5. Add the foreign key to component_id in budget_limits
* 6. Drop column 'budget_id' in budget_limits.
* 7. Create table journal_components.
* 8. create entries for budgets in journal_components.
* 9. create entries for categories in journal_components.
* 10. drop table budget_journals
* 11. drop table category_journals
* 12. drop table budgets
* 13. drop table categories.
* 14. rename budget_limits to limits.
* 15. Rename piggy_bank_events to piggybank_events
* 16. Rename field 'budget_limit_id' to 'limit_id' in 'limit_repetitions'
* 17. Do not recreate component_recurring_transaction
* 18. Do not recreate component_transaction
* 19. Do not recreate field 'piggybank_id' in 'transactions'
* 20. Drop fields from currency table.
*
*
*
* Up:
*
* 1. Create new budget table.
* 2. Create new category table.
* 3. Create journal_budget table.
* 4. Create journal_category table.
* 5. Move budgets to new budgets table AND move journal_components to budget_components.
* 6. Move categories to categories table AND move journal_components to category_components.
* 7. Rename limits to budget_limits.
* 8. Rename piggybank_events to piggy_bank_events
* 9. Rename field 'limit_id' to 'budget_limit_id' in 'limit_repetitions'
* 10. Create field budget_id in budget_limits.
* 11. Update budget_limits with budgets (instead of components).
* 12. drop table journal_components
* 13. Drop table component_recurring_transaction
* 14. Drop table component_transaction
* 15. Drop field 'piggybank_id' from 'transactions'
* 16. Drop field 'component_id' from 'budget_limits'
* 17. Expand currency table with new fields.
*
* Class ChangesForV321
*/
class ChangesForV321 extends Migration
{
public function down()
{
$this->moveBudgetsBack(); // 1.
$this->moveCategoriesBack(); // 2.
$this->createComponentId(); // 3.
$this->updateComponentInBudgetLimits(); // 4.
$this->createComponentIdForeignKey(); // 5.
$this->dropBudgetIdColumnInBudgetLimits(); // 6.
$createComponents = new CreateComponentTransactionJournalTable; // 7.
$createComponents->up();
$this->moveBackEntriesForBudgetsInJoinedTable(); // 8.
$this->moveBackEntriesForCategoriesInJoinedTable(); // 9.
$this->dropBudgetJournalTable(); // 10.
$this->dropCategoryJournalTable(); // 11.
$this->dropBudgetTable(); // 12.
$this->dropCategoryTable(); // 13.
$this->renameBudgetLimits(); // 14.
$this->renamePiggyBankEvents(); // 15.
$this->renameBudgetLimitToBudgetInRepetitions(); // 16.
// 17, 18, 19
$this->dropFieldsFromCurrencyTable(); // 20.
}
public function moveBudgetsBack()
{
Budget::get()->each(
function (Budget $budget) {
Component::firstOrCreate(
[
'name' => $budget->name,
'user_id' => $budget->user_id,
'class' => 'Budget'
]
);
}
);
}
public function moveCategoriesBack()
{
Category::get()->each(
function (Category $category) {
Component::firstOrCreate(
[
'name' => $category->name,
'user_id' => $category->user_id,
'class' => 'Category'
]
);
}
);
}
public function createComponentId()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->integer('component_id')->unsigned();
}
);
}
public function updateComponentInBudgetLimits()
{
BudgetLimit::get()->each(
function (BudgetLimit $bl) {
$budgetId = $bl->budget_id;
$budget = Budget::find($budgetId);
if ($budget) {
$component = Component::where('class', 'Budget')->where('user_id', $budget->user_id)->where('name', $budget->name)->first();
if ($component) {
$bl->component_id = $component->id;
$bl->save();
}
}
}
);
}
public function createComponentIdForeignKey()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->foreign('component_id', 'limits_component_id_foreign')->references('id')->on('components')->onDelete('cascade');
}
);
}
public function dropBudgetIdColumnInBudgetLimits()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropForeign('bid_foreign');
$table->dropColumn('budget_id'); // also drop foreign key!
}
);
}
public function moveBackEntriesForBudgetsInJoinedTable()
{
$set = DB::table('budget_transaction_journal')->get();
foreach ($set as $entry) {
$budget = Budget::find($entry->budget_id);
if ($budget) {
$component = Component::where('class', 'Budget')->where('name', $budget->name)->where('user_id', $budget->user_id)->first();
if ($component) {
DB::table('component_transaction_journal')->insert(
[
'component_id' => $component->id,
'transaction_journal_id' => $entry->transaction_journal_id
]
);
}
}
}
}
public function moveBackEntriesForCategoriesInJoinedTable()
{
$set = DB::table('category_transaction_journal')->get();
foreach ($set as $entry) {
$category = Category::find($entry->category_id);
if ($category) {
$component = Component::where('class', 'Category')->where('name', $category->name)->where('user_id', $category->user_id)->first();
if ($component) {
DB::table('component_transaction_journal')->insert(
[
'component_id' => $component->id,
'transaction_journal_id' => $entry->transaction_journal_id
]
);
}
}
}
}
public function dropBudgetJournalTable()
{
Schema::dropIfExists('budget_transaction_journal');
}
public function dropCategoryJournalTable()
{
Schema::dropIfExists('category_transaction_journal');
}
public function dropBudgetTable()
{
Schema::dropIfExists('budgets');
}
public function dropCategoryTable()
{
Schema::dropIfExists('categories');
}
public function renameBudgetLimits()
{
Schema::rename('budget_limits', 'limits');
}
public function renamePiggyBankEvents()
{
Schema::rename('piggy_bank_events', 'piggybank_events');
}
public function renameBudgetLimitToBudgetInRepetitions()
{
Schema::table(
'limit_repetitions', function (Blueprint $table) {
$table->renameColumn('budget_limit_id', 'limit_id');
}
);
}
public function dropFieldsFromCurrencyTable()
{
Schema::table(
'transaction_currencies', function (Blueprint $table) {
$table->dropColumn('symbol');
$table->dropColumn('name');
}
);
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$this->createBudgetTable(); // 1.
$this->createCategoryTable(); // 2.
$this->createBudgetJournalTable(); // 3
$this->createCategoryJournalTable(); // 4.
$this->moveBudgets(); // 5.
$this->moveCategories(); // 6.
$this->correctNameForBudgetLimits(); // 7.
$this->correctNameForPiggyBankEvents(); // 8.
$this->renameBudgetToBudgetLimitInRepetitions(); // 9.
$this->addBudgetIdFieldToBudgetLimits(); // 10.
$this->moveComponentIdToBudgetId(); // 11.
$this->dropComponentJournalTable(); // 12.
$this->dropComponentRecurringTransactionTable(); // 13.
$this->dropComponentTransactionTable(); // 14.
$this->dropPiggyBankIdFromTransactions(); // 15.
$this->dropComponentIdFromBudgetLimits(); // 16.
$this->expandCurrencyTable(); // 17.
// $this->doRenameInLimitRepetitions();
// $this->doBudgetLimits();
// $this->doPiggyBankEvents();
// $this->doCreateCategoryTables();
// $this->doUpdateTransactionTable();
// $this->doDropCompRecurTable();
// $this->doDropCompTransTable();
// $this->doMoveBudgets();
// $this->doMoveCategories();
// $this->doMoveLimitReferences();
}
public function createBudgetTable()
{
Schema::create(
'budgets', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'name']);
}
);
}
public function createCategoryTable()
{
Schema::create(
'categories', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'name']);
}
);
}
public function createBudgetJournalTable()
{
Schema::create(
'budget_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('budget_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->unique(['budget_id', 'transaction_journal_id'], 'budid_tjid_unique');
}
);
}
public function createCategoryJournalTable()
{
Schema::create(
'category_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->unique(['category_id', 'transaction_journal_id'], 'catid_tjid_unique');
}
);
}
public function moveBudgets()
{
Component::where('class', 'Budget')->get()->each(
function (Component $c) {
$entry = [
'user_id' => $c->user_id,
'name' => $c->name
];
$budget = Budget::firstOrCreate($entry);
Log::debug('Migrated budget #' . $budget->id . ': ' . $budget->name);
// create entry in budget_transaction_journal
$connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get();
foreach ($connections as $connection) {
DB::table('budget_transaction_journal')->insert(
[
'budget_id' => $budget->id,
'transaction_journal_id' => $connection->transaction_journal_id
]
);
}
}
);
}
public function moveCategories()
{
Component::where('class', 'Category')->get()->each(
function (Component $c) {
$entry = [
'user_id' => $c->user_id,
'name' => $c->name
];
$category = Category::firstOrCreate($entry);
Log::debug('Migrated category #' . $category->id . ': ' . $category->name);
// create entry in category_transaction_journal
$connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get();
foreach ($connections as $connection) {
DB::table('category_transaction_journal')->insert(
[
'category_id' => $category->id,
'transaction_journal_id' => $connection->transaction_journal_id
]
);
}
}
);
}
public function correctNameForBudgetLimits()
{
Schema::rename('limits', 'budget_limits');
}
public function correctNameForPiggyBankEvents()
{
Schema::rename('piggybank_events', 'piggy_bank_events');
}
public function renameBudgetToBudgetLimitInRepetitions()
{
Schema::table(
'limit_repetitions', function (Blueprint $table) {
$table->renameColumn('limit_id', 'budget_limit_id');
}
);
}
public function addBudgetIdFieldToBudgetLimits()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->integer('budget_id', false, true)->nullable()->after('updated_at');
$table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade');
}
);
}
public function moveComponentIdToBudgetId()
{
\Log::debug('Now in moveComponentIdToBudgetId()');
BudgetLimit::get()->each(
function (BudgetLimit $bl) {
\Log::debug('Now at budgetLimit #' . $bl->id . ' with component_id: ' . $bl->component_id);
$component = Component::find($bl->component_id);
if ($component) {
\Log::debug('Found component with id #' . $component->id . ' and name ' . $component->name);
$budget = Budget::whereName($component->name)->whereUserId($component->user_id)->first();
if ($budget) {
\Log::debug('Found a budget with ID #' . $budget->id . ' and name ' . $budget->name);
$bl->budget_id = $budget->id;
$bl->save();
\Log::debug('Connected budgetLimit #' . $bl->id . ' to budget_id' . $budget->id);
} else {
\Log::debug('Could not find a matching budget with name ' . $component->name);
}
} else {
\Log::debug('Could not find a component with id ' . $bl->component_id);
}
}
);
\Log::debug('Done with moveComponentIdToBudgetId()');
}
public function dropComponentJournalTable()
{
Schema::dropIfExists('component_transaction_journal');
}
public function dropComponentRecurringTransactionTable()
{
Schema::dropIfExists('component_recurring_transaction');
}
public function dropComponentTransactionTable()
{
Schema::dropIfExists('component_transaction');
}
public function dropPiggyBankIdFromTransactions()
{
Schema::table(
'transactions', function (Blueprint $table) {
if (Schema::hasColumn('transactions', 'piggybank_id')) {
$table->dropForeign('transactions_piggybank_id_foreign');
$table->dropColumn('piggybank_id');
}
}
);
}
public function dropComponentIdFromBudgetLimits()
{
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropForeign('limits_component_id_foreign');
$table->dropColumn('component_id');
}
);
}
public function expandCurrencyTable()
{
Schema::table(
'transaction_currencies', function (Blueprint $table) {
$table->string('name', 48)->nullable();
$table->string('symbol', 8)->nullable();
}
);
\DB::update('UPDATE `transaction_currencies` SET `symbol` = "&#8364;", `name` = "Euro" WHERE `code` = "EUR";');
}
//
// public function doRenameInLimitRepetitions()
// {
// Schema::table(
// 'limit_repetitions', function (Blueprint $table) {
// $table->renameColumn('limit_id', 'budget_limit_id');
// }
// );
// }
//
// public function doBudgetLimits()
// {
// Schema::rename('limits', 'budget_limits');
// Schema::table(
// 'budget_limits', function (Blueprint $table) {
// $table->integer('budget_id')->unsigned()->after('updated_at');
// $table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade');
// }
// );
// }
//
// public function doPiggyBankEvents()
// {
// Schema::rename('piggybank_events', 'piggy_bank_events');
//
// }
//
// public function doCreateCategoryTables()
// {
// Schema::create(
// 'categories', function (Blueprint $table) {
// $table->increments('id');
// $table->timestamps();
// $table->softDeletes();
// $table->string('name', 50);
// $table->integer('user_id')->unsigned();
// $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
// $table->unique(['user_id', 'name']);
// }
// );
// Schema::create(
// 'category_transaction_journal', function (Blueprint $table) {
// $table->increments('id');
// $table->integer('category_id')->unsigned();
// $table->integer('transaction_journal_id')->unsigned();
// $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
// $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
// $table->unique(['category_id', 'transaction_journal_id'], 'catid_tjid_unique');
// }
// );
//
// }
//
// public function doUpdateTransactionTable()
// {
// Schema::table(
// 'transactions', function (Blueprint $table) {
// $table->dropForeign('transactions_piggybank_id_foreign');
// #$table->dropIndex('transactions_piggybank_id_foreign');
// $table->dropColumn('piggybank_id');
// }
// );
// }
//
// public function doDropCompRecurTable()
// {
// Schema::drop('component_recurring_transaction');
// }
//
// public function doDropCompTransTable()
// {
// Schema::drop('component_transaction');
// }
//
// public function doMoveBudgets()
// {
// Component::where('class', 'Budget')->get()->each(
// function (Component $c) {
// $entry = [
// 'user_id' => $c->user_id,
// 'name' => $c->name
//
// ];
// $budget = Budget::firstOrCreate($entry);
// Log::debug('Migrated budget #' . $budget->id . ': ' . $budget->name);
// // create entry in budget_transaction_journal
// $connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get();
// foreach ($connections as $connection) {
// DB::table('budget_transaction_journal')->insert(
// [
// 'budget_id' => $budget->id,
// 'transaction_journal_id' => $connection->transaction_journal_id
// ]
// );
// }
// }
// );
// }
//
// public function doMoveCategories()
// {
// Component::where('class', 'Category')->get()->each(
// function (Component $c) {
// $entry = [
// 'user_id' => $c->user_id,
// 'name' => $c->name
//
// ];
// $category = Category::firstOrCreate($entry);
// Log::debug('Migrated category #' . $category->id . ': ' . $category->name);
// // create entry in category_transaction_journal
// $connections = DB::table('component_transaction_journal')->where('component_id', $c->id)->get();
// foreach ($connections as $connection) {
// DB::table('category_transaction_journal')->insert(
// [
// 'category_id' => $category->id,
// 'transaction_journal_id' => $connection->transaction_journal_id
// ]
// );
// }
// }
// );
// }
//
// public function doMoveLimitReferences()
// {
// throw new \FireflyIII\Exception\FireflyException('TODO');
// }
}

View File

@ -8,17 +8,11 @@ class DefaultUserSeeder extends Seeder
public function run()
{
DB::table('users')->delete();
if (App::environment() == 'testing') {
if (App::environment() == 'testing' || App::environment() == 'homestead') {
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]
);
User::create(
['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null, 'migrated' => 0]
);
User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'acceptance@example.com', 'password' => 'acceptance', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null]);
}
}

View File

@ -2,20 +2,21 @@
use Carbon\Carbon;
/**
* Class TestContentSeeder
*/
class TestContentSeeder extends Seeder
{
public function run()
{
if (App::environment() == 'testing') {
if (App::environment() == 'testing' || App::environment() == 'homestead') {
$assetType = AccountType::whereType('Asset account')->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();
@ -27,18 +28,147 @@ class TestContentSeeder extends Seeder
// 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]);
Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Delete me', 'active' => 1]);
// create two budgets:
$groceriesBudget = Budget::create(['user_id' => $user->id, 'name' => 'Groceries']);
$billsBudget = Budget::create(['user_id' => $user->id, 'name' => 'Bills']);
$deleteBudget = Budget::create(['user_id' => $user->id, 'name' => 'Delete me']);
// some limits:
$startDate = Carbon::now()->startOfMonth();
$endDate = Carbon::now()->endOfMonth();
$secondStart = Carbon::now()->subMonth()->startOfMonth();
$secondEnd = Carbon::now()->subMonth()->endOfMonth();
$limitOne = BudgetLimit::create(
['startdate' => $startDate->format('Y-m-d'), 'amount' => 201, 'repeats' => 0, 'repeat_freq' => 'monthly',
'budget_id' => $groceriesBudget->id]
);
$limitTwo = BudgetLimit::create(
['startdate' => $secondStart->format('Y-m-d'), 'amount' => 202, 'repeats' => 0, 'repeat_freq' => 'monthly',
'budget_id' => $billsBudget->id]
);
$limitThree = BudgetLimit::create(
['startdate' => '2014-01-01', 'amount' => 203, 'repeats' => 0, 'repeat_freq' => 'monthly',
'budget_id' => $deleteBudget->id]
);
// and because we have no filters, some repetitions:
$repOne = LimitRepetition::create(
['budget_limit_id' => $limitOne->id, 'startdate' => $startDate->format('Y-m-d'), 'enddate' => $endDate->format('Y-m-d'), 'amount' => 201]
);
$repTwo = LimitRepetition::create(
['budget_limit_id' => $limitTwo->id, 'startdate' => $secondStart->format('Y-m-d'), 'enddate' => $secondEnd->format('Y-m-d'), 'amount' => 202]
);
$repThree = LimitRepetition::create(
['budget_limit_id' => $limitThree->id, 'startdate' => '2014-01-01', 'enddate' => '2014-01-31', 'amount' => 203]
);
// create two categories:
$dailyGroceries = Category::create(['user_id' => $user->id, 'name' => 'Daily groceries']);
$dailyGroceries = Category::create(['user_id' => $user->id, 'name' => 'DailyGroceries']);
$lunch = Category::create(['user_id' => $user->id, 'name' => 'Lunch']);
$house = Category::create(['user_id' => $user->id, 'name' => 'House']);
$deleteMe = Category::create(['user_id' => $user->id, 'name' => 'Delete me']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 1', 'class' => 'Budget']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 2', 'class' => 'Budget']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 3', 'class' => 'Budget']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 4', 'class' => 'Category']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 5', 'class' => 'Category']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 6', 'class' => 'Category']);
Component::create(['user_id' => $user->id, 'name' => 'Some Component 7', 'class' => 'Category']);
// piggy bank
$piggy = Piggybank::create(
[
'account_id' => $savings->id,
'name' => 'New camera',
'targetamount' => 2000,
'startdate' => Carbon::now()->format('Y-m-d'),
'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null,
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 0,
]
);
PiggyBankEvent::create(['piggybank_id' => 1, 'date' => $startDate->format('Y-m-d'), 'amount' => 100]);
PiggybankRepetition::create(
[
'piggybank_id' => $piggy->id,
'startdate' => Carbon::now()->format('Y-m-d'),
'targetdate' => null,
'currentamount' => 0
]
);
// piggy bank
$piggyTargeted = Piggybank::create(
[
'account_id' => $savings->id,
'name' => 'New clothes',
'targetamount' => 2000,
'startdate' => Carbon::now()->format('Y-m-d'),
'targetdate' => Carbon::now()->addMonths(4)->format('Y-m-d'),
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null,
'reminder_skip' => 0,
'remind_me' => 0,
'order' => 0,
]
);
PiggyBankEvent::create(['piggybank_id' => $piggyTargeted->id, 'date' => $startDate->format('Y-m-d'), 'amount' => 100]);
PiggybankRepetition::create(
[
'piggybank_id' => $piggyTargeted->id,
'startdate' => Carbon::now()->format('Y-m-d'),
'targetdate' => Carbon::now()->addMonths(4)->format('Y-m-d'),
'currentamount' => 0
]
);
// recurring transaction
$recurring = \RecurringTransaction::create(
[
'user_id' => $user->id,
'name' => 'Huur',
'match' => 'huur,portaal',
'amount_min' => 500,
'amount_max' => 700,
'date' => '2014-01-12',
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
]
);
// recurring transaction
$secondRecurring = \RecurringTransaction::create(
[
'user_id' => $user->id,
'name' => 'Gas licht',
'match' => 'no,match',
'amount_min' => 500,
'amount_max' => 700,
'date' => '2014-01-12',
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
]
);
// create some expense accounts.
$ah = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Albert Heijn', 'active' => 1]);
$albert = 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]);
@ -48,7 +178,7 @@ class TestContentSeeder extends Seeder
// 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]);
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(
@ -64,10 +194,11 @@ class TestContentSeeder extends Seeder
// create some expenses and incomes and what-not (for every month):
$start = new Carbon('2014-01-01');
$end = Carbon::now()->startOfMonth()->subDay();
$end = Carbon::now()->endOfMonth()->addDay();
while ($start <= $end) {
$this->createTransaction(
$checking, $portaal, 500, $withdrawal, 'Rent for ' . $start->format('F Y'), $start->format('Y-m-') . '01', $billsBudget, $house
$checking, $portaal, 500, $withdrawal, 'Huur Portaal for ' . $start->format('F Y'), $start->format('Y-m-') . '01', $billsBudget, $house,
$recurring
);
$this->createTransaction(
$checking, $vitens, 12, $withdrawal, 'Water for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house
@ -87,7 +218,7 @@ class TestContentSeeder extends Seeder
$groceriesStart->addDay();
if (intval($groceriesStart->format('d')) % 2 == 0) {
$this->createTransaction(
$checking, $ah, $amt, $withdrawal, 'Groceries', $groceriesStart->format('Y-m-d'), $groceriesBudget, $dailyGroceries
$checking, $albert, $amt, $withdrawal, 'Groceries', $groceriesStart->format('Y-m-d'), $groceriesBudget, $dailyGroceries
);
}
$groceriesStart->addDay();
@ -121,30 +252,37 @@ class TestContentSeeder extends Seeder
}
/**
* @param Account $from
* @param Account $to
* @param $amount
* @param TransactionType $type
* @param $description
* @param $date
* @param Account $from
* @param Account $to
* @param $amount
* @param TransactionType $type
* @param $description
* @param $date
*
* @param Budget $budget
* @param Category $category
* @param RecurringTransaction $recurring
*
* @return TransactionJournal
*/
public function createTransaction(
Account $from, Account $to, $amount, TransactionType $type, $description, $date, Budget $budget = null, Category $category = null
Account $from, Account $to, $amount, TransactionType $type, $description, $date, Budget $budget = null, Category $category = null,
$recurring = null
) {
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$recurringID = is_null($recurring) ? null : $recurring->id;
/** @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
'user_id' => $user->id,
'transaction_type_id' => $type->id,
'transaction_currency_id' => $euro->id,
'recurring_transaction_id' => $recurringID,
'description' => $description,
'completed' => 1,
'date' => $date
]
);
@ -154,7 +292,6 @@ class TestContentSeeder extends Seeder
'transaction_journal_id' => $journal->id,
'amount' => $amount * -1
]
);
Transaction::create(
[
@ -162,7 +299,6 @@ class TestContentSeeder extends Seeder
'transaction_journal_id' => $journal->id,
'amount' => $amount
]
);
if (!is_null($budget)) {
$journal->budgets()->save($budget);

View File

@ -10,9 +10,8 @@ class TransactionCurrencySeeder extends Seeder
{
DB::table('transaction_currencies')->delete();
TransactionCurrency::create(
['code' => 'EUR']
);
TransactionCurrency::create(['code' => 'EUR','name' => 'Euro','symbol' => '&#8364;']);
TransactionCurrency::create(['code' => 'USD','name' => 'US Dollar','symbol' => '$']);
}
}

View File

@ -4,11 +4,15 @@
App::before(
function ($request) {
// put IP in session if not already there.
$reminders = [];
if (Auth::check()) {
Filter::setSessionDateRange();
Reminders::updateReminders();
Steam::removeEmptyBudgetLimits();
$reminders = Reminders::getReminders();
}
View::share('reminders', $reminders);

View File

@ -1,121 +0,0 @@
<?php
namespace Firefly\Database;
use LaravelBook\Ardent\Ardent;
/**
* Class SingleTableInheritanceEntity
*
* @package Firefly\Database
*/
abstract class SingleTableInheritanceEntity extends Ardent
{
/**
* The field that stores the subclass
*
* @var string
*/
protected $subclassField = null;
/**
* must be overridden and set to true in subclasses
*
* @var bool
*/
protected $isSubclass = false;
/**
* @param array $attributes
*
* @return \Illuminate\Database\Eloquent\Model|static
*/
public function newFromBuilder($attributes = [])
{
$instance = $this->mapData((array)$attributes)->newInstance([], true);
$instance->setRawAttributes((array)$attributes, true);
return $instance;
}
/**
* 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];
}
/**
*
* 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;
}
/**
* @return bool
*/
public function isSubclass()
{
return $this->isSubclass;
}
/**
* 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);
}
}

View File

@ -1,14 +0,0 @@
<?php
namespace Firefly\Exception;
/**
* Class FireflyException
*
* @package Firefly\Exception
*/
class FireflyException extends \Exception
{
}

View File

@ -1,11 +0,0 @@
<?php
namespace Firefly\Exception;
/**
* Class ValidationException
*
* @package Firefly\Exception
*/
class ValidationException extends \Exception {
}

View File

@ -1,365 +0,0 @@
<?php
namespace Firefly\Form;
use Firefly\Exception\FireflyException;
use Illuminate\Support\MessageBag;
class Form
{
/**
* @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);
}
public static function ffCheckbox($name, $value = 1, $checked = null, $options = [])
{
$options['checked'] = $checked ? true : null;
return self::ffInput('checkbox', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffAmount($name, $value = null, array $options = [])
{
$options['step'] = 'any';
$options['min'] = '0.01';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffBalance($name, $value = null, array $options = [])
{
$options['step'] = 'any';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
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 ffTags($name, $value = null, array $options = [])
{
$options['data-role'] = 'tagsinput';
return self::ffInput('text', $name, $value, $options);
}
/**
* @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 ffText($name, $value = null, array $options = array())
{
return self::ffInput('text', $name, $value, $options);
}
/**
* @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));
}
/**
* Return buttons for update/validate/return.
*
* @param $type
* @param $name
*/
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 = '<div class="form-group"><label for="default" class="col-sm-4 control-label">Store</label>';
$store .= '<div class="col-sm-8"><div class="radio"><label>';
$store .= \Form::radio('post_submit_action', 'store', $previousValue == 'store');
$store .= 'Store ' . $name . '</label></div></div></div>';
break;
case 'update':
$store = '<div class="form-group"><label for="default" class="col-sm-4 control-label">Store</label>';
$store .= '<div class="col-sm-8"><div class="radio"><label>';
$store .= \Form::radio('post_submit_action', 'update', $previousValue == 'store');
$store .= 'Update ' . $name . '</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store) ' . $type);
break;
}
/*
* validate is always the same:
*/
$validate = '<div class="form-group"><label for="validate_only" class="col-sm-4 control-label">Validate only';
$validate .= '</label><div class="col-sm-8"><div class="radio"><label>';
$validate .= \Form::radio('post_submit_action', 'validate_only', $previousValue == 'validate_only');
$validate .= 'Only validate, do not save</label></div></div></div>';
/*
* Store & return:
*/
switch ($type) {
case 'create':
$return = '<div class="form-group"><label for="return_to_form" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action', 'create_another', $previousValue == 'create_another');
$return .= 'After storing, return here to create another one.</label></div></div></div>';
break;
case 'update':
$return = '<div class="form-group"><label for="return_to_edit" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action', 'return_to_edit', $previousValue == 'return_to_edit');
$return .= 'After updating, return here.</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type);
break;
}
return $store . $validate . $return;
}
/**
* @param $type
* @param $name
* @param null $value
* @param array $options
* @param array $list
*
* @return string
* @throws FireflyException
*/
public static function ffInput($type, $name, $value = null, array $options = array(), $list = [])
{
/*
* add some defaults to this method:
*/
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['autocomplete'] = 'off';
$label = self::label($name, $options);
/*
* Make label and placeholder look nice.
*/
$options['placeholder'] = ucfirst($name);
/*
* Get pre filled value:
*/
if (\Session::has('prefilled')) {
$preFilled = \Session::get('preFilled');
$value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value;
}
/*
* Get the value.
*/
if (!is_null(\Input::old($name))) {
/*
* Old value overrules $value.
*/
$value = \Input::old($name);
}
/*
* Get errors, warnings and successes from session:
*/
/** @var MessageBag $errors */
$errors = \Session::get('errors');
/** @var MessageBag $warnings */
$warnings = \Session::get('warnings');
/** @var MessageBag $successes */
$successes = \Session::get('successes');
/*
* If errors, add some more classes.
*/
switch (true) {
case (!is_null($errors) && $errors->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 = '<div class="' . $classes . '">';
$html .= '<label for="' . $options['id'] . '" class="col-sm-4 control-label">' . $label . '</label>';
$html .= '<div class="col-sm-8">';
/*
* Switch input type:
*/
unset($options['label']);
switch ($type) {
case 'text':
$html .= \Form::input('text', $name, $value, $options);
break;
case 'amount':
$html .= '<div class="input-group"><div class="input-group-addon">&euro;</div>';
$html .= \Form::input('number', $name, $value, $options);
$html .= '</div>';
break;
case 'number':
$html .= \Form::input('number', $name, $value, $options);
break;
case 'checkbox':
$checked = $options['checked'];
unset($options['checked'], $options['placeholder'], $options['autocomplete'], $options['class']);
$html .= '<div class="checkbox"><label>';
$html .= \Form::checkbox($name, $value, $checked, $options);
$html .= '</label></div>';
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 .= '<span class="glyphicon glyphicon-remove form-control-feedback"></span>';
$html .= '<p class="text-danger">' . e($errors->first($name)) . '</p>';
}
}
unset($errors);
/*
* If warnings, respond to them:
*/
if (!is_null($warnings)) {
if ($warnings->has($name)) {
$html .= '<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>';
$html .= '<p class="text-warning">' . e($warnings->first($name)) . '</p>';
}
}
unset($warnings);
/*
* If successes, respond to them:
*/
if (!is_null($successes)) {
if ($successes->has($name)) {
$html .= '<span class="glyphicon glyphicon-ok form-control-feedback"></span>';
$html .= '<p class="text-success">' . e($successes->first($name)) . '</p>';
}
}
unset($successes);
$html .= '</div>';
$html .= '</div>';
return $html;
}
}

View File

@ -1,154 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
/**
* Class Account
*
* @package Firefly\Helper\Controllers
*/
class Account implements AccountInterface
{
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()
->accountIs($account)
->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']);
}
/**
* Since it is entirely possible the database is messed up somehow it might be that a transaction
* journal has only one transaction. This is mainly caused by wrong deletions and other artefacts from the past.
*
* If it is the case, Firefly removes $item and continues like nothing ever happened. This will however,
* mess up some statisics but it's decided everybody should learn to live with that.
*
* Firefly might be needing some cleanup routine in the future.
*
* For now, Firefly simply warns the user of this.
*
* @param \Account $account
* @param $perPage
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function show(\Account $account, $perPage)
{
$start = \Session::get('start');
$end = \Session::get('end');
$stats = [
'accounts' => []
];
$items = [];
// build a query:
$query = \TransactionJournal::withRelevantData()
->defaultSorting()
->accountIs($account)
->after($start)
->before($end);
// filter some:
switch (\Input::get('type')) {
case 'transactions':
$query->transactionTypes(['Deposit', 'Withdrawal']);
break;
case 'transfers':
$query->transactionTypes(['Transfer']);
break;
}
switch (\Input::get('show')) {
case 'expenses':
case 'out':
$query->lessThan(0);
break;
case 'income':
case 'in':
$query->moreThan(0);
break;
}
// build paginator:
$totalItems = $query->count();
$page = max(1, intval(\Input::get('page')));
$skip = ($page - 1) * $perPage;
$result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']);
// get the relevant budgets, categories and accounts from this list:
/** @var $item \TransactionJournal */
foreach ($result as $index => $item) {
foreach ($item->components as $component) {
$stats[$component->class][$component->id] = $component;
}
if (count($item->transactions) < 2) {
\Session::flash('warning', 'Some transactions are incomplete; they will not be shown.');
unset($result[$index]);
continue;
}
$items[] = $item;
$fromAccount = $item->transactions[0]->account;
$toAccount = $item->transactions[1]->account;
$stats['accounts'][$fromAccount->id] = $fromAccount;
$stats['accounts'][$toAccount->id] = $toAccount;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
unset($result, $page, $item, $fromAccount, $toAccount);
// statistics (transactions)
$trIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trDiff = $trIn + $trOut;
// statistics (transfers)
$trfIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfDiff = $trfIn + $trfOut;
$stats['period'] = [
'in' => $trIn,
'out' => $trOut,
'diff' => $trDiff,
't_in' => $trfIn,
't_out' => $trfOut,
't_diff' => $trfDiff
];
$return = [
'journals' => $paginator,
'statistics' => $stats
];
return $return;
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Interface AccountInterface
*
* @package Firefly\Helper\Controllers
*/
interface AccountInterface
{
/**
* @param \Account $account
*
* @return mixed
*/
public function openingBalanceTransaction(\Account $account);
/**
* @param \Account $account
* @param $perPage
*
* @return mixed
*/
public function show(\Account $account, $perPage);
}

View File

@ -1,227 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Class Budget
*
* @package Firefly\Helper\Controllers
*/
class Budget implements BudgetInterface
{
/**
* First, loop all budgets, all of their limits and all repetitions to get an overview per period
* and some basic information about that repetition's data.
*
*
*
* @param Collection $budgets
*
* @return mixed|void
*/
public function organizeByDate(Collection $budgets)
{
$return = [];
/** @var \Budget $budget */
foreach ($budgets as $budget) {
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
$repetition->left = $repetition->leftInRepetition();
$periodOrder = $repetition->periodOrder();
$period = $repetition->periodShow();
if (!isset($return[$periodOrder])) {
$return[$periodOrder] = [
'date' => $period,
'start' => $repetition->startdate,
'end' => $repetition->enddate,
'budget_id' => $budget->id,
'limitrepetitions' => [$repetition]
];
} else {
$return[$periodOrder]['limitrepetitions'][] = $repetition;
}
}
}
}
krsort($return);
return $return;
}
/**
* Get a repetition (complex because of user check)
* and then get the transactions in it.
* @param $repetitionId
*
* @return array
*/
public function organizeRepetition(\LimitRepetition $repetition)
{
$result = [];
// get transactions:
$set = $repetition->limit->budget
->transactionjournals()
->withRelevantData()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.*']);
$result[0] = [
'date' => $repetition->periodShow(),
'limit' => $repetition->limit,
'limitrepetition' => $repetition,
'journals' => $set,
'paginated' => false
];
return $result;
}
/**
*
*
* @param \Budget $budget
* @param bool $useSessionDates
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
*/
public function organizeRepetitions(\Budget $budget, $useSessionDates = false)
{
$sessionStart = \Session::get('start');
$sessionEnd = \Session::get('end');
$result = [];
$inRepetition = [];
// get the limits:
if ($useSessionDates) {
$limits = $budget->limits()->where('startdate', '>=', $sessionStart->format('Y-m-d'))->where(
'startdate', '<=', $sessionEnd->format('Y-m-d')
)->get();
} else {
$limits = $budget->limits;
}
/** @var \Limit $limit */
foreach ($limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$order = $repetition->periodOrder();
$result[$order] = [
'date' => $repetition->periodShow(),
'limitrepetition' => $repetition,
'limit' => $limit,
'journals' => [],
'paginated' => false
];
$transactions = [];
$set = $budget->transactionjournals()
->withRelevantData()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.*']);
foreach ($set as $entry) {
$transactions[] = $entry;
$inRepetition[] = $entry->id;
}
$result[$order]['journals'] = $transactions;
}
}
if ($useSessionDates === false) {
$query = $budget->transactionjournals()->withRelevantData()->defaultSorting();
if (count($inRepetition) > 0) {
$query->whereNotIn('transaction_journals.id', $inRepetition);
}
// build paginator:
$perPage = 25;
$totalItems = $query->count();
$page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1;
$skip = ($page - 1) * $perPage;
$set = $query->skip($skip)->take($perPage)->get();
// stupid paginator!
$items = [];
/** @var $item \TransactionJournal */
foreach ($set as $item) {
$items[] = $item;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
$result['0000'] = [
'date' => 'Not in an envelope',
'limit' => null,
'paginated' => true,
'journals' => $paginator];
}
krsort($result);
return $result;
}
/**
* @param \Budget $budget
*
* @return mixed|void
*/
public function outsideRepetitions(\Budget $budget)
{
$inRepetitions = [];
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$set = $budget->transactionjournals()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.id']);
foreach ($set as $item) {
$inRepetitions[] = $item->id;
}
}
}
$query = $budget->transactionjournals()
->withRelevantData()
->whereNotIn('transaction_journals.id', $inRepetitions)
->defaultSorting();
// build paginator:
$perPage = 25;
$totalItems = $query->count();
$page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1;
$skip = ($page - 1) * $perPage;
$set = $query->skip($skip)->take($perPage)->get();
// stupid paginator!
$items = [];
/** @var $item \TransactionJournal */
foreach ($set as $item) {
$items[] = $item;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
$result = [0 => [
'date' => 'Not in an envelope',
'limit' => null,
'paginated' => true,
'journals' => $paginator
]];
return $result;
}
}

View File

@ -1,44 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Interface BudgetInterface
*
* @package Firefly\Helper\Controllers
*/
interface BudgetInterface
{
/**
* @param Collection $budgets
*
* @return mixed
*/
public function organizeByDate(Collection $budgets);
/**
* @param $repetitionId
*
* @return mixed
*/
public function organizeRepetition(\LimitRepetition $repetition);
/**
* @param \Budget $budget
* @param bool $useSessionDates
*
* @return mixed
*/
public function organizeRepetitions(\Budget $budget, $useSessionDates = false);
/**
* @param \Budget $budget
*
* @return mixed
*/
public function outsideRepetitions(\Budget $budget);
}

View File

@ -1,29 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
/**
* Class Category
*
* @package Firefly\Helper\Controllers
*/
class Category implements CategoryInterface
{
/**
* @param \Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function journalsInRange(\Category $category, Carbon $start, Carbon $end)
{
return $category->transactionjournals()->with(
['transactions', 'transactions.account', 'transactiontype', 'components']
)->orderBy('date', 'DESC')->orderBy('id', 'DESC')->before($end)->after($start)->get();
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
/**
* Interface CategoryInterface
*
* @package Firefly\Helper\Controllers
*/
interface CategoryInterface
{
/**
* @param \Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function journalsInRange(\Category $category, Carbon $start, Carbon $end);
}

View File

@ -1,475 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Support\Collection;
/**
* Class Chart
*
* @package Firefly\Helper\Controllers
*/
class Chart implements ChartInterface
{
/**
* @param \Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function account(\Account $account, Carbon $start, Carbon $end)
{
$current = clone $start;
$today = new Carbon;
$return = [
'name' => $account->name,
'id' => $account->id,
'type' => 'spline',
'pointStart' => $start->timestamp * 1000,
'pointInterval' => 24 * 3600 * 1000, // one day
'data' => []
];
while ($current <= $end) {
if ($current > $today) {
$return['data'][] = $account->predict(clone $current);
} else {
$return['data'][] = $account->balance(clone $current);
}
$current->addDay();
}
return $return;
}
/**
* @param \Account $account
* @param Carbon $date
*
* @return array
*/
public function accountDailySummary(\Account $account, Carbon $date)
{
$result = [
'rows' => [],
'sum' => 0
];
if ($account) {
// get journals in range:
$journals = \Auth::user()->transactionjournals()->with(
[
'transactions',
'transactions.account',
'transactioncurrency',
'transactiontype'
]
)
->distinct()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('transactions.account_id', $account->id)
->where('transaction_journals.date', $date->format('Y-m-d'))
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
// loop all journals:
foreach ($journals as $journal) {
foreach ($journal->transactions as $transaction) {
$name = $transaction->account->name;
if ($transaction->account->id != $account->id) {
if (!isset($result['rows'][$name])) {
$result['rows'][$name] = [
'name' => $name,
'id' => $transaction->account->id,
'amount' => floatval($transaction->amount)
];
} else {
$result['rows'][$name]['amount'] += floatval($transaction->amount);
}
$result['sum'] += floatval($transaction->amount);
}
}
}
}
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws \Firefly\Exception\FireflyException
*/
public function categories(Carbon $start, Carbon $end)
{
$result = [];
// grab all transaction journals in this period:
$journals = \TransactionJournal::
with(
['components', 'transactions' => function ($q) {
$q->where('amount', '>', 0);
}]
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', 'Withdrawal')
->after($start)->before($end)
->where('completed', 1)
->get(['transaction_journals.*']);
foreach ($journals as $journal) {
// has to be one:
if (!isset($journal->transactions[0])) {
throw new FireflyException('Journal #' . $journal->id . ' has ' . count($journal->transactions)
. ' transactions!');
}
$transaction = $journal->transactions[0];
$amount = floatval($transaction->amount);
// get budget from journal:
$category = $journal->categories()->first();
$categoryName = is_null($category) ? '(no category)' : $category->name;
$result[$categoryName] = isset($result[$categoryName]) ? $result[$categoryName] + floatval($amount)
: $amount;
}
unset($journal, $transaction, $category, $amount);
// sort
arsort($result);
$chartData = [];
foreach ($result as $name => $value) {
$chartData[] = [$name, $value];
}
return $chartData;
}
/**
* @param \Category $category
* @param $range
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws \Firefly\Exception\FireflyException
*/
public function categoryShowChart(\Category $category, $range, Carbon $start, Carbon $end)
{
$data = ['name' => $category->name . ' per ' . $range, 'data' => []];
// go back twelve periods. Skip if empty.
$beginning = clone $start;
switch ($range) {
default:
throw new FireflyException('No beginning for range ' . $range);
break;
case '1D':
$beginning->subDays(12);
break;
case '1W':
$beginning->subWeeks(12);
break;
case '1M':
$beginning->subYear();
break;
case '3M':
$beginning->subYears(3);
break;
case '6M':
$beginning->subYears(6);
break;
case 'custom':
$diff = $start->diff($end);
$days = $diff->days;
$beginning->subDays(12 * $days);
break;
}
// loop over the periods:
while ($beginning <= $start) {
// increment currentEnd to fit beginning:
$currentEnd = clone $beginning;
// increase beginning for next round:
switch ($range) {
default:
throw new FireflyException('No currentEnd incremental for range ' . $range);
break;
case '1D':
break;
case '1W':
$currentEnd->addWeek()->subDay();
break;
case '1M':
$currentEnd->addMonth()->subDay();
break;
case '3M':
$currentEnd->addMonths(3)->subDay();
break;
case '6M':
$currentEnd->addMonths(6)->subDay();
break;
case 'custom':
$diff = $start->diff($end);
$days = $diff->days;
$days = $days == 1 ? 2 : $days;
$currentEnd->addDays($days)->subDay();
break;
}
// now format the current range:
$title = '';
switch ($range) {
default:
throw new FireflyException('No date formats for frequency "' . $range . '"!');
break;
case '1D':
$title = $beginning->format('j F Y');
break;
case '1W':
$title = $beginning->format('\W\e\e\k W, Y');
break;
case '1M':
$title = $beginning->format('F Y');
break;
case '3M':
case '6M':
$title = $beginning->format('M Y') . ' - ' . $currentEnd->format('M Y');
break;
case 'custom':
$title = $beginning->format('d-m-Y') . ' - ' . $currentEnd->format('d-m-Y');
break;
case 'yearly':
// return $this->startdate->format('Y');
break;
}
// get sum for current range:
$journals = \TransactionJournal::
with(
['transactions' => function ($q) {
$q->where('amount', '>', 0);
}]
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', 'Withdrawal')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('components.id', '=', $category->id)
//->leftJoin()
->after($beginning)->before($currentEnd)
->where('completed', 1)
->get(['transaction_journals.*']);
$currentSum = 0;
foreach ($journals as $journal) {
if (!isset($journal->transactions[0])) {
throw new FireflyException('Journal #' . $journal->id . ' has ' . count($journal->transactions)
. ' transactions!');
}
$transaction = $journal->transactions[0];
$amount = floatval($transaction->amount);
$currentSum += $amount;
}
$data['data'][] = [$title, $currentSum];
// increase beginning for next round:
switch ($range) {
default:
throw new FireflyException('No incremental for range ' . $range);
break;
case '1D':
$beginning->addDay();
break;
case '1W':
$beginning->addWeek();
break;
case '1M':
$beginning->addMonth();
break;
case '3M':
$beginning->addMonths(3);
break;
case '6M':
$beginning->addMonths(6);
break;
case 'custom':
$diff = $start->diff($end);
$days = $diff->days;
$beginning->addDays($days);
break;
}
}
return $data;
}
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return float|null
*/
public function spentOnDay(\Budget $budget, Carbon $date)
{
return floatval(
\Transaction::
leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)->where('component_transaction_journal.component_id', '=', $budget->id)->where(
'transaction_journals.date', $date->format('Y-m-d')
)->where('amount', '>', 0)->sum('amount')
);
}
/**
* @param \Budget $budget
*
* @return int[]
*/
public function allJournalsInBudgetEnvelope(\Budget $budget)
{
$inRepetitions = [];
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$set = $budget
->transactionjournals()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->get(['transaction_journals.id']);
foreach ($set as $item) {
$inRepetitions[] = $item->id;
}
}
}
return $inRepetitions;
}
/**
* @param \Budget $budget
* @param array $ids
*
* @return mixed|void
*/
public function journalsNotInSet(\Budget $budget, array $ids)
{
$query = $budget->transactionjournals()
->whereNotIn('transaction_journals.id', $ids)
->orderBy('date', 'DESC')
->orderBy('transaction_journals.id', 'DESC');
$result = $query->get(['transaction_journals.id']);
$set = [];
foreach ($result as $entry) {
$set[] = $entry->id;
}
return $set;
}
/**
* @param array $set
*
* @return mixed
*/
public function transactionsByJournals(array $set)
{
$transactions = \Transaction::whereIn('transaction_journal_id', $set)
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->groupBy('transaction_journals.date')
->where('amount', '>', 0)->get(['transaction_journals.date', \DB::Raw('SUM(`amount`) as `aggregate`')]);
return $transactions;
}
/**
* Get all limit (LimitRepetitions) for a budget falling in a certain date range.
*
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end)
{
$reps = new Collection;
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
$set = $limit->limitrepetitions()->where(
function ($q) use ($start, $end) {
// startdate is between range
$q->where(
function ($q) use ($start, $end) {
$q->where('startdate', '>=', $start->format('Y-m-d'));
$q->where('startdate', '<=', $end->format('Y-m-d'));
}
);
// or enddate is between range.
$q->orWhere(
function ($q) use ($start, $end) {
$q->where('enddate', '>=', $start->format('Y-m-d'));
$q->where('enddate', '<=', $end->format('Y-m-d'));
}
);
}
)->get();
$reps = $reps->merge($set);
}
return $reps;
}
/**
* Firefly checks how much money has been spend on the limitrepetition (aka: the current envelope) in
* the period denoted. Aka, the user has a certain amount of money in an envelope and wishes to know how
* much he has spent between the dates entered. This date range can be a partial match with the date range
* of the envelope or no match at all.
*
* @param \LimitRepetition $repetition
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end)
{
return floatval(
\Transaction::
leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)->where('component_transaction_journal.component_id', '=', $repetition->limit->budget->id)->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->where(
'amount', '>', 0
)->sum('amount')
);
}
}

View File

@ -1,108 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
/**
* Interface ChartInterface
*
* @package Firefly\Helper\Controllers
*/
interface ChartInterface
{
/**
* @param \Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function account(\Account $account, Carbon $start, Carbon $end);
/**
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function categories(Carbon $start, Carbon $end);
/**
* @param \Account $account
* @param Carbon $date
*
* @return mixed
*/
public function accountDailySummary(\Account $account, Carbon $date);
/**
* @param \Category $category
* @param $range
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function categoryShowChart(\Category $category, $range, Carbon $start, Carbon $end);
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return float|null
*/
public function spentOnDay(\Budget $budget, Carbon $date);
/**
* @param \Budget $budget
*
* @return int[]
*/
public function allJournalsInBudgetEnvelope(\Budget $budget);
/**
* @param \Budget $budget
* @param array $ids
*
* @return mixed
*/
public function journalsNotInSet(\Budget $budget, array $ids);
/**
* @param array $set
*
* @return mixed
*/
public function transactionsByJournals(array $set);
/**
* Get all limit (LimitRepetitions) for a budget falling in a certain date range.
*
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end);
/**
* Firefly checks how much money has been spend on the limitrepetition (aka: the current envelope) in
* the period denoted. Aka, the user has a certain amount of money in an envelope and wishes to know how
* much he has spent between the dates entered. This date range can be a partial match with the date range
* of the envelope or no match at all.
*
* @param \LimitRepetition $repetition
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end);
}

View File

@ -1,396 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
/**
* Class Json
*
* @package Firefly\Helper\Controllers
*/
class Json implements JsonInterface
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters()
{
/*
* Process all parameters!
*/
if (intval(\Input::get('length')) < 0) {
$length = 10000; // we get them all if no length is defined.
} else {
$length = intval(\Input::get('length'));
}
$parameters = [
'start' => intval(\Input::get('start')),
'length' => $length,
'draw' => intval(\Input::get('draw')),
];
/*
* Columns:
*/
if (!is_null(\Input::get('columns')) && is_array(\Input::get('columns'))) {
foreach (\Input::get('columns') as $column) {
$parameters['columns'][] = [
'data' => $column['data'],
'name' => $column['name'],
'searchable' => $column['searchable'] == 'true' ? true : false,
'orderable' => $column['orderable'] == 'true' ? true : false,
'search' => [
'value' => $column['search']['value'],
'regex' => $column['search']['regex'] == 'true' ? true : false,
]
];
}
}
/*
* Sorting.
*/
$parameters['orderOnAccount'] = false;
if (!is_null(\Input::get('order')) && is_array(\Input::get('order'))) {
foreach (\Input::get('order') as $order) {
$columnIndex = intval($order['column']);
$columnName = $parameters['columns'][$columnIndex]['name'];
$parameters['order'][] = [
'name' => $columnName,
'dir' => strtoupper($order['dir'])
];
if ($columnName == 'to' || $columnName == 'from') {
$parameters['orderOnAccount'] = true;
}
}
}
/*
* Search parameters:
*/
$parameters['search'] = [
'value' => '',
'regex' => false
];
if (!is_null(\Input::get('search')) && is_array(\Input::get('search'))) {
$search = \Input::get('search');
$parameters['search'] = [
'value' => $search['value'],
'regex' => $search['regex'] == 'true' ? true : false
];
}
return $parameters;
}
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function journalDataset(array $parameters, Builder $query)
{
/*
* Count query:
*/
$count = $query->count();
/*
* Update the selection:
*/
$query->take($parameters['length']);
if ($parameters['start'] > 0) {
$query->skip($parameters['start']);
}
/*
* Input search parameters:
*/
$filtered = $count;
if (strlen($parameters['search']['value']) > 0) {
$query->where('transaction_journals.description', 'LIKE', '%' . e($parameters['search']['value']) . '%');
$filtered = $query->count();
}
/*
* Build return array:
*/
$data = [
'draw' => $parameters['draw'],
'recordsTotal' => $count,
'recordsFiltered' => $filtered,
'data' => [],
];
/*
* Get paginated result set:
*/
if ($parameters['orderOnAccount'] === true) {
/** @var Collection $set */
$set = $query->get(
[
'transaction_journals.*',
't1.amount',
't1.account_id AS from_id',
'a1.name AS from',
't2.account_id AS to_id',
'a2.name AS to',
]
);
} else {
/** @var Collection $set */
$set = $query->get(
[
'transaction_journals.*',
'transactions.amount',
]
);
}
/*
* Loop set and create entries to return.
*/
/** @var \TransactionJournal $entry */
foreach ($set as $entry) {
$from = $entry->transactions[0]->account;
$to = $entry->transactions[1]->account;
$budget = $entry->budgets()->first();
$category = $entry->categories()->first();
$recurring = $entry->recurringTransaction()->first();
$arr = [
'date' => $entry->date->format('j F Y'),
'description' => [
'description' => $entry->description,
'url' => route('transactions.show', $entry->id)
],
'amount' => floatval($entry->amount),
'from' => ['name' => $from->name, 'url' => route('accounts.show', $from->id)],
'to' => ['name' => $to->name, 'url' => route('accounts.show', $to->id)],
'components' => [
'budget_id' => 0,
'budget_url' => '',
'budget_name' => '',
'category_id' => 0,
'category_url' => '',
'category_name' => ''
],
'id' => [
'edit' => route('transactions.edit', $entry->id),
'delete' => route('transactions.delete', $entry->id)
]
];
if ($budget) {
$arr['components']['budget_id'] = $budget->id;
$arr['components']['budget_name'] = $budget->name;
$arr['components']['budget_url'] = route('budgets.show', $budget->id);
}
if ($category) {
$arr['components']['category_id'] = $category->id;
$arr['components']['category_name'] = $category->name;
$arr['components']['category_url'] = route('categories.show', $category->id);
}
if ($recurring) {
$arr['components']['recurring_id'] = $recurring->id;
$arr['components']['recurring_name'] = e($recurring->name);
$arr['components']['recurring_url'] = route('recurring.show', $recurring->id);
}
$data['data'][] = $arr;
}
return $data;
}
/**
* Builds most of the query required to grab transaction journals from the database.
* This is useful because all three pages showing different kinds of transactions use
* the exact same query with only slight differences.
*
* @param array $parameters
*
* @return Builder
*/
public function journalQuery(array $parameters)
{
/*
* We need the following vars to fine tune the query:
*/
if ($parameters['amount'] == 'negative') {
$operator = '<';
$operatorNegated = '>';
$function = 'lessThan';
} else {
$operator = '>';
$operatorNegated = '<';
$function = 'moreThan';
}
/*
* Build query:
*/
$query = \TransactionJournal::transactionTypes($parameters['transactionTypes'])->withRelevantData();
$query->where('user_id', \Auth::user()->id);
$query->where('completed', 1);
/*
* This is complex. Join `transactions` twice, once for the "to" account and once for the
* "from" account. Then get the amount from one of these (depends on type).
*
* Only need to do this when there's a sort order for "from" or "to".
*
* Also need the table prefix for this to work.
*/
if ($parameters['orderOnAccount'] === true) {
$connection = \Config::get('database.default');
$prefix = \Config::get('database.connections.' . $connection . '.prefix');
// left join first table for "from" account:
$query->leftJoin(
'transactions AS ' . $prefix . 't1', function ($join) use ($operator) {
$join->on('t1.transaction_journal_id', '=', 'transaction_journals.id')
->on('t1.amount', $operator, \DB::Raw(0));
}
);
// left join second table for "to" account:
$query->leftJoin(
'transactions AS ' . $prefix . 't2', function ($join) use ($operatorNegated) {
$join->on('t2.transaction_journal_id', '=', 'transaction_journals.id')
->on('t2.amount', $operatorNegated, \DB::Raw(0));
}
);
// also join accounts twice to get the account's name, which we need for sorting.
$query->leftJoin('accounts as ' . $prefix . 'a1', 'a1.id', '=', 't1.account_id');
$query->leftJoin('accounts as ' . $prefix . 'a2', 'a2.id', '=', 't2.account_id');
} else {
// less complex
$query->$function(0);
}
/*
* Add sort parameters to query:
*/
if (isset($parameters['order']) && count($parameters['order']) > 0) {
foreach ($parameters['order'] as $order) {
$query->orderBy($order['name'], $order['dir']);
}
} else {
$query->defaultSorting();
}
return $query;
}
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function recurringTransactionsDataset(array $parameters, Builder $query)
{
/*
* Count query:
*/
$count = $query->count();
/*
* Update the selection:
*/
$query->take($parameters['length']);
if ($parameters['start'] > 0) {
$query->skip($parameters['start']);
}
/*
* Input search parameters:
*/
$filtered = $count;
if (strlen($parameters['search']['value']) > 0) {
$query->where('recurring_transactions.description', 'LIKE', '%' . e($parameters['search']['value']) . '%');
$filtered = $query->count();
}
/*
* Build return array:
*/
$data = [
'draw' => $parameters['draw'],
'recordsTotal' => $count,
'recordsFiltered' => $filtered,
'data' => [],
];
/*
* Get paginated result set:
*/
/** @var Collection $set */
$set = $query->get(
[
'recurring_transactions.*',
]
);
/*
* Loop set and create entries to return.
*/
foreach ($set as $entry) {
$set = [
'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)],
'match' => explode(' ', $entry->match),
'amount_max' => floatval($entry->amount_max),
'amount_min' => floatval($entry->amount_min),
'date' => $entry->date->format('j F Y'),
'active' => intval($entry->active),
'automatch' => intval($entry->automatch),
'repeat_freq' => $entry->repeat_freq,
'id' => [
'edit' => route('recurring.edit', $entry->id),
'delete' => route('recurring.delete', $entry->id)
]
];
if (intval($entry->skip) > 0) {
$set['repeat_freq'] = $entry->repeat_freq . ' (skip ' . $entry->skip . ')';
}
$data['data'][] = $set;
}
return $data;
}
/**
* Create a query that will pick up all recurring transactions from the database.
*
* @param array $parameters
*
* @return Builder
*/
public function recurringTransactionsQuery(array $parameters)
{
$query = \RecurringTransaction::where('user_id', \Auth::user()->id);
if (isset($parameters['order']) && count($parameters['order']) > 0) {
foreach ($parameters['order'] as $order) {
$query->orderBy($order['name'], $order['dir']);
}
} else {
$query->orderBy('name', 'ASC');
}
return $query;
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
/**
* Interface JsonInterface
*
* @package Firefly\Helper\Controllers
*/
interface JsonInterface
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters();
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function journalDataset(array $parameters, Builder $query);
/**
* Builds most of the query required to grab transaction journals from the database.
* This is useful because all three pages showing different kinds of transactions use
* the exact same query with only slight differences.
*
* @param array $parameters
*
* @return Builder
*/
public function journalQuery(array $parameters);
/**
* Do some sorting, counting and ordering on the query and return a nicely formatted array
* that can be used by the DataTables JQuery plugin.
*
* @param array $parameters
* @param Builder $query
*
* @return array
*/
public function recurringTransactionsDataset(array $parameters, Builder $query);
/**
* Create a query that will pick up all recurring transactions from the database.
*
* @param array $parameters
*
* @return Builder
*/
public function recurringTransactionsQuery(array $parameters);
}

View File

@ -1,120 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\MessageBag;
class Recurring implements RecurringInterface
{
/**
* Returns messages about the validation.
*
* @param array $data
*
* @return array
*/
public function validate(array $data)
{
$errors = new MessageBag;
$warnings = new MessageBag;
$successes = new MessageBag;
/*
* Name:
*/
if (strlen($data['name']) == 0) {
$errors->add('name', 'The name should not be this short.');
}
if (strlen($data['name']) > 250) {
$errors->add('name', 'The name should not be this long.');
}
if (! isset($data['id'])) {
$count = \Auth::user()->recurringtransactions()->whereName($data['name'])->count();
} else {
$count = \Auth::user()->recurringtransactions()->whereName($data['name'])->where('id', '!=', $data['id'])->count();
}
if ($count > 0) {
$errors->add('name', 'A recurring transaction with this name already exists.');
}
if (count($errors->get('name')) == 0) {
$successes->add('name', 'OK!');
}
/*
* Match
*/
if (count(explode(',', $data['match'])) > 10) {
$warnings->add('match', 'This many matches is pretty pointless');
}
if (strlen($data['match']) == 0) {
$errors->add('match', 'Cannot match on nothing.');
}
if (count($errors->get('match')) == 0) {
$successes->add('match', 'OK!');
}
/*
* Amount
*/
if (floatval($data['amount_max']) == 0 && floatval($data['amount_min']) == 0) {
$errors->add('amount_min', 'Amount max and min cannot both be zero.');
$errors->add('amount_max', 'Amount max and min cannot both be zero.');
}
if (floatval($data['amount_max']) < floatval($data['amount_min'])) {
$errors->add('amount_max', 'Amount max must be more than amount min.');
}
if (floatval($data['amount_min']) > floatval($data['amount_max'])) {
$errors->add('amount_max', 'Amount min must be less than amount max.');
}
if (count($errors->get('amount_min')) == 0) {
$successes->add('amount_min', 'OK!');
}
if (count($errors->get('amount_max')) == 0) {
$successes->add('amount_max', 'OK!');
}
/*
* Date
*/
try {
$date = new Carbon($data['date']);
} catch (Exception $e) {
$errors->add('date', 'The date entered was invalid');
}
if (strlen($data['date']) == 0) {
$errors->add('date', 'The date entered was invalid');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK!');
}
$successes->add('active', 'OK!');
$successes->add('automatch', 'OK!');
if (intval($data['skip']) < 0) {
$errors->add('skip', 'Cannot be below zero.');
} else if (intval($data['skip']) > 31) {
$errors->add('skip', 'Cannot be above 31.');
}
if (count($errors->get('skip')) == 0) {
$successes->add('skip', 'OK!');
}
$set = \Config::get('firefly.budget_periods');
if (!in_array($data['repeat_freq'], $set)) {
$errors->add('repeat_freq', 'Invalid value.');
}
if (count($errors->get('repeat_freq')) == 0) {
$successes->add('repeat_freq', 'OK!');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
}

View File

@ -1,15 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
interface RecurringInterface {
/**
* Returns messages about the validation.
*
* @param array $data
*
* @return array
*/
public function validate(array $data);
}

View File

@ -1,101 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Support\Collection;
/**
* Class Search
*
* @package Firefly\Helper\Controllers
*/
class Search implements SearchInterface
{
/**
* @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();
}
/**
* @param array $words
*
* @return Collection
*/
public function searchAccounts(array $words)
{
return \Auth::user()->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 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 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 searchTags(array $words)
{
return new Collection;
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
/**
* Interface SearchInterface
*
* @package Firefly\Helper\Controllers
*/
interface SearchInterface
{
/**
* @param array $words
*/
public function searchTransactions(array $words);
/**
* @param array $words
*/
public function searchAccounts(array $words);
/**
* @param array $words
*/
public function searchCategories(array $words);
/**
* @param array $words
*/
public function searchBudgets(array $words);
/**
* @param array $words
*/
public function searchTags(array $words);
}

View File

@ -1,485 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Exception;
use Firefly\Exception\FireflyException;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
use Firefly\Storage\Category\CategoryRepositoryInterface as CRI;
use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
use Illuminate\Support\MessageBag;
/**
* Class Transaction
*
* @package Firefly\Helper\Controllers
*/
class Transaction implements TransactionInterface
{
protected $_user = null;
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $_journals */
protected $_journals;
/** @var \Firefly\Storage\Category\CategoryRepositoryInterface $_categories */
protected $_categories;
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $_budgets */
protected $_budgets;
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $_piggybanks */
protected $_piggybanks;
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $_accounts */
protected $_accounts;
/**
* @param TJRI $journals
* @param CRI $categories
* @param BRI $budgets
* @param PRI $piggybanks
* @param ARI $accounts
*/
public function __construct(TJRI $journals, CRI $categories, BRI $budgets, PRI $piggybanks, ARI $accounts)
{
$this->_journals = $journals;
$this->_categories = $categories;
$this->_budgets = $budgets;
$this->_piggybanks = $piggybanks;
$this->_accounts = $accounts;
$this->overruleUser(\Auth::user());
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
$this->_journals->overruleUser($user);
$this->_categories->overruleUser($user);
$this->_budgets->overruleUser($user);
$this->_piggybanks->overruleUser($user);
$this->_accounts->overruleUser($user);
return true;
}
/**
* @param \TransactionJournal $journal
* @param array $data
*
* @return MessageBag|\TransactionJournal
*/
public function update(\TransactionJournal $journal, array $data)
{
/*
* Update the journal using the repository.
*/
$journal = $this->_journals->update($journal, $data);
/*
* If invalid, return the message bag:
*/
if (!$journal->validate()) {
return $journal->errors();
}
/*
* find budget using repository
*/
if (isset($data['budget_id'])) {
$budget = $this->_budgets->find($data['budget_id']);
}
/*
* find category using repository
*/
$category = $this->_categories->firstOrCreate($data['category']);
/*
* Find piggy bank using repository:
*/
$piggybank = null;
if (isset($data['piggybank_id'])) {
$piggybank = $this->_piggybanks->find($data['piggybank_id']);
}
/*
* save accounts using repositories
* this depends on the kind of transaction and i've yet to fix this.
*/
if (isset($data['account_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['expense_account'])) {
$to = $this->_accounts->findExpenseAccountByName($data['expense_account']);
}
if (isset($data['revenue_account'])) {
$from = $this->_accounts->findRevenueAccountByName($data['revenue_account']);
$to = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['account_from_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_from_id']);
}
if (isset($data['account_to_id'])) {
$to = $this->_accounts->findAssetAccountById($data['account_to_id']);
}
/*
* Add a custom error when they are the same.
*/
if ($to->id == $from->id) {
$bag = new MessageBag;
$bag->add('account_from_id', 'The account from cannot be the same as the account to.');
return $bag;
}
/*
* Check if the transactions need new data:
*/
$transactions = $journal->transactions()->orderBy('amount', 'ASC')->get();
/** @var \Transaction $transaction */
foreach ($transactions as $index => $transaction) {
switch (true) {
case ($index == 0): // FROM account
$transaction->account()->associate($from);
$transaction->amount = floatval($data['amount']) * -1;
break;
case ($index == 1): // TO account.
$transaction->account()->associate($to);
$transaction->amount = floatval($data['amount']);
break;
}
$transaction->save();
// either way, try to attach the piggy bank:
if (!is_null($piggybank)) {
if ($piggybank->account_id == $transaction->account_id) {
$transaction->piggybank()->associate($piggybank);
}
}
}
/*
* Connect budget and category:
*/
$budgetids = !isset($budget) || (isset($budget) && is_null($budget)) ? [] : [$budget->id];
$catids = is_null($category) ? [] : [$category->id];
$components = array_merge($budgetids,$catids);
$journal->components()->sync($components);
$journal->save();
if (isset($data['return_journal']) && $data['return_journal'] == true) {
return $journal;
}
return $journal->errors();
}
/**
* Returns messages about the validation.
*
* @param array $data
* @return array
* @throws FireflyException
*/
public function validate(array $data)
{
$errors = new MessageBag;
$warnings = new MessageBag;
$successes = new MessageBag;
/*
* Description:
*/
if (strlen($data['description']) == 0) {
$errors->add('description', 'The description should not be this short.');
}
if (strlen($data['description']) > 250) {
$errors->add('description', 'The description should not be this long.');
}
/*
* Amount
*/
if (floatval($data['amount']) <= 0) {
$errors->add('amount', 'The amount cannot be zero or less than zero.');
}
if (floatval($data['amount']) > 10000) {
$warnings->add('amount', 'OK, but that\'s a lot of money dude.');
}
/*
* Date
*/
try {
$date = new Carbon($data['date']);
} catch (Exception $e) {
$errors->add('date', 'The date entered was invalid');
}
if (strlen($data['date']) == 0) {
$errors->add('date', 'The date entered was invalid');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK!');
}
/*
* Category
*/
$category = $this->_categories->findByName($data['category']);
if (strlen($data['category']) == 0) {
$warnings->add('category', 'No category will be created.');
} else {
if (is_null($category)) {
$warnings->add('category', 'Will have to be created.');
} else {
$successes->add('category', 'OK!');
}
}
switch ($data['what']) {
default:
throw new FireflyException('Cannot validate a ' . $data['what']);
break;
case 'deposit':
/*
* Tests for deposit
*/
// asset account
$accountId = isset($data['account_id']) ? intval($data['account_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_id', 'Cannot find this asset account.');
} else {
$successes->add('account_id', 'OK!');
}
// revenue account:
if (strlen($data['revenue_account']) == 0) {
$warnings->add('revenue_account', 'Revenue account will be "cash".');
} else {
$exp = $this->_accounts->findRevenueAccountByName($data['revenue_account'], false);
if (is_null($exp)) {
$warnings->add('revenue_account', 'Expense account will be created.');
} else {
$successes->add('revenue_account', 'OK!');
}
}
break;
case 'transfer':
// account from
$accountId = isset($data['account_from_id']) ? intval($data['account_from_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_from_id', 'Cannot find this asset account.');
} else {
$successes->add('account_from_id', 'OK!');
}
unset($accountId);
// account to
$accountId = isset($data['account_to_id']) ? intval($data['account_to_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_to_id', 'Cannot find this asset account.');
} else {
$successes->add('account_to_id', 'OK!');
}
unset($accountId);
// piggy bank
$piggybankId = isset($data['piggybank_id']) ? intval($data['piggybank_id']) : 0;
$piggybank = $this->_piggybanks->find($piggybankId);
if (is_null($piggybank)) {
$warnings->add('piggybank_id', 'No piggy bank will be modified.');
} else {
$successes->add('piggybank_id', 'OK!');
}
break;
case 'withdrawal':
/*
* Tests for withdrawal
*/
// asset account
$accountId = isset($data['account_id']) ? intval($data['account_id']) : 0;
$account = $this->_accounts->find($accountId);
if (is_null($account)) {
$errors->add('account_id', 'Cannot find this asset account.');
} else {
$successes->add('account_id', 'OK!');
}
// expense account
if (strlen($data['expense_account']) == 0) {
$warnings->add('expense_account', 'Expense account will be "cash".');
} else {
$exp = $this->_accounts->findExpenseAccountByName($data['expense_account'], false);
if (is_null($exp)) {
$warnings->add('expense_account', 'Expense account will be created.');
} else {
$successes->add('expense_account', 'OK!');
}
}
// budget
if (!isset($data['budget_id']) || (isset($data['budget_id']) && intval($data['budget_id']) == 0)) {
$warnings->add('budget_id', 'No budget selected.');
} else {
$budget = $this->_budgets->find(intval($data['budget_id']));
if (is_null($budget)) {
$errors->add('budget_id', 'This budget does not exist');
} else {
$successes->add('budget_id', 'OK!');
}
}
break;
}
if (count($errors->get('description')) == 0) {
$successes->add('description', 'OK!');
}
if (count($errors->get('amount')) == 0) {
$successes->add('amount', 'OK!');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
/*
* Tests for deposit
*/
/*
* Tests for transfer
*/
}
/**
* Store a full transaction journal and associated stuff
*
* @param array $data
*
* @return MessageBag|\TransactionJournal
*
* @SuppressWarnings(PHPMD.ShortVariable)
*/
public function store(array $data)
{
/*
* save journal using repository
*/
$journal = $this->_journals->store($data);
/*
* If invalid, return the message bag:
*/
if (!$journal->validate()) {
return $journal->errors();
}
/*
* find budget using repository
*/
if (isset($data['budget_id'])) {
$budget = $this->_budgets->find($data['budget_id']);
}
/*
* find category using repository
*/
$category = $this->_categories->firstOrCreate($data['category']);
/*
* Find piggy bank using repository:
*/
$piggybank = null;
if (isset($data['piggybank_id'])) {
$piggybank = $this->_piggybanks->find($data['piggybank_id']);
}
/*
* save accounts using repositories
* this depends on the kind of transaction and i've yet to fix this.
*/
if (isset($data['account_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['expense_account'])) {
$to = $this->_accounts->findExpenseAccountByName($data['expense_account']);
}
if (isset($data['revenue_account'])) {
$from = $this->_accounts->findRevenueAccountByName($data['revenue_account']);
$to = $this->_accounts->findAssetAccountById($data['account_id']);
}
if (isset($data['account_from_id'])) {
$from = $this->_accounts->findAssetAccountById($data['account_from_id']);
}
if (isset($data['account_to_id'])) {
$to = $this->_accounts->findAssetAccountById($data['account_to_id']);
}
/*
* Add a custom error when they are the same.
*/
if ($to->id == $from->id) {
$bag = new MessageBag;
$bag->add('account_from_id', 'The account from cannot be the same as the account to.');
return $bag;
}
/*
* Save transactions using repository. We try to connect the (possibly existing)
* piggy bank to either transaction, knowing it will only work with one of them.
*/
/** @var \Transaction $one */
$one = $this->_journals->saveTransaction($journal, $from, floatval($data['amount']) * -1);
$one->connectPiggybank($piggybank);
$two = $this->_journals->saveTransaction($journal, $to, floatval($data['amount']));
$two->connectPiggybank($piggybank);
/*
* Count for $journal is zero? Then there were errors!
*/
if ($journal->transactions()->count() < 2) {
/*
* Join message bags and return them:
*/
$bag = $one->errors();
$bag->merge($two->errors());
return $bag;
}
/*
* Connect budget, category and piggy bank:
*/
if (isset($budget) && !is_null($budget)) {
$journal->budgets()->save($budget);
}
if (!is_null($category)) {
$journal->categories()->save($category);
}
$journal->completed = true;
$journal->save();
/*
* Trigger recurring transaction event.
*/
\Event::fire('journals.store',[$journal]);
if (isset($data['return_journal']) && $data['return_journal'] == true) {
return ['journal' => $journal, 'messagebag' => $journal->errors()];
}
return $journal->errors();
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace Firefly\Helper\Controllers;
use Illuminate\Support\MessageBag;
/**
* Interface TransactionInterface
*
* @package Firefly\Helper\Controllers
*/
interface TransactionInterface {
/**
* Store a full transaction journal and associated stuff
*
* @param array $data
*
* @return MessageBag|\TransactionJournal
*/
public function store(array $data);
/**
* Returns messages about the validation.
*
* @param array $data
*
* @return array
*/
public function validate(array $data);
/**
* @param \TransactionJournal $journal
* @param array $data
*
* @return MessageBag|\TransactionJournal
*/
public function update(\TransactionJournal $journal, array $data);
/**
* Overrule the user used when the class is created.
*
* @param \User $user
*
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@ -1,77 +0,0 @@
<?php
namespace Firefly\Helper\Email;
/**
* Class EmailHelper
*
* @package Firefly\Helper\Email
*/
class EmailHelper implements EmailHelperInterface
{
/**
* @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.');
}
);
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function sendPasswordMail(\User $user)
{
$password = \Str::random(12);
$user->password = $password;
$user->reset = \Str::random(32); // new one.
$user->forceSave();
$email = $user->email;
$data = ['password' => $password];
\Mail::send(
['emails.user.register-html', 'emails.user.register-text'], $data, function ($message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly!');
}
);
}
/**
* @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?');
}
);
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace Firefly\Helper\Email;
/**
* Interface EmailHelperInterface
*
* @package Firefly\Helper\Email
*/
interface EmailHelperInterface
{
/**
* @param \User $user
*
* @return mixed
*/
public function sendVerificationMail(\User $user);
/**
* @param \User $user
*
* @return mixed
*/
public function sendPasswordMail(\User $user);
/**
* @param \User $user
*
* @return mixed
*/
public function sendResetVerification(\User $user);
}

View File

@ -1,78 +0,0 @@
<?php
namespace Firefly\Helper;
use Illuminate\Support\ServiceProvider;
/**
* Class HelperServiceProvider
*
* @package Firefly\Helper
*/
class HelperServiceProvider extends ServiceProvider
{
/**
* Triggered automatically by Laravel
*/
public function register()
{
// controllers:
$this->app->bind(
'Firefly\Helper\Controllers\AccountInterface',
'Firefly\Helper\Controllers\Account'
);
$this->app->bind(
'Firefly\Helper\Controllers\ChartInterface',
'Firefly\Helper\Controllers\Chart'
);
$this->app->bind(
'Firefly\Helper\Controllers\JsonInterface',
'Firefly\Helper\Controllers\Json'
);
$this->app->bind(
'Firefly\Helper\Controllers\RecurringInterface',
'Firefly\Helper\Controllers\Recurring'
);
$this->app->bind(
'Firefly\Helper\Controllers\SearchInterface',
'Firefly\Helper\Controllers\Search'
);
$this->app->bind(
'Firefly\Helper\Controllers\TransactionInterface',
'Firefly\Helper\Controllers\Transaction'
);
$this->app->bind(
'Firefly\Helper\Controllers\CategoryInterface',
'Firefly\Helper\Controllers\Category'
);
$this->app->bind(
'Firefly\Helper\Controllers\BudgetInterface',
'Firefly\Helper\Controllers\Budget'
);
// mail:
$this->app->bind(
'Firefly\Helper\Email\EmailHelperInterface',
'Firefly\Helper\Email\EmailHelper'
);
// settings:
$this->app->bind(
'Firefly\Helper\Preferences\PreferencesHelperInterface',
'Firefly\Helper\Preferences\PreferencesHelper'
);
// settings:
$this->app->bind(
'Firefly\Helper\Toolkit\ToolkitInterface',
'Firefly\Helper\Toolkit\Toolkit'
);
}
}

View File

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

View File

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

View File

@ -1,504 +0,0 @@
<?php
namespace Firefly\Helper\Toolkit;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
/**
* Class Toolkit
*
* @package Firefly\Helper\Toolkit
* @SuppressWarnings(PHPMD.CamelCaseMethodName)
*/
class Toolkit implements ToolkitInterface
{
/**
* Lots of code in Firefly III still depends on session['start'], session['end'] and
* session['range'] to be available, even though this feature has been removed from Firefly
* in favor of a new reporting feature. This reporting feature can show the user past and future
* date ranges instead of the dashboard (the dashboard always shows "right now").
*
* The only actual choice the user is left with is the range, which can be changed using the Preferences pane.
*
* The start/end dates are set here, regardless of what the user might want to see.
*
* @return null
*/
public function getDateRange()
{
/*
* Get all data from the session:
*/
$range = $this->_getRange();
#\Log::debug('Range is: ' . $range);
$start = \Session::has('start') ? \Session::get('start') : new Carbon;
#\Log::debug('Session start is: ' . $start->format('Y-m-d'));
$end = \Session::has('end') ? \Session::get('end') : new Carbon;
#\Log::debug('Session end is : ' . $end->format('Y-m-d'));
/*
* Force start date to at the start of the $range.
* Ie. the start of the week, month, year.
*/
$start = $this->_updateStartDate($range, $start);
#\Log::debug('After update, session start is: ' . $start->format('Y-m-d'));
/*
* 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;
}
/**
*
*/
public function checkImportJobs()
{
/*
* Get all jobs.
*/
/** @var \Importmap $importJob */
$importJob = \Importmap::where('user_id', \Auth::user()->id)
->where('totaljobs', '>', \DB::Raw('`jobsdone`'))
->orderBy('created_at', 'DESC')
->first();
if (!is_null($importJob)) {
$diff = intval($importJob->totaljobs) - intval($importJob->jobsdone);
$date = new Carbon;
$today = new Carbon;
$date->addSeconds($diff);
\Session::put('job_pct', $importJob->pct());
\Session::put('job_text', $date->diffForHumans());
} else {
\Session::forget('job_pct');
\Session::forget('job_text');
}
}
protected function _getRange()
{
if (!is_null(\Session::get('range'))) {
$range = \Session::get('range');
} else {
/** @noinspection PhpUndefinedClassInspection */
$preferences = \App::make('Firefly\Helper\Preferences\PreferencesHelperInterface');
$viewRange = $preferences->get('viewRange', '1M');
// default range:
$range = $viewRange->data;
\Session::put('range', $range);
}
return $range;
}
/**
* @param $range
* @param Carbon $start
*
* @return Carbon
*/
protected function _updateStartDate($range, Carbon $start)
{
switch ($range) {
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
* @param Carbon $end
*
* @return Carbon
*/
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;
}
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;
}
}
protected 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;
}
protected 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;
}
public function next()
{
/*
* Get the start date and the range from the session
*/
$range = $this->_getRange();
$start = \Session::get('start');
/*
* Add some period to $start.
*/
$next = $this->_next($range, clone $start);
/*
* Save in session:
*/
\Session::put('start', $next);
return true;
}
public function prev()
{
/*
* Get the start date and the range from the session
*/
$range = $this->_getRange();
$start = \Session::get('start');
/*
* Substract some period to $start.
*/
$prev = $this->_previous($range, clone $start);
/*
* Save in session:
*/
\Session::put('start', $prev);
return true;
}
/**
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
* @param null $titleField
*
* @return mixed
*/
public function makeSelectList(Collection $set, $titleField = null)
{
$selectList = [];
/** @var Model $entry */
foreach ($set as $entry) {
$id = intval($entry->id);
$title = null;
if (is_null($titleField)) {
// try 'title' field.
if (isset($entry->title)) {
$title = $entry->title;
}
// try 'name' field
if (is_null($title)) {
$title = $entry->name;
}
// try 'description' field
if (is_null($title)) {
$title = $entry->description;
}
} else {
$title = $entry->$titleField;
}
$selectList[$id] = $title;
}
return $selectList;
}
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5)
{
if (strlen($start) != 6) {
throw new FireflyException('Start, ' . e($start) . ' should be a six character HTML colour.');
}
if (strlen($end) != 6) {
throw new FireflyException('End, ' . e($end) . ' should be a six character HTML colour.');
}
if ($steps < 1) {
throw new FireflyException('Steps must be > 1');
}
$start = '#' . $start;
$end = '#' . $end;
/*
* Split html colours.
*/
list($rs, $gs, $bs) = sscanf($start, "#%02x%02x%02x");
list($re, $ge, $be) = sscanf($end, "#%02x%02x%02x");
$stepr = ($re - $rs) / $steps;
$stepg = ($ge - $gs) / $steps;
$stepb = ($be - $bs) / $steps;
$return = [];
for ($i = 0; $i <= $steps; $i++) {
$cr = $rs + ($stepr * $i);
$cg = $gs + ($stepg * $i);
$cb = $bs + ($stepb * $i);
$return[] = $this->rgb2html($cr, $cg, $cb);
}
return $return;
}
protected function rgb2html($r, $g = -1, $b = -1)
{
$r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r));
$g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g));
$b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b));
$color = (strlen($r) < 2 ? '0' : '') . $r;
$color .= (strlen($g) < 2 ? '0' : '') . $g;
$color .= (strlen($b) < 2 ? '0' : '') . $b;
return '#' . $color;
}
/**
* @param Carbon $currentEnd
* @param $repeatFreq
* @throws FireflyException
*/
public function endOfPeriod(Carbon $currentEnd, $repeatFreq)
{
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->addDay();
break;
case 'weekly':
$currentEnd->addWeek()->subDay();
break;
case 'monthly':
$currentEnd->addMonth()->subDay();
break;
case 'quarterly':
$currentEnd->addMonths(3)->subDay();
break;
case 'half-year':
$currentEnd->addMonths(6)->subDay();
break;
case 'yearly':
$currentEnd->addYear()->subDay();
break;
}
}
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
* @throws FireflyException
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip)
{
$add = ($skip + 1);
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$date->addDays($add);
break;
case 'weekly':
$date->addWeeks($add);
break;
case 'monthly':
$date->addMonths($add);
break;
case 'quarterly':
$months = $add * 3;
$date->addMonths($months);
break;
case 'half-year':
$months = $add * 6;
$date->addMonths($months);
break;
case 'yearly':
$date->addYears($add);
break;
}
return $date;
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace Firefly\Helper\Toolkit;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface ToolkitInterface
*
* @package Firefly\Helper\Toolkit
*/
interface ToolkitInterface
{
/**
*
* @return null
*/
public function getDateRange();
/**
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
* @param null $titleField
*
* @return mixed
*/
public function makeSelectList(Collection $set, $titleField = null);
public function next();
public function prev();
public function checkImportJobs();
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5);
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip);
}

View File

@ -1,596 +0,0 @@
<?php
namespace Firefly\Queue;
use Illuminate\Queue\Jobs\Job;
/**
* Class Import
*
* @package Firefly\Queue
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class Import
{
/** @var \Firefly\Storage\Account\AccountRepositoryInterface */
protected $_accounts;
/** @var \Firefly\Storage\Import\ImportRepositoryInterface */
protected $_repository;
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface */
protected $_piggybanks;
/**
* This constructs the import handler and initiates all the relevant interfaces / classes.
*/
public function __construct()
{
$this->_accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
$this->_piggybanks = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
}
/**
* The final step in the import routine is to get all transactions which have one of their accounts
* still set to "import", which means it is a cash transaction. This routine will set them all to cash instead.
*
* If there was no account present for these accounts in the import routine (no beneficiary set), Firefly
* II would fall back to the import account.
*
* @param Job $job
* @param array $payload
*/
public function cleanImportAccount(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// two import account types.
$importAccountType = $this->_accounts->findAccountType('Import account');
$cashAccountType = $this->_accounts->findAccountType('Cash account');
// find or create import account:
$importAccount = $this->_accounts->firstOrCreate(
[
'name' => 'Import account',
'account_type_id' => $importAccountType->id,
'active' => 1,
'user_id' => $user->id,
]
);
// find or create cash account:
$cashAccount = $this->_accounts->firstOrCreate(
[
'name' => 'Cash account',
'account_type_id' => $cashAccountType->id,
'active' => 1,
'user_id' => $user->id,
]
);
// update all users transactions:
$count = \DB::table('transactions')
->where('account_id', $importAccount->id)->count();
\DB::table('transactions')
->where('account_id', $importAccount->id)
->update(['account_id' => $cashAccount->id]);
\Log::debug('Updated ' . $count . ' transactions from Import Account to cash.');
$job->delete(); // no count fix
}
/**
* @param \User $user
*/
protected function overruleUser(\User $user)
{
$this->_accounts->overruleUser($user);
$this->_repository->overruleUser($user);
$this->_piggybanks->overruleUser($user);
}
/**
* This job queues new jobs that will connect components to their proper transactions and updates the
* expense account: categories, budgets an beneficiaries used to be components.
*
* @param Job $job
* @param array $payload
*/
public function importComponentTransaction(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
/*
* Took too long to fix this:
*/
if ($job->attempts() > 10) {
\Log::error('Could not map transaction to component after 10 tries. KILL');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* Prep some vars from the payload
*/
$transactionId = intval($payload['data']['transaction_id']);
$componentId = intval($payload['data']['component_id']);
/*
* We don't know what kind of component we have. So we search for it. We have a specific function
* for this:
*/
$oldComponentMap = $this->_repository->findImportComponentMap($importMap, $componentId);
/*
* If the map is null, the component (whatever it is) is not imported yet, and we release the job.
*/
if (is_null($oldComponentMap)) {
\Log::notice('No map for this component, release transaction/component import.');
/*
* When in sync, its pointless to release jobs. Simply remove them.
*/
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Switch on the class found in the map, and push a new job to update the transaction journal:
*/
switch ($oldComponentMap->class) {
default:
\Log::error('Cannot handle "' . $oldComponentMap->class . '" in component<>transaction routine!');
$job->delete();
break;
case 'Budget':
\Log::debug('Push job to connect budget to transaction #' . $transactionId);
\Queue::push( // count fixed
'Firefly\Storage\Budget\BudgetRepositoryInterface@importUpdateTransaction', $payload
);
$importMap->totaljobs++;
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
break;
case 'Category':
\Log::debug('Push job to connect category to transaction #' . $transactionId);
\Queue::push( // count fixed
'Firefly\Storage\Category\CategoryRepositoryInterface@importUpdateTransaction', $payload
);
$importMap->totaljobs++;
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
break;
case 'Account':
\Log::debug('Push job to connect account to transaction #' . $transactionId);
\Queue::push( // count fixed
'Firefly\Storage\Account\AccountRepositoryInterface@importUpdateTransaction', $payload
);
$importMap->totaljobs++;
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
break;
}
return;
}
/**
* This job queues new jobs that will connect components to their proper transfers and updates the
* expense account: categories, budgets an beneficiaries used to be components. Even though not all
* of the transfers used to have these components, we check for them all.
*
* @param Job $job
* @param array $payload
*/
public function importComponentTransfer(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
/*
* Took too long to fix this:
*/
if ($job->attempts() > 10) {
\Log::error('Could not map transaction to component after 10 tries. KILL');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* Prep some vars from the payload
*/
$transferId = intval($payload['data']['transfer_id']);
$componentId = intval($payload['data']['component_id']);
/*
* We don't know what kind of component we have. So we search for it. We have a specific function
* for this:
*/
$oldComponentMap = $this->_repository->findImportComponentMap($importMap, $componentId);
/*
* If the map is null, the component (whatever it is) is not imported yet, and we release the job.
*/
if (is_null($oldComponentMap)) {
\Log::notice('No map for this component, release transfer/component import.');
/*
* When in sync, its pointless to release jobs. Simply remove them.
*/
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Switch on the class found in the map, and push a new job to update the transaction journal:
*/
switch ($oldComponentMap->class) {
default:
\Log::error('Cannot handle "' . $oldComponentMap->class . '" in component<>transfer routine!');
$job->delete();
break;
case 'Category':
\Log::debug('Push job to connect category to transfer #' . $transferId);
\Queue::push( // count fixed
'Firefly\Storage\Category\CategoryRepositoryInterface@importUpdateTransfer', $payload
);
$importMap->totaljobs++;
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
break;
case 'Budget':
\Log::debug('Push job to connect budget to transfer #' . $transferId);
\Queue::push( // count fixed
'Firefly\Storage\Budget\BudgetRepositoryInterface@importUpdateTransfer', $payload
);
$importMap->totaljobs++;
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
break;
}
}
/**
* This job will see if the particular setting is a 'piggyAccount' setting,
* one we need to fix all imported piggy banks.
*
* @param Job $job
* @param array $payload
*/
public function importSetting(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('No account found for piggyAccount setting after 10 tries. KILL!');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
$name = $payload['data']['name'];
switch ($name) {
default:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
return;
break;
case 'piggyAccount':
/*
* If user has this account, update all piggy banks:
*/
$accountID = intval($payload['data']['value']);
/*
* Is account imported already?
*/
$importEntry = $this->_repository->findImportEntry($importMap, 'Account', $accountID);
/*
* We imported this account already.
*/
if ($importEntry) {
$all = $this->_piggybanks->get();
$account = $this->_accounts->find($importEntry->new);
/*
* Update all piggy banks.
*/
if (!is_null($account)) {
\Log::debug('Updating all piggybanks, found the right setting.');
foreach ($all as $piggy) {
$piggy->account()->associate($account);
unset($piggy->leftInAccount);
$piggy->save();
}
}
} else {
\Log::notice('Account not yet imported, hold or 5 minutes.');
/*
* When in sync, its pointless to release jobs. Simply remove them.
*/
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
}
break;
}
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
}
/**
* This job will loop and queue jobs for the import file; almost every set of records will be imported.
*
* @param Job $job
* @param $payload
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
public function start(Job $job, array $payload)
{
\Log::debug('Start with job "start"');
$user = \User::find($payload['user']);
$filename = $payload['file'];
if (file_exists($filename) && !is_null($user)) {
/*
* Make an import map. Need it to refer back to import.
*/
$importMap = new \Importmap;
$importMap->user()->associate($user);
$importMap->file = $filename;
$importMap->totaljobs = 0;
$importMap->jobsdone = 0;
$importMap->save();
$totalJobs = 0;
/*
* Loop over all data in the JSON file, then create jobs.
*/
$raw = file_get_contents($filename);
$JSON = json_decode($raw);
// first import all asset accounts:
foreach ($JSON->accounts as $entry) {
\Log::debug('Create job to import asset account');
\Queue::push( // count fixed
'Firefly\Storage\Account\AccountRepositoryInterface@importAccount', [
'data' => $entry,
'class' => 'Account',
'account_type' => 'Asset account',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// then import all beneficiaries:
foreach ($JSON->components as $entry) {
if ($entry->type->type == 'beneficiary') {
\Log::debug('Create job to import expense account');
\Queue::push( // count fixed
'Firefly\Storage\Account\AccountRepositoryInterface@importAccount', [
'data' => $entry,
'class' => 'Account',
'account_type' => 'Expense account',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
}
// then import all categories.
foreach ($JSON->components as $entry) {
if ($entry->type->type == 'category') {
\Log::debug('Create job to import category');
\Queue::push( // count fixed
'Firefly\Storage\Category\CategoryRepositoryInterface@importCategory', [
'data' => $entry,
'class' => 'Category',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
}
// then import all budgets:
foreach ($JSON->components as $entry) {
if ($entry->type->type == 'budget') {
\Log::debug('Create job to import budget');
\Queue::push( // count fixed
'Firefly\Storage\Budget\BudgetRepositoryInterface@importBudget', [
'data' => $entry,
'class' => 'Budget',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
}
// then import all limits.
foreach ($JSON->limits as $entry) {
\Log::debug('Create job to import limit');
\Queue::push( // count fixed
'Firefly\Storage\Limit\LimitRepositoryInterface@importLimit', [
'data' => $entry,
'class' => 'Limit',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// all piggy banks
foreach ($JSON->piggybanks as $entry) {
\Log::debug('Create job to import piggy bank');
\Queue::push( // count fixed
'Firefly\Storage\Piggybank\PiggybankRepositoryInterface@importPiggybank', [
'data' => $entry,
'class' => 'Piggybank',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// all predictables.
foreach ($JSON->predictables as $entry) {
\Log::debug('Create job to import predictable');
\Queue::push( // count fixed
'Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface@importPredictable', [
'data' => $entry,
'class' => 'Predictable',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// all settings (to fix the piggy banks)
foreach ($JSON->settings as $entry) {
\Log::debug('Create job to import setting');
\Queue::push( // count fixed
'Firefly\Queue\Import@importSetting', [
'data' => $entry,
'class' => 'Setting',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// all transactions
foreach ($JSON->transactions as $entry) {
\Log::debug('Create job to import transaction');
\Queue::push( // count fixed
'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface@importTransaction', [
'data' => $entry,
'class' => 'Transaction',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// all transfers
foreach ($JSON->transfers as $entry) {
\Log::debug('Create job to import transfer');
\Queue::push( // count fixed
'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface@importTransfer', [
'data' => $entry,
'class' => 'Transfer',
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// then, fix all component <> transaction links
foreach ($JSON->component_transaction as $entry) {
\Log::debug('Create job to import components_transaction');
\Queue::push( // count fixed
'Firefly\Queue\Import@importComponentTransaction',
[
'data' => $entry,
'mapID' => $importMap->id
]
);
$totalJobs++;
}
// then, fix all component <> transfer links
foreach ($JSON->component_transfer as $entry) {
\Log::debug('Create job to import components_transfer');
\Queue::push( // count fixed
'Firefly\Queue\Import@importComponentTransfer',
[
'data' => $entry,
'mapID' => $importMap->id
]
);
$totalJobs++;
}
$importMap->totaljobs = $totalJobs;
$importMap->save();
/*
* We save the import map which now holds the number of jobs we've got planned.
*/
\Queue::push('Firefly\Queue\Import@cleanImportAccount', ['mapID' => $importMap->id]);
$job->delete(); // count fixed
\Log::debug('Done with job "start"');
}
}
}

View File

@ -1,146 +0,0 @@
<?php
namespace Firefly\Storage\Account;
use Illuminate\Queue\Jobs\Job;
/**
* Interface AccountRepositoryInterface
*
* @package Firefly\Storage\Account
*/
interface AccountRepositoryInterface
{
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importAccount(Job $job, array $payload);
/**
* @return mixed
*/
public function count();
/**
* Gets a list of accounts that have the mentioned type. Will automatically convert
* strings in this array to actual (model) account types.
*
* @param array $types
*
* @return Collection
*/
public function getOfTypes(array $types);
/**
* @param array $data
*
* @return mixed
*/
public function firstOrCreate(array $data);
/**
* @param \Account $account
*
* @return mixed
*/
public function destroy(\Account $account);
/**
* @param $accountId
*
* @return mixed
*/
public function find($accountId);
/**
* @param $type
*
* @return mixed
*/
public function findAccountType($type);
/**
* Takes a transaction/account component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransaction(Job $job, array $payload);
/**
* @param $id
*
* @return |Account|null
*/
public function findAssetAccountById($id);
/**
* @param $name
* @param $create
*
* @return |Account|null
*/
public function findExpenseAccountByName($name, $create = true);
/**
* @param $name
* @param $create
*
* @return |Account|null
*/
public function findRevenueAccountByName($name, $create = true);
/**
* @return mixed
*/
public function getActiveDefault();
/**
* @param $ids
*
* @return mixed
*/
public function getByIds(array $ids);
/**
* @return mixed
*/
public function getDefault();
/**
* @param \AccountType $type
*
* @return mixed
*/
public function getByAccountType(\AccountType $type);
/**
* @param \User $user
*
* @return mixed
*/
public function overruleUser(\User $user);
/**
* @param $data
*
* @return \Account
*/
public function store($data);
/**
* @param \Account $account
* @param $data
*
* @return mixed
*/
public function update(\Account $account, $data);
}

View File

@ -1,736 +0,0 @@
<?php
namespace Firefly\Storage\Account;
use Carbon\Carbon;
use Illuminate\Database\QueryException;
use Illuminate\Queue\Jobs\Job;
/**
* Class EloquentAccountRepository
*
* @package Firefly\Storage\Account
*/
class EloquentAccountRepository implements AccountRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @return mixed
*/
public function count()
{
return $this->_user->accounts()->count();
}
/**
* @param \Account $account
*
* @return bool|mixed
*/
public function destroy(\Account $account)
{
// find all transaction journals related to this account:
$journals = \TransactionJournal::withRelevantData()->accountIs($account)->get(['transaction_journals.*']);
$accountIDs = [];
/** @var \TransactionJournal $journal */
foreach ($journals as $journal) {
// remember the account id's of the transactions involved:
foreach ($journal->transactions as $t) {
$accountIDs[] = $t->account_id;
}
$journal->delete();
}
$accountIDs = array_unique($accountIDs);
if (count($accountIDs) > 0) {
// find the "initial balance" type accounts in this list. Should be just 1.
$query = $this->_user->accounts()->accountTypeIn(['Initial balance account'])
->whereIn('accounts.id', $accountIDs);
if ($query->count() == 1) {
$iba = $query->first(['accounts.*']);
$iba->delete();
}
}
$account->delete();
/**
*
*
* Also delete: initial balance, initial balance account, and transactions
*/
return true;
}
/**
* @param $id
*
* @return |Account|null
*/
public function findAssetAccountById($id)
{
return $this->_user->accounts()->find($id);
}
/**
* This method finds the expense account mentioned by name. This method is a sneaky little hobbits,
* because when you feed it "Import account" it will always return an import account of that type.
*
* @param $name
* @param $create
*
* @return null|\Account
*/
public function findExpenseAccountByName($name, $create = true)
{
$cashType = $this->findAccountType('Cash account');
$importType = $this->findAccountType('Import account');
// catch Import account:
if ($name == 'Import account') {
$import = $this->firstOrCreate(
[
'name' => 'Import account',
'user_id' => $this->_user->id,
'account_type_id' => $importType->id,
'active' => 1
]
);
return $import;
}
// find account:
$account = $this->_user->accounts()->where('name', $name)->accountTypeIn(
['Expense account', 'Beneficiary account']
)->first(['accounts.*']);
// create if not found:
if (strlen($name) > 0 && is_null($account) && $create === true) {
$type = $this->findAccountType('Expense account');
$set = [
'name' => $name,
'user_id' => $this->_user->id,
'active' => 1,
'account_type_id' => $type->id
];
$account = $this->firstOrCreate($set);
} else if (strlen($name) > 0 && is_null($account) && $create === false) {
return null;
}
// find cash account as fall back:
if (is_null($account)) {
$account = $this->_user->accounts()->where('account_type_id', $cashType->id)->first();
}
// create cash account as ultimate fall back:
if (is_null($account)) {
$set = [
'name' => 'Cash account',
'user_id' => $this->_user->id,
'active' => 1,
'account_type_id' => $cashType->id
];
$account = $this->firstOrCreate($set);
}
if ($account->active == 0 && $account->account_type_id != $cashType->id) {
return null;
}
return $account;
}
/**
* @param $type
*
* @return \AccountType|null
*/
public function findAccountType($type)
{
return \AccountType::where('type', $type)->first();
}
public function firstOrCreate(array $data)
{
return \Account::firstOrCreate($data);
}
/**
* @param $name
* @param $create
*
* @return |Account|null
*/
public function findRevenueAccountByName($name, $create = true)
{
// catch Import account:
if ($name == 'Import account') {
$importType = $this->findAccountType('Import account');
$import = $this->firstOrCreate(
[
'name' => 'Import account',
'user_id' => $this->_user->id,
'account_type_id' => $importType->id,
'active' => 1
]
);
return $import;
}
// find account:
$type = $this->findAccountType('Revenue account');
$account = $this->_user->accounts()->where('name', $name)->where('account_type_id', $type->id)->first();
// create if not found:
if (strlen($name) > 0 && is_null($account) && $create === true) {
$set = [
'name' => $name,
'user_id' => $this->_user->id,
'active' => 1,
'account_type_id' => $type->id
];
$account = $this->firstOrCreate($set);
} else if (strlen($name) > 0 && is_null($account) && $create === false) {
return null;
}
// find cash account as fall back:
if (is_null($account)) {
$cashType = $this->findAccountType('Cash account');
$account = $this->_user->accounts()->where('account_type_id', $cashType->id)->first();
}
// create cash account as ultimate fall back:
if (is_null($account)) {
$set = [
'name' => 'Cash account',
'user_id' => $this->_user->id,
'active' => 1,
'account_type_id' => $cashType->id
];
$account = $this->firstOrCreate($set);
}
if ($account->active == 0) {
return null;
}
return $account;
}
public function getByAccountType(\AccountType $type)
{
return $this->_user->accounts()->with('accounttype')->orderBy('name', 'ASC')
->where('account_type_id', $type->id)->get();
}
/**
* @param $ids
*
* @return array|mixed
*/
public function getByIds(array $ids)
{
if (count($ids) > 0) {
return $this->_user->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name', 'ASC')->get();
} else {
return $this->getActiveDefault();
}
}
//
// /**
// * @param $name
// *
// * @return \Account|mixed|null
// */
// public function createOrFindBeneficiary($name)
// {
// if (is_null($name) || strlen($name) == 0) {
// return null;
// }
// $type = \AccountType::where('type', 'Expense account')->first();
// return $this->createOrFind($name, $type);
// }
//
// /**
// * @param $name
// * @param \AccountType $type
// *
// * @return \Account|mixed
// */
// public function createOrFind($name, \AccountType $type = null)
// {
// $account = $this->findByName($name, $type);
// if (!$account) {
// $data = [
// 'name' => $name,
// 'account_type' => $type
// ];
//
// return $this->store($data);
// }
//
// return $account;
// }
//
// /**
// * @param $name
// * @param \AccountType $type
// *
// * @return mixed
// */
// public function findByName($name, \AccountType $type = null)
// {
// $type = is_null($type) ? \AccountType::where('type', 'Asset account')->first() : $type;
//
// return $this->_user->accounts()->where('account_type_id', $type->id)
// ->where('name', 'like', '%' . $name . '%')
// ->first();
// }
/**
* @return mixed
*/
public function getActiveDefault()
{
return $this->_user->accounts()->accountTypeIn(['Default account', 'Asset account'])->where(
'accounts.active', 1
)
->get(['accounts.*']);
}
/**
* @return mixed
*/
public function getDefault()
{
return $this->_user->accounts()->accountTypeIn(['Default account', 'Asset account'])
->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
}
/**
* Gets a list of accounts that have the mentioned type. Will automatically convert
* strings in this array to actual (model) account types.
*
* @param array $types
*
* @return Collection
*/
public function getOfTypes(array $types)
{
$accounts = $this->_user->accounts()->accountTypeIn($types)->get(['accounts.*']);
return $accounts;
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importAccount(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
/*
* maybe Account is already imported:
*/
$importEntry = $repository->findImportEntry($importMap, 'Account', intval($payload['data']['id']));
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported ' . $payload['data']['name'] . ' of type ' . $payload['account_type']);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* find the payload's account type:
*/
$payload['account_type'] = isset($payload['account_type']) ? $payload['account_type'] : 'Expense account';
$type = $this->findAccountType($payload['account_type']);
/*
* Create data array for store() procedure.
*/
$data = [
'account_type' => $type,
'name' => $payload['data']['name'],
];
if (isset($payload['data']['openingbalance'])) {
$data['openingbalance'] = floatval($payload['data']['openingbalance']);
$data['openingbalancedate'] = $payload['data']['openingbalancedate'];
}
if (isset($payload['data']['inactive'])) {
$data['active'] = intval($payload['data']['inactive']) == 0 ? 1 : 0;
}
/*
* Try to store:
*/
$account = $this->store($data);
/*
* Check for failure.
*/
if (count($account->errors()) > 0) {
\Log::error('Account creation error: ' . $account->errors()->first());
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
\Log::debug('Imported ' . $payload['account_type'] . ': ' . $payload['data']['name']);
/*
* Save meta data
*/
$repository->store($importMap, 'Account', intval($payload['data']['id']), $account->id);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
return;
}
// /**
// * Used for import
// *
// * @param $name
// *
// * @return mixed
// */
// public function findByNameAny($name)
// {
// return $this->_user->accounts()
// ->where('name', 'like', '%' . $name . '%')
// ->first();
// }
/**
* @param $data
*
* @return \Account
* @throws \Firefly\Exception\FireflyException
*/
public function store($data)
{
/**
* If the AccountType has been passed through, use it:
*/
if (isset($data['account_type']) && is_object($data['account_type'])
&& get_class($data['account_type']) == 'AccountType'
) {
$accountType = $data['account_type'];
} else if (isset($data['account_type']) && is_string($data['account_type'])) {
// if it isnt but set as string, find it:
$accountType = \AccountType::where('type', $data['account_type'])->first();
} else {
$accountType = \AccountType::where('type', 'Asset account')->first();
}
$active = isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval(
$data['active']
) : 1;
/**
* Create new account:
*/
$account = new \Account;
$account->accountType()->associate($accountType);
$account->user()->associate($this->_user);
$account->name = $data['name'];
$account->active
= isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval(
$data['active']
) : 1;
// try to save it:
try {
if ($account->save()) {
// create initial balance, if necessary:
if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) {
$amount = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
if ($amount != 0) {
$this->_createInitialBalance($account, $amount, $date);
}
}
}
} catch (QueryException $e) {
// do nothing
}
// whatever the result, return the account.
return $account;
}
/**
* @param \Account $account
* @param int $amount
* @param Carbon $date
*
* @return bool
* @SuppressWarnings(PHPMD.CamelCaseMethodName)
*/
protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date)
{
/*
* The repositories we need:
*/
/** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */
$transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface');
$transactions->overruleUser($this->_user);
/*
* get account type:
*/
$initialBalanceAT = $this->findAccountType('Initial balance account');
/*
* create new account
*/
$initial = new \Account;
$initial->accountType()->associate($initialBalanceAT);
$initial->user()->associate($this->_user);
$initial->name = $account->name . ' initial balance';
$initial->active = 0;
if ($initial->validate()) {
$initial->save();
/*
* create new transaction journal (and transactions):
*/
$set = [
'account_from_id' => $initial->id,
'account_to_id' => $account->id,
'description' => 'Initial Balance for ' . $account->name,
'what' => 'Opening balance',
'amount' => $amount,
'category' => '',
'date' => $date->format('Y-m-d')
];
$transactions->store($set);
return true;
}
return false;
}
/**
* Takes a transaction/account component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransaction(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found budget/account combination "' . $payload['data']['transaction_id'] . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
return;
}
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$journals->overruleUser($user);
/*
* Prep some vars from the payload
*/
$transactionId = intval($payload['data']['transaction_id']);
$componentId = intval($payload['data']['component_id']);
/*
* Find the import map for both:
*/
$accountMap = $repository->findImportEntry($importMap, 'Account', $componentId);
$transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId);
/*
* Either may be null:
*/
if (is_null($accountMap) || is_null($transactionMap)) {
\Log::notice('No map found in account/transaction mapper. Release.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Find the account and the transaction:
*/
$account = $this->find($accountMap->new);
/** @var \TransactionJournal $journal */
$journal = $journals->find($transactionMap->new);
/*
* If either is null, release:
*/
if (is_null($account) || is_null($journal)) {
\Log::notice('Map is incorrect in account/transaction mapper. Release.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Update one of the journal's transactions to have the right account:
*/
$importType = $this->findAccountType('Import account');
/** @var \Transaction $transaction */
$updated = false;
\Log::debug(
'Connect "' . $account->name . '" (#' . $account->id . ') to "' . $journal->description . '" (#'
. $journal->id . ')'
);
foreach ($journal->transactions as $index => $transaction) {
/*
* If it's of the right type, update it!
*/
\Log::debug(
'Transaction ' . $index . ' (#' . $transaction->id . '): [' . $transaction->account->account_type_id
. ' vs. ' . $importType->id . ']'
);
if ($transaction->account->account_type_id == $importType->id) {
$transaction->account()->associate($account);
$transaction->save();
$updated = true;
\Log::debug(
'Connected expense account "' . $account->name . '" to journal "' . $journal->description . '"'
);
}
}
if ($updated === false) {
\Log::error(
'Did not connect transactions of journal #' . $journal->id . ' to expense account ' . $account->id
);
}
$journal->save();
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $accountId
*
* @return mixed
*/
public function find($accountId)
{
return $this->_user->accounts()->where('id', $accountId)->first();
}
/**
* @param \Account $account
* @param $data
*
* @return \Account|mixed
*/
public function update(\Account $account, $data)
{
// update account accordingly:
$account->name = $data['name'];
if ($account->validate()) {
$account->save();
}
// update initial balance if necessary:
if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) {
/** @var \Firefly\Helper\Controllers\AccountInterface $interface */
$interface = \App::make('Firefly\Helper\Controllers\AccountInterface');
if ($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account') {
$journal = $interface->openingBalanceTransaction($account);
if ($journal) {
$journal->date = new Carbon($data['openingbalancedate']);
$journal->transactions[0]->amount = floatval($data['openingbalance']) * -1;
$journal->transactions[1]->amount = floatval($data['openingbalance']);
$journal->transactions[0]->save();
$journal->transactions[1]->save();
$journal->save();
}
}
}
return $account;
}
}

View File

@ -1,88 +0,0 @@
<?php
namespace Firefly\Storage\Budget;
use Illuminate\Queue\Jobs\Job;
/**
* Interface BudgetRepositoryInterface
*
* @package Firefly\Storage\Budget
*/
interface BudgetRepositoryInterface
{
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importBudget(Job $job, array $payload);
/**
* Takes a transaction/budget component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransaction(Job $job, array $payload);
/**
* Takes a transfer/budget component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransfer(Job $job, array $payload);
/**
* @param \Budget $budget
*
* @return mixed
*/
public function destroy(\Budget $budget);
/**
* @param $budgetId
*
* @return mixed
*/
public function find($budgetId);
/**
* @param $budgetName
* @return mixed
*/
public function findByName($budgetName);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
/**
* @return mixed
*/
public function get();
/**
* @param $data
*
* @return mixed
*/
public function store($data);
/**
* @param \Budget $budget
* @param $data
*
* @return mixed
*/
public function update(\Budget $budget, $data);
}

View File

@ -1,401 +0,0 @@
<?php
namespace Firefly\Storage\Budget;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Queue\Jobs\Job;
/**
* Class EloquentBudgetRepository
*
* @package Firefly\Storage\Budget
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*
*/
class EloquentBudgetRepository implements BudgetRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importBudget(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
/*
* maybe Budget is already imported:
*/
$importEntry = $repository->findImportEntry($importMap, 'Budget', intval($payload['data']['id']));
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported budget ' . $payload['data']['name']);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* maybe Budget is already imported.
*/
$budget = $this->findByName($payload['data']['name']);
if (is_null($budget)) {
/*
* Not imported yet.
*/
$budget = $this->store($payload['data']);
$repository->store($importMap, 'Budget', $payload['data']['id'], $budget->id);
\Log::debug('Imported budget "' . $payload['data']['name'] . '".');
} else {
/*
* already imported.
*/
$repository->store($importMap, 'Budget', $payload['data']['id'], $budget->id);
\Log::debug('Already had budget "' . $payload['data']['name'] . '".');
}
// update map:
$importMap->jobsdone++;
$importMap->save();
// delete job.
$job->delete(); // count fixed
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $budgetName
*
* @return \Budget|null
*/
public function findByName($budgetName)
{
return $this->_user->budgets()->whereName($budgetName)->first();
}
/**
* @param $data
*
* @return \Budget
*/
public function store($data)
{
$budget = new \Budget;
$budget->name = $data['name'];
$budget->user()->associate($this->_user);
$budget->save();
// if limit, create limit (repetition itself will be picked up elsewhere).
if (isset($data['amount']) && floatval($data['amount']) > 0) {
$startDate = new Carbon;
$limitData = [
'budget_id' => $budget->id,
'startdate' => $startDate->format('Y-m-d'),
'period' => $data['repeat_freq'],
'amount' => floatval($data['amount']),
'repeats' => 0
];
/** @var \Firefly\Storage\Limit\LimitRepositoryInterface $limitRepository */
$limitRepository = \App::make('Firefly\Storage\Limit\LimitRepositoryInterface');
$limitRepository->overruleUser($this->_user);
$limit = $limitRepository->store($limitData);
\Event::fire('limits.store', [$limit]);
}
if ($budget->validate()) {
$budget->save();
}
return $budget;
}
/**
* Takes a transfer/budget component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransfer(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found budget/transfer combination "' . $payload['data']['transfer_id'] . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$journals->overruleUser($user);
/*
* Prep some vars from the payload
*/
$transferId = intval($payload['data']['transfer_id']);
$componentId = intval($payload['data']['component_id']);
/*
* Find the import map for both:
*/
$budgetMap = $repository->findImportEntry($importMap, 'Budget', $componentId);
$transferMap = $repository->findImportEntry($importMap, 'Transfer', $transferId);
/*
* Either may be null:
*/
if (is_null($budgetMap) || is_null($transferMap)) {
\Log::notice('No map found in budget/transfer mapper. Release.');
if(\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Find the budget and the transaction:
*/
$budget = $this->find($budgetMap->new);
/** @var \TransactionJournal $journal */
$journal = $journals->find($transferMap->new);
/*
* If either is null, release:
*/
if (is_null($budget) || is_null($journal)) {
\Log::notice('Map is incorrect in budget/transfer mapper. Release.');
if(\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Update journal to have budget:
*/
$journal->budgets()->save($budget);
$journal->save();
\Log::debug('Connected budget "' . $budget->name . '" to journal "' . $journal->description . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/**
* Takes a transaction/budget component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransaction(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found budget/transaction combination "' . $payload['data']['transaction_id'] . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$journals->overruleUser($user);
/*
* Prep some vars from the payload
*/
$transactionId = intval($payload['data']['transaction_id']);
$componentId = intval($payload['data']['component_id']);
/*
* Find the import map for both:
*/
$budgetMap = $repository->findImportEntry($importMap, 'Budget', $componentId);
$transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId);
/*
* Either may be null:
*/
if (is_null($budgetMap) || is_null($transactionMap)) {
\Log::notice('No map found in budget/transaction mapper. Release.');
if(\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Find the budget and the transaction:
*/
$budget = $this->find($budgetMap->new);
/** @var \TransactionJournal $journal */
$journal = $journals->find($transactionMap->new);
/*
* If either is null, release:
*/
if (is_null($budget) || is_null($journal)) {
\Log::notice('Map is incorrect in budget/transaction mapper. Release.');
if(\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Update journal to have budget:
*/
$journal->budgets()->save($budget);
$journal->save();
\Log::debug('Connected budget "' . $budget->name . '" to journal "' . $journal->description . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/**
* @param $budgetId
*
* @return \Budget|null
*/
public function find($budgetId)
{
return $this->_user->budgets()->find($budgetId);
}
/**
* @param \Budget $budget
*
* @return bool
*/
public function destroy(\Budget $budget)
{
$budget->delete();
return true;
}
/**
* @return Collection
*/
public function get()
{
$set = $this->_user->budgets()->with(
['limits' => function ($q) {
$q->orderBy('limits.startdate', 'DESC');
}, 'limits.limitrepetitions' => function ($q) {
$q->orderBy('limit_repetitions.startdate', 'ASC');
}]
)->orderBy('name', 'ASC')->get();
return $set;
}
/**
* @param \Budget $budget
* @param $data
*
* @return \Budget|mixed
*/
public function update(\Budget $budget, $data)
{
// update account accordingly:
$budget->name = $data['name'];
if ($budget->validate()) {
$budget->save();
}
return $budget;
}
}

View File

@ -1,95 +0,0 @@
<?php
namespace Firefly\Storage\Category;
use Illuminate\Queue\Jobs\Job;
/**
* Interface CategoryRepositoryInterface
*
* @package Firefly\Storage\Category
*/
interface CategoryRepositoryInterface
{
/**
* Takes a transaction/category component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransaction(Job $job, array $payload);
/**
* Takes a transfer/category component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransfer(Job $job, array $payload);
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importCategory(Job $job, array $payload);
/**
* @return mixed
*/
public function get();
/**
* @param $categoryId
*
* @return mixed
*/
public function find($categoryId);
/**
* @param $name
*
* @return mixed
*/
public function firstOrCreate($name);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
/**
* @param $name
*
* @return mixed
*/
public function findByName($name);
/**
* @param $data
*
* @return mixed
*/
public function store($data);
/**
* @param $category
* @param $data
*
* @return mixed
*/
public function update($category, $data);
/**
* @param $category
*
* @return mixed
*/
public function destroy($category);
}

View File

@ -1,387 +0,0 @@
<?php
namespace Firefly\Storage\Category;
use Illuminate\Queue\Jobs\Job;
/**
* Class EloquentCategoryRepository
*
* @package Firefly\Storage\Category
*/
class EloquentCategoryRepository implements CategoryRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* Takes a transfer/category component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransfer(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found category/transfer combination "' . $payload['data']['transfer_id'] . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
return;
}
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$journals->overruleUser($user);
/*
* Prep some vars from the payload
*/
$transferId = intval($payload['data']['transfer_id']);
$componentId = intval($payload['data']['component_id']);
/*
* Find the import map for both:
*/
$categoryMap = $repository->findImportEntry($importMap, 'Category', $componentId);
$transferMap = $repository->findImportEntry($importMap, 'Transfer', $transferId);
/*
* Either may be null:
*/
if (is_null($categoryMap) || is_null($transferMap)) {
\Log::notice('No map found in category/transfer mapper. Release.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Find the budget and the transaction:
*/
$category = $this->find($categoryMap->new);
/** @var \TransactionJournal $journal */
$journal = $journals->find($transferMap->new);
/*
* If either is null, release:
*/
if (is_null($category) || is_null($journal)) {
\Log::notice('Map is incorrect in category/transfer mapper. Release.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Update journal to have budget:
*/
$journal->categories()->save($category);
$journal->save();
\Log::debug('Connected category "' . $category->name . '" to journal "' . $journal->description . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $categoryId
*
* @return mixed
*/
public function find($categoryId)
{
return $this->_user->categories()->find($categoryId);
}
/**
* Takes a transaction/category component and updates the transaction journal to match.
*
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importUpdateTransaction(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found category/transaction combination "' . $payload['data']['transaction_id'] . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
return;
}
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$journals->overruleUser($user);
/*
* Prep some vars from the payload
*/
$transactionId = intval($payload['data']['transaction_id']);
$componentId = intval($payload['data']['component_id']);
/*
* Find the import map for both:
*/
$categoryMap = $repository->findImportEntry($importMap, 'Category', $componentId);
$transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId);
/*
* Either may be null:
*/
if (is_null($categoryMap) || is_null($transactionMap)) {
\Log::notice('No map found in category/transaction mapper. Release.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Find the budget and the transaction:
*/
$category = $this->find($categoryMap->new);
/** @var \TransactionJournal $journal */
$journal = $journals->find($transactionMap->new);
/*
* If either is null, release:
*/
if (is_null($category) || is_null($journal)) {
\Log::notice('Map is incorrect in category/transaction mapper. Release.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Update journal to have budget:
*/
$journal->categories()->save($category);
$journal->save();
\Log::debug('Connected category "' . $category->name . '" to journal "' . $journal->description . '"');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importCategory(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
/*
* Maybe the category has already been imported
*/
$importEntry = $repository->findImportEntry($importMap, 'Category', intval($payload['data']['id']));
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported category ' . $payload['data']['name']);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* try to find category first
*/
$current = $this->findByName($payload['data']['name']);
/*
* If not found, create it:
*/
if (is_null($current)) {
$category = $this->store($payload['data']);
$repository->store($importMap, 'Category', $payload['data']['id'], $category->id);
\Log::debug('Imported category "' . $payload['data']['name'] . '".');
} else {
$repository->store($importMap, 'Category', $payload['data']['id'], $current->id);
\Log::debug('Already had category "' . $payload['data']['name'] . '".');
}
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/**
* @param $name
*
* @return mixed
*/
public function findByName($name)
{
if ($name == '' || strlen($name) == 0) {
return null;
}
return $this->_user->categories()->where('name', $name)->first();
}
/**
* @param $data
*
* @return \Category|mixed
*/
public function store($data)
{
$category = new \Category;
$category->name = $data['name'];
$category->user()->associate($this->_user);
$category->save();
return $category;
}
/**
* @param $name
*
* @return \Category|mixed
*/
public function firstOrCreate($name)
{
if (strlen($name) == 0) {
return null;
}
$data = [
'name' => $name,
'user_id' => $this->_user->id,
];
return \Category::firstOrCreate($data);
}
/**
* @param $category
*
* @return bool|mixed
*/
public function destroy($category)
{
$category->delete();
return true;
}
/**
* @return mixed
*/
public function get()
{
return $this->_user->categories()->orderBy('name', 'ASC')->get();
}
/**
* @param $category
* @param $data
*
* @return mixed
*/
public function update($category, $data)
{
// update account accordingly:
$category->name = $data['name'];
if ($category->validate()) {
$category->save();
}
return $category;
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace Firefly\Storage\Import;
class EloquentImportRepository implements ImportRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
public function findImportComponentMap(\Importmap $map, $oldComponentId)
{
$entry = \Importentry::where('importmap_id', $map->id)
->whereIn('class', ['Budget', 'Category', 'Account', 'Component'])
->where('old', intval($oldComponentId))->first();
return $entry;
}
public function findImportEntry(\Importmap $map, $class, $oldID)
{
return \Importentry::where('importmap_id', $map->id)->where('class', $class)->where('old', $oldID)->first();
}
public function findImportMap($id)
{
return \Importmap::find($id);
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
public function store(\Importmap $map, $class, $oldID, $newID)
{
$entry = new \Importentry;
$entry->importmap()->associate($map);
$entry->class = $class;
$entry->old = intval($oldID);
$entry->new = intval($newID);
$entry->save();
}
}

View File

@ -1,51 +0,0 @@
<?php
namespace Firefly\Storage\Import;
/**
* Interface ImportRepositoryInterface
* @package Firefly\Storage\Import
*/
interface ImportRepositoryInterface
{
/**
* @param \Importmap $map
* @param $class
* @param $oldID
* @param $newID
* @return mixed
*/
public function store(\Importmap $map, $class, $oldID, $newID);
/**
* @param $id
*
* @return mixed
*/
public function findImportMap($id);
/**
* @param \Importmap $map
* @param $class
* @param $oldID
*
* @return mixed
*/
public function findImportEntry(\Importmap $map, $class, $oldID);
/**
* @param \Importmap $map
* @param $oldComponentId
*
* @return mixed
*/
public function findImportComponentMap(\Importmap $map, $oldComponentId);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@ -1,249 +0,0 @@
<?php
namespace Firefly\Storage\Limit;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
/**
* Class EloquentLimitRepository
*
* @package Firefly\Storage\Limit
*/
class EloquentLimitRepository implements LimitRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importLimit(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error(
'No budget found for limit #' . $payload['data']['id'] . '. Prob. for another component. KILL!'
);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
return;
}
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgets */
$budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$budgets->overruleUser($user);
/*
* Find the budget this limit is part of:
*/
$importEntry = $repository->findImportEntry($importMap, 'Budget', intval($payload['data']['component_id']));
/*
* There is no budget (yet?)
*/
if (is_null($importEntry)) {
$componentId = intval($payload['data']['component_id']);
\Log::warning('Budget #' . $componentId . ' not found. Requeue import job.');
if(\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Find budget import limit is for:
*/
$budget = $budgets->find($importEntry->new);
if (!is_null($budget)) {
/*
* Is actual limit already imported?
*/
$limit = $this->findByBudgetAndDate($budget, new Carbon($payload['data']['date']));
if (is_null($limit)) {
/*
* It isn't imported yet.
*/
$payload['data']['budget_id'] = $budget->id;
$payload['data']['startdate'] = $payload['data']['date'];
$payload['data']['period'] = 'monthly';
/*
* Store limit, and fire event for LimitRepetition.
*/
$limit = $this->store($payload['data']);
$repository->store($importMap, 'Limit', $payload['data']['id'], $limit->id);
\Event::fire('limits.store', [$limit]);
\Log::debug('Imported limit for budget ' . $budget->name);
} else {
/*
* Limit already imported:
*/
$repository->store($importMap, 'Budget', $payload['data']['id'], $limit->id);
}
} else {
\Log::error(print_r($importEntry,true));
\Log::error('Cannot import limit! Big bad error!');
}
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
public function findByBudgetAndDate(\Budget $budget, Carbon $date)
{
return \Limit::whereComponentId($budget->id)->where('startdate', $date->format('Y-m-d'))->first();
}
/**
* @param $data
*
* @return \Limit
*/
public function store($data)
{
$budget = \Budget::find($data['budget_id']);
if (is_null($budget)) {
\Session::flash('error', 'No such budget.');
return new \Limit;
}
// set the date to the correct start period:
$date = new Carbon($data['startdate']);
switch ($data['period']) {
case 'daily':
$date->startOfDay();
break;
case 'weekly':
$date->startOfWeek();
break;
case 'monthly':
$date->startOfMonth();
break;
case 'quarterly':
$date->firstOfQuarter();
break;
case 'half-year':
if (intval($date->format('m')) >= 7) {
$date->startOfYear();
$date->addMonths(6);
} else {
$date->startOfYear();
}
break;
case 'yearly':
$date->startOfYear();
break;
}
// find existing:
$count = \Limit::
leftJoin('components', 'components.id', '=', 'limits.component_id')->where(
'components.user_id', $this->_user->id
)->where('startdate', $date->format('Y-m-d'))->where('component_id', $data['budget_id'])->where(
'repeat_freq', $data['period']
)->count();
if ($count > 0) {
\Session::flash('error', 'There already is an entry for these parameters.');
return new \Limit;
}
// create new limit:
$limit = new \Limit;
$limit->budget()->associate($budget);
$limit->startdate = $date;
$limit->amount = floatval($data['amount']);
$limit->repeats = isset($data['repeats']) ? intval($data['repeats']) : 0;
$limit->repeat_freq = $data['period'];
if (!$limit->save()) {
\Session::flash('error', 'Could not save: ' . $limit->errors()->first());
}
return $limit;
}
/**
* @param \Limit $limit
*
* @return bool
*/
public function destroy(\Limit $limit)
{
$limit->delete();
return true;
}
/**
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getTJByBudgetAndDateRange(\Budget $budget, Carbon $start, Carbon $end)
{
$result = $budget->transactionjournals()->with('transactions')->after($start)->before($end)->get();
return $result;
}
/**
* @param \Limit $limit
* @param $data
*
* @return mixed|void
*/
public function update(\Limit $limit, $data)
{
$limit->startdate = new Carbon($data['startdate']);
$limit->repeat_freq = $data['period'];
$limit->repeats = isset($data['repeats']) && $data['repeats'] == '1' ? 1 : 0;
$limit->amount = floatval($data['amount']);
$limit->save();
return $limit;
}
}

View File

@ -1,70 +0,0 @@
<?php
namespace Firefly\Storage\Limit;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
/**
* Interface LimitRepositoryInterface
*
* @package Firefly\Storage\Limit
*/
interface LimitRepositoryInterface
{
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importLimit(Job $job, array $payload);
/**
* @param \Limit $limit
*
* @return mixed
*/
public function destroy(\Limit $limit);
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return mixed
*/
public function findByBudgetAndDate(\Budget $budget, Carbon $date);
/**
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getTJByBudgetAndDateRange(\Budget $budget, Carbon $start, Carbon $end);
/**
* @param $data
*
* @return mixed
*/
public function store($data);
/**
* @param \Limit $limit
* @param $data
*
* @return mixed
*/
public function update(\Limit $limit, $data);
/**
* @param \User $user
*
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@ -1,381 +0,0 @@
<?php
namespace Firefly\Storage\Piggybank;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
/**
* Class EloquentLimitRepository
*
* @package Firefly\Storage\Limit
*/
class EloquentPiggybankRepository implements PiggybankRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importPiggybank(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('No account available for piggy bank "' . $payload['data']['name'] . '". KILL!');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts->overruleUser($user);
/*
* Maybe the piggy bank has already been imported
*/
$importEntry = $repository->findImportEntry($importMap, 'Piggybank', intval($payload['data']['id']));
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported piggy bank ' . $payload['data']['name']);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* Try to find related piggybank:
*/
$piggyBank = $this->findByName($payload['data']['name']);
/*
* Find an account (any account, really, at this point).
*/
$accountType = $accounts->findAccountType('Asset account');
/** @var Collection $set */
$set = $accounts->getByAccountType($accountType);
/*
* If there is an account to attach to this piggy bank, simply use that one.
*/
if ($set->count() > 0) {
/** @var \Account $account */
$account = $set->first();
$payload['data']['account_id'] = $account->id;
} else {
\Log::notice('No account available yet for piggy bank "' . $payload['data']['name'] . '".');
if(\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* No existing piggy bank, create it:
*/
if (is_null($piggyBank)) {
$payload['data']['targetamount'] = floatval($payload['data']['target']);
$payload['data']['repeats'] = 0;
$payload['data']['rep_every'] = 1;
$payload['data']['reminder_skip'] = 1;
$payload['data']['rep_times'] = 1;
$piggyBank = $this->store($payload['data']);
/*
* Store and fire event.
*/
$repository->store($importMap, 'Piggybank', intval($payload['data']['id']), $piggyBank->id);
\Log::debug('Imported piggy "' . $payload['data']['name'] . '".');
\Event::fire('piggybanks.store', [$piggyBank]);
} else {
/*
* Already have a piggy bank with this name, we skip it.
*/
$repository->store($importMap, 'Piggybank', $payload['data']['id'], $piggyBank->id);
\Log::debug('Already imported piggy "' . $payload['data']['name'] . '".');
}
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
public function findByName($piggyBankName)
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', $this->_user->id
)->where('piggybanks.name', $piggyBankName)->first(['piggybanks.*']);
}
public function countNonrepeating()
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', $this->_user->id
)->where('repeats', 0)->count();
}
public function countRepeating()
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', $this->_user->id
)->where('repeats', 1)->count();
}
/**
* @param \Piggybank $piggyBank
*
* @return mixed|void
*/
public function destroy(\Piggybank $piggyBank)
{
$piggyBank->delete();
return true;
}
/**
* @param $piggyBankId
*
* @return mixed
*/
public function find($piggyBankId)
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', $this->_user->id
)->where('piggybanks.id', $piggyBankId)->first(['piggybanks.*']);
}
/**
* @return mixed
*/
public function get()
{
$piggies = $this->_user->piggybanks()->with(['account', 'piggybankrepetitions'])->get();
return $piggies;
}
/**
* @param \Account $account
*
* @return mixed|void
*/
public function leftOnAccount(\Account $account)
{
$balance = $account->balance();
/** @var \Piggybank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;
}
return $balance;
}
/**
* @param \Piggybank $piggyBank
* @param $amount
*
* @return bool|mixed
*/
public function modifyAmount(\Piggybank $piggyBank, $amount)
{
$rep = $piggyBank->currentRelevantRep();
if (!is_null($rep)) {
$rep->currentamount += $amount;
$rep->save();
}
return true;
}
/**
* @param $data
*
* @return \Piggybank
*/
public function store($data)
{
if (isset($data['targetdate']) && $data['targetdate'] == '') {
unset($data['targetdate']);
}
if (isset($data['reminder']) && $data['reminder'] == 'none') {
unset($data['reminder']);
}
if (isset($data['startdate']) && $data['startdate'] == '') {
unset($data['startdate']);
}
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts->overruleUser($this->_user);
$account = isset($data['account_id']) ? $accounts->find($data['account_id']) : null;
$piggyBank = new \Piggybank($data);
if (!is_null($piggyBank->reminder) && is_null($piggyBank->startdate) && is_null($piggyBank->targetdate)) {
$piggyBank->errors()->add('reminder', 'Cannot create reminders without start ~ AND target date.');
\Log::error('PiggyBank create-error: ' . $piggyBank->errors()->first());
return $piggyBank;
}
if ($piggyBank->repeats && !isset($data['targetdate'])) {
$piggyBank->errors()->add('targetdate', 'Target date is mandatory!');
\Log::error('PiggyBank create-error: ' . $piggyBank->errors()->first());
return $piggyBank;
}
if (!is_null($account)) {
$piggyBank->account()->associate($account);
}
$today = new Carbon;
if ($piggyBank->validate()) {
if (!is_null($piggyBank->targetdate) && $piggyBank->targetdate < $today) {
$piggyBank->errors()->add('targetdate', 'Target date cannot be in the past.');
\Log::error('PiggyBank create-error: ' . $piggyBank->errors()->first());
return $piggyBank;
}
if (!is_null($piggyBank->reminder) && !is_null($piggyBank->targetdate)) {
// first period for reminder is AFTER target date.
$reminderSkip = $piggyBank->reminder_skip < 1 ? 1 : intval($piggyBank->reminder_skip);
$firstReminder = new Carbon;
switch ($piggyBank->reminder) {
case 'day':
$firstReminder->addDays($reminderSkip);
break;
case 'week':
$firstReminder->addWeeks($reminderSkip);
break;
case 'month':
$firstReminder->addMonths($reminderSkip);
break;
case 'year':
$firstReminder->addYears($reminderSkip);
break;
default:
throw new FireflyException('Invalid reminder period');
break;
}
if ($firstReminder > $piggyBank->targetdate) {
$piggyBank->errors()->add(
'reminder', 'The reminder has been set to remind you after the piggy bank will expire.'
);
\Log::error('PiggyBank create-error: ' . $piggyBank->errors()->first());
return $piggyBank;
}
}
$piggyBank->save();
}
return $piggyBank;
}
/**
* @param \Piggybank $piggy
* @param $data
*
* @return mixed
*/
public function update(\Piggybank $piggy, $data)
{
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts->overruleUser($this->_user);
$account = isset($data['account_id']) ? $accounts->find($data['account_id']) : null;
if (!is_null($account)) {
$piggy->account()->associate($account);
}
$piggy->name = $data['name'];
$piggy->targetamount = floatval($data['targetamount']);
$piggy->reminder = isset($data['reminder']) && $data['reminder'] != 'none' ? $data['reminder'] : null;
$piggy->reminder_skip = $data['reminder_skip'];
$piggy->targetdate = strlen($data['targetdate']) > 0 ? new Carbon($data['targetdate']) : null;
$piggy->startdate
=
isset($data['startdate']) && strlen($data['startdate']) > 0 ? new Carbon($data['startdate']) : null;
foreach ($piggy->piggybankrepetitions()->get() as $rep) {
$rep->delete();
}
if ($piggy->repeats == 1) {
$piggy->rep_every = intval($data['rep_every']);
$piggy->rep_length = $data['rep_length'];
}
if ($piggy->validate()) {
// check the things we check for new piggies
$piggy->save();
}
return $piggy;
}
}

View File

@ -1,95 +0,0 @@
<?php
namespace Firefly\Storage\Piggybank;
use Illuminate\Queue\Jobs\Job;
/**
* Interface LimitRepositoryInterface
*
* @package Firefly\Storage\Limit
*/
interface PiggybankRepositoryInterface
{
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importPiggybank(Job $job, array $payload);
/**
* @return mixed
*/
public function countNonrepeating();
/**
* @return mixed
*/
public function countRepeating();
/**
* @param \Piggybank $piggyBank
*
* @return mixed
*/
public function destroy(\Piggybank $piggyBank);
/**
* @param $piggyBankId
*
* @return mixed
*/
public function find($piggyBankId);
public function findByName($piggyBankName);
/**
* @return mixed
*/
public function get();
/**
* Will tell you how much money is left on this account.
*
* @param \Account $account
*
* @return mixed
*/
public function leftOnAccount(\Account $account);
/**
* @param \Piggybank $piggyBank
* @param $amount
*
* @return mixed
*/
public function modifyAmount(\Piggybank $piggyBank, $amount);
/**
* @param $data
*
* @return mixed
*/
public function store($data);
/**
* @param \Piggybank $piggy
* @param $data
*
* @return mixed
*/
public function update(\Piggybank $piggy, $data);
/**
* @param \User $user
*
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@ -1,223 +0,0 @@
<?php
namespace Firefly\Storage\RecurringTransaction;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\MessageBag;
/**
* Class EloquentRecurringTransactionRepository
*
* @package Firefly\Storage\RecurringTransaction
*/
class EloquentRecurringTransactionRepository implements RecurringTransactionRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param \RecurringTransaction $recurringTransaction
*
* @return bool|mixed
*/
public function destroy(\RecurringTransaction $recurringTransaction)
{
$recurringTransaction->delete();
return true;
}
/**
* @return mixed
*/
public function get()
{
return $this->_user->recurringtransactions()->get();
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importPredictable(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
/*
* maybe the recurring transaction is already imported:
*/
$oldId = intval($payload['data']['id']);
$description = $payload['data']['description'];
$importEntry = $repository->findImportEntry($importMap, 'RecurringTransaction', $oldId);
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported recurring transaction #' . $payload['data']['id']);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
// try to find related recurring transaction:
$recurringTransaction = $this->findByName($payload['data']['description']);
if (is_null($recurringTransaction)) {
$amount = floatval($payload['data']['amount']);
$pct = intval($payload['data']['pct']);
$set = [
'name' => $description,
'match' => join(',', explode(' ', $description)),
'amount_min' => $amount * ($pct / 100) * -1,
'amount_max' => $amount * (1 + ($pct / 100)) * -1,
'date' => date('Y-m-') . $payload['data']['dom'],
'repeat_freq' => 'monthly',
'active' => intval($payload['data']['inactive']) == 1 ? 0 : 1,
'automatch' => 1,
];
$recurringTransaction = $this->store($set);
$this->store($importMap, 'RecurringTransaction', $oldId, $recurringTransaction->id);
\Log::debug('Imported predictable ' . $description);
} else {
$this->store($importMap, 'RecurringTransaction', $oldId, $recurringTransaction->id);
\Log::debug('Already had predictable ' . $description);
}
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
public function findByName($name)
{
return $this->_user->recurringtransactions()->where('name', 'LIKE', '%' . $name . '%')->first();
}
/**
* @param $data
*
* @return MessageBag
*/
public function store($data)
{
$messageBag = new MessageBag;
$recurringTransaction = new \RecurringTransaction(
[
'user_id' => $this->_user->id,
'name' => $data['name'],
'match' => join(' ', explode(',', $data['match'])),
'amount_max' => floatval($data['amount_max']),
'amount_min' => floatval($data['amount_min']),
'date' => new Carbon($data['date']),
'active' => isset($data['active']) ? intval($data['active']) : 0,
'automatch' => isset($data['automatch']) ? intval($data['automatch']) : 0,
'skip' => isset($data['skip']) ? intval($data['skip']) : 0,
'repeat_freq' => $data['repeat_freq'],
]
);
// unique name?
$count = $this->_user->recurringtransactions()->whereName($data['name'])->count();
if ($count > 0) {
$messageBag->add('name', 'A recurring transaction with this name already exists.');
return $messageBag;
}
// both amounts zero?:
if ($recurringTransaction->amount_max == 0 && $recurringTransaction->amount_min == 0) {
$messageBag->add('amount_max', 'Amount max and min cannot both be zero.');
return $messageBag;
}
if ($recurringTransaction->amount_max < $recurringTransaction->amount_min) {
$messageBag->add('amount_max', 'Amount max must be more than amount min.');
return $messageBag;
}
if ($recurringTransaction->amount_min > $recurringTransaction->amount_max) {
$messageBag->add('amount_max', 'Amount min must be less than amount max.');
return $messageBag;
}
if ($recurringTransaction->validate()) {
$recurringTransaction->save();
} else {
$messageBag = $recurringTransaction->errors();
}
return $messageBag;
}
/**
* @param \RecurringTransaction $recurringTransaction
* @param $data
*
* @return MessageBag
*/
public function update(\RecurringTransaction $recurringTransaction, $data)
{
$messageBag = new MessageBag;
$recurringTransaction->name = $data['name'];
$recurringTransaction->match = join(' ', explode(',', $data['match']));
$recurringTransaction->amount_max = floatval($data['amount_max']);
$recurringTransaction->amount_min = floatval($data['amount_min']);
// both amounts zero:
if ($recurringTransaction->amount_max == 0 && $recurringTransaction->amount_min == 0) {
$messageBag->add('amount_max', 'Amount max and min cannot both be zero.');
return $messageBag;
}
$recurringTransaction->date = new Carbon($data['date']);
$recurringTransaction->active = isset($data['active']) ? intval($data['active']) : 0;
$recurringTransaction->automatch = isset($data['automatch']) ? intval($data['automatch']) : 0;
$recurringTransaction->skip = isset($data['skip']) ? intval($data['skip']) : 0;
$recurringTransaction->repeat_freq = $data['repeat_freq'];
if ($recurringTransaction->validate()) {
$recurringTransaction->save();
} else {
$messageBag = $recurringTransaction->errors();
}
return $messageBag;
}
}

View File

@ -1,64 +0,0 @@
<?php
namespace Firefly\Storage\RecurringTransaction;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\MessageBag;
/**
* Interface RecurringTransactionRepositoryInterface
*
* @package Firefly\Storage\RecurringTransaction
*/
interface RecurringTransactionRepositoryInterface
{
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importPredictable(Job $job, array $payload);
/**
* @return mixed
*/
public function get();
/**
* @param $name
* @return mixed
*/
public function findByName($name);
/**
* @param $data
*
* @return MessageBag
*/
public function store($data);
/**
* @param \RecurringTransaction $recurringTransaction
*
* @return mixed
*/
public function destroy(\RecurringTransaction $recurringTransaction);
/**
* @param \RecurringTransaction $recurringTransaction
* @param $data
*
* @return MessageBag
*/
public function update(\RecurringTransaction $recurringTransaction, $data);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@ -1,34 +0,0 @@
<?php
namespace Firefly\Storage\Reminder;
use Carbon\Carbon;
/**
* Class EloquentReminderRepository
*
* @package Firefly\Storage\Reminder
*/
class EloquentReminderRepository implements ReminderRepositoryInterface
{
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace Firefly\Storage\Reminder;
/**
* Interface ReminderRepositoryInterface
*
* @package Firefly\Storage\Reminder
*/
interface ReminderRepositoryInterface
{
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

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