Compare commits

..

51 Commits
3.0.0 ... 3.0.3

Author SHA1 Message Date
James Cole
e1a2b4b9af Fixed a bug that would stop you from registering. 2014-09-11 15:19:30 +02:00
James Cole
0eadfa1c83 Lighter chart. 2014-09-11 15:19:18 +02:00
James Cole
c8dd935460 Better feedback. 2014-09-11 15:19:07 +02:00
James Cole
3290ce85a9 Updated read me. 2014-09-09 20:57:23 +02:00
James Cole
60ef80c1a5 Show the balance after the occurrence of a transaction (experimental). 2014-09-09 20:37:11 +02:00
James Cole
74e852b8bd Some final touches. 2014-09-09 20:19:19 +02:00
James Cole
90ae21d257 Some cleanup in the models. 2014-09-09 20:01:31 +02:00
James Cole
fdf03cd8e2 Some comment cleanup in the libraries. 2014-09-09 20:01:13 +02:00
James Cole
f6586be5e7 Enddate can be NULL. 2014-09-09 20:00:15 +02:00
James Cole
f9dc627d84 Cleanup controllers and small bug fixes. 2014-09-09 20:00:04 +02:00
James Cole
309177ca9c Extended gitignore. 2014-09-09 19:59:34 +02:00
James Cole
456d2342b6 Do not need a CSS file here. 2014-09-09 19:59:19 +02:00
James Cole
0717aa22d7 Some minor cleanup. 2014-09-09 14:03:55 +02:00
James Cole
b8e07ac38e Removed another word wrap. 2014-09-09 10:46:35 +02:00
James Cole
c7273e4b60 Removed a chart wrap. 2014-09-09 10:45:51 +02:00
James Cole
33d4fd4af0 Small fix for the budget overview. 2014-09-09 10:43:12 +02:00
James Cole
71b11e26d2 Cleaned up the chart controller. 2014-09-09 10:42:58 +02:00
James Cole
77f4111b09 Fixed some bugs in the recurring transactions trigger 2014-09-09 07:10:37 +02:00
James Cole
9965297f36 Cleaning up the chart controller. 2014-09-09 07:10:16 +02:00
James Cole
5ca466a826 Update view. 2014-09-08 10:38:39 +02:00
James Cole
90f417facc Update model. 2014-09-08 10:38:26 +02:00
James Cole
eacbd038b7 Updated migration to include recurring transactions. 2014-09-08 10:38:14 +02:00
James Cole
5446e85424 New trigger for new journals. 2014-09-08 10:37:55 +02:00
James Cole
77b4942691 Uniform query. 2014-09-08 10:37:31 +02:00
James Cole
824cf71e0b Some re-alignment and catches for NULLs 2014-09-08 10:37:14 +02:00
James Cole
239bbd30c0 Fire some events. 2014-09-08 10:36:32 +02:00
James Cole
6f6b653d54 Removed some logging. 2014-09-08 10:36:19 +02:00
James Cole
e4155ce735 Moved a migration because of reasons. 2014-09-08 10:36:06 +02:00
James Cole
7eaf307834 Some code cleanup. 2014-09-04 09:02:54 +02:00
James Cole
7db7950415 Updated read-me. 2014-09-04 08:32:37 +02:00
James Cole
fcc184cd2a Removed ALL tests. Yes, I know. 2014-09-04 08:32:13 +02:00
James Cole
6423feff3a Cleaned up some models and controllers. 2014-09-04 08:31:45 +02:00
James Cole
e97da25d5a Cleaned up build files. 2014-09-04 08:31:07 +02:00
James Cole
f49a37a38e Removed unused classes. 2014-09-04 08:30:17 +02:00
James Cole
07e6b33095 Some tests fixed. But messy! [skip ci] 2014-09-03 21:12:51 +02:00
James Cole
9136b50e3c Slowly working my way "down" the list of controllers to fix and enhance. 2014-09-03 16:50:53 +02:00
James Cole
c3fd5c7136 Some new stuff that really doesn't belong here. I'm not good at this. 2014-09-03 07:11:35 +02:00
James Cole
98612dd253 Last changes to make the import routine work. 2014-09-02 19:12:57 +02:00
James Cole
4d7f5238dd Moar updates. 2014-09-02 17:27:28 +02:00
James Cole
f472a01a80 First attempt, trying to build an import stuff routine. 2014-09-02 08:58:56 +02:00
James Cole
420b5790e3 New ignore files [skip ci] 2014-08-31 15:26:51 +02:00
James Cole
2e342e47a7 Added unicity to AccountType model. Fixed some tests because of this. 2014-08-31 10:03:24 +02:00
James Cole
58b3334f05 Cleaned up some tests and moved some methods. 2014-08-31 08:59:43 +02:00
James Cole
4cd955e5cf Small text change in login. 2014-08-31 07:33:11 +02:00
James Cole
4dbf410cdf Removed codeception. 2014-08-31 07:32:59 +02:00
James Cole
074e6d52b1 Regenerated all model phpdocs. 2014-08-31 07:32:48 +02:00
James Cole
a0a36c5137 Ran model helper, added some new features. 2014-08-30 14:27:05 +02:00
James Cole
9db4137a1b Revamped the account controller. 2014-08-30 14:26:33 +02:00
James Cole
85f1e744b8 Added a new type of validation to be used for account names. 2014-08-30 14:25:52 +02:00
James Cole
8d90061d90 Included codeception for future use. 2014-08-30 14:25:21 +02:00
James Cole
b20d84e4b8 Fixed the accounts routes to be more strict about the types of accounts they accept. 2014-08-30 14:25:03 +02:00
152 changed files with 3658 additions and 5533 deletions

2
.gitignore vendored
View File

@@ -12,3 +12,5 @@ _ide_helper.php
/build/logs/clover.xml
index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json

View File

@@ -2,7 +2,6 @@ firefly-iii
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/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)
![Still maintained?](http://stillmaintained.com/JC5/firefly-iii.png)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
@@ -14,42 +13,52 @@ Firefly Mark III is a new version of Firefly built upon best practices and lesso
from building [Firefly](https://github.com/JC5/Firefly). It's Mark III since the original Firefly never made it outside of my
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).
- 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).
Everything is organised:
- Clear views that should show you how you're doing;
- Easy navigation through your records;
- Browse back and forth to see previous months or even years;
- Lots of help text in case you don't get it;
- Lots of charts because we all love them.
## Changes
Firefly III will feature:
Firefly III will feature, but does not feature yet:
- Double-entry bookkeeping system;
- Better budgeting tools;
- Better financial reporting;
- Financial reporting showing you how well you are doing;
- More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts
- Credit cards
- More robust code base (mainly for my own peace of mind);
- More test-coverage (aka: actual test coverage);
## More features
- 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 will be 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. And it will not, in the future.
- The nesting of budgets, categories and beneficiaries will be removed.
- Firefly will be able to automatically login a specified account. Although this is pretty unsafe, it removes the need for you to login to your own tool.
- 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.
## Not changed
Some stuff has been removed:
- The nesting of budgets, categories and beneficiaries is removed because it was pretty pointless.
- Firefly will not encrypt the content of the (MySQL) tables. Old versions of Firefly had this capability but it sucks when searching, sorting and organizing entries.
## Current state
I have the basics up and running and test coverage is doing very well.
I have the basics up and running. Test coverage is currently non-existent.
Current issues are the consistent look-and-feel of forms and likewise, the consistent inner workings of most of Firefly.
Example: every "create"-action tends to be slightly different from the rest. Also is the fact that not all lists
and forms are equally well thought of; some are not looking very well or miss feedback.
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.
Most forms will not allow you to enter invalid data because the database cracks, not because it's actually checked.
I'm still thinking about a way to build consistent forms. Laravel doesn't really cut it.
A lot of views have CSRF vulnerabilities. The general advice is NOT to use this tool in production.
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

@@ -26,6 +26,9 @@ $(function () {
str += '<span style="color:' + colour + '">' + point.series.name + '</span>: € ' + Highcharts.numberFormat(point.y, 2) + '<br />';
}
if (x == 1) {
str += '<span style="color:' + colour + '">' + point.series.name + '</span>: € ' + Highcharts.numberFormat(point.y, 2) + '<br />';
}
if (x == 2) {
str += '<span style="color:' + colour + '">' + point.series.name + '</span>: ' + Highcharts.numberFormat(point.y, 1) + '%<br />';
}
}

View File

@@ -1,48 +0,0 @@
.bootstrap-tagsinput {
background-color: #fff;
border: 1px solid #ccc;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
display: inline-block;
padding: 4px 6px;
margin-bottom: 10px;
color: #555;
vertical-align: middle;
border-radius: 4px;
max-width: 100%;
line-height: 22px;
cursor: text;
}
.bootstrap-tagsinput input {
border: none;
box-shadow: none;
outline: none;
background-color: transparent;
padding: 0;
margin: 0;
width: auto !important;
max-width: inherit;
}
.bootstrap-tagsinput input:focus {
border: none;
box-shadow: none;
}
.bootstrap-tagsinput .tag {
margin-right: 2px;
color: white;
}
.bootstrap-tagsinput .tag [data-role="remove"] {
margin-left: 8px;
cursor: pointer;
}
.bootstrap-tagsinput .tag [data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.bootstrap-tagsinput {width:100%;}

View File

@@ -1,2 +1,3 @@
local/
laptop/
vagrant/

View File

@@ -40,6 +40,7 @@ return [
# 'Barryvdh\Debugbar\ServiceProvider',
'Firefly\Storage\StorageServiceProvider',
'Firefly\Helper\HelperServiceProvider',
'Firefly\Validation\ValidationServiceProvider',
'Codesleeve\AssetPipeline\AssetPipelineServiceProvider',
],
'manifest' => storage_path() . '/meta',

View File

@@ -5,6 +5,8 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
/**
* Class AccountController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class AccountController extends \BaseController
{
@@ -27,47 +29,30 @@ class AccountController extends \BaseController
*/
public function create()
{
return View::make('accounts.create');
return View::make('accounts.create')->with('title', 'Create account');
}
/**
* @param Account $account
*
* @return \Illuminate\View\View
* @return $this
*/
public function delete(Account $account)
{
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return \View::make('error')->with(
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
return View::make('accounts.delete')->with('account', $account);
return View::make('accounts.delete')->with('account', $account)
->with('title', 'Delete account "' . $account->name . '"');
}
/**
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function destroy(Account $account)
{
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
$result = $this->_repository->destroy($account);
if ($result === true) {
$this->_repository->destroy($account);
Session::flash('success', 'The account was deleted.');
} else {
Session::flash('error', 'Could not delete the account.');
}
return Redirect::route('accounts.index');
@@ -76,54 +61,56 @@ class AccountController extends \BaseController
/**
* @param Account $account
*
* @return \Illuminate\View\View
* @return $this
*/
public function edit(Account $account)
{
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
$openingBalance = $this->_accounts->openingBalanceTransaction($account);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)
->with('title', 'Edit account "' . $account->name . '"');
}
/**
* @return \Illuminate\View\View
* @return $this
*/
public function index()
{
$accounts = $this->_repository->get();
$display = $this->_accounts->index($accounts);
$set = [
'personal' => [],
'beneficiaries' => []
];
foreach ($accounts as $account) {
switch ($account->accounttype->type) {
case 'Default account':
$set['personal'][] = $account;
break;
case 'Beneficiary account':
$set['beneficiaries'][] = $account;
break;
}
}
return View::make('accounts.index')->with('accounts', $display);
return View::make('accounts.index')->with('accounts', $set)->with('title', 'All your accounts');
}
/**
* @param Account $account
*
* @return \Illuminate\View\View
* @return $this
*/
public function show(Account $account)
{
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot show this account type (' . $accountType->description . ').'
$data = $this->_accounts->show($account, 40);
return View::make('accounts.show')->with('account', $account)->with('show', $data)->with(
'title',
'Details for account "' . $account->name . '"'
);
}
$show = $this->_accounts->show($account, 40);
return View::make('accounts.show')->with('account', $account)->with('show', $show);
}
/**
* @return \Illuminate\Http\RedirectResponse
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store()
{
@@ -133,14 +120,14 @@ class AccountController extends \BaseController
if ($account->validate()) {
// saved! return to wherever.
Session::flash('success', 'Account "' . $account->name . '" created!');
if (Input::get('create') == '1') {
if (intval(Input::get('create')) === 1) {
return Redirect::route('accounts.create')->withInput();
} else {
return Redirect::route('accounts.index');
}
} else {
// did not save, return with error:
Session::flash('error', 'Could not save the new account. Please check the form.');
Session::flash('error', 'Could not save the new account: ' . $account->errors()->first());
return Redirect::route('accounts.create')->withErrors($account->errors())->withInput();
@@ -150,16 +137,10 @@ class AccountController extends \BaseController
/**
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(Account $account)
{
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot show this account type (' . $accountType->description . ').'
);
}
$account = $this->_repository->update($account, Input::all());
if ($account->validate()) {
Session::flash('success', 'Account "' . $account->name . '" updated.');

View File

@@ -1,5 +1,7 @@
<?php
use Illuminate\Routing\Controller;
/**
* Class BaseController
*/
@@ -11,8 +13,6 @@ class BaseController extends Controller
*/
public function __construct()
{
\Event::fire('limits.check');
\Event::fire('piggybanks.check');
}
/**

View File

@@ -6,6 +6,10 @@ use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
/**
* Class BudgetController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
*/
class BudgetController extends BaseController
{
@@ -30,7 +34,7 @@ class BudgetController extends BaseController
{
$periods = \Config::get('firefly.periods_to_text');
return View::make('budgets.create')->with('periods', $periods);
return View::make('budgets.create')->with('periods', $periods)->with('title', 'Create a new budget');
}
/**
@@ -40,7 +44,8 @@ class BudgetController extends BaseController
*/
public function delete(Budget $budget)
{
return View::make('budgets.delete')->with('budget', $budget);
return View::make('budgets.delete')->with('budget', $budget)
->with('title', 'Delete budget "' . $budget->name . '"');
}
/**
@@ -50,20 +55,16 @@ class BudgetController extends BaseController
*/
public function destroy(Budget $budget)
{
// remove budget
Event::fire('budgets.destroy', [$budget]); // just before deletion.
$result = $this->_repository->destroy($budget);
if ($result === true) {
$this->_repository->destroy($budget);
Session::flash('success', 'The budget was deleted.');
// redirect:
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
}
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not delete the budget. Check the logs to be sure.');
}
return Redirect::route('budgets.index');
}
@@ -74,7 +75,8 @@ class BudgetController extends BaseController
*/
public function edit(Budget $budget)
{
return View::make('budgets.edit')->with('budget', $budget);
return View::make('budgets.edit')->with('budget', $budget)
->with('title', 'Edit budget "' . $budget->name . '"');
}
@@ -84,16 +86,14 @@ class BudgetController extends BaseController
public function indexByBudget()
{
$budgets = $this->_repository->get();
$today = new Carbon;
return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', $today);
return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon)
->with('title', 'All your budgets grouped by budget');
}
/**
* @return $this|\Illuminate\View\View
* @throws Firefly\Exception\FireflyException
* @return $this
*/
public function indexByDate()
{
@@ -101,45 +101,58 @@ class BudgetController extends BaseController
$set = $this->_repository->get();
$budgets = $this->_budgets->organizeByDate($set);
return View::make('budgets.indexByDate')->with('budgets', $budgets);
return View::make('budgets.indexByDate')->with('budgets', $budgets)
->with('title', 'All your budgets grouped by date');
}
/**
* Three use cases for this view:
*
* - Show everything.
* - Show a specific repetition.
* - Show everything shows NO repetition.
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return int
*/
public function show(Budget $budget)
public function show(Budget $budget, \LimitRepetition $repetition = null)
{
/**
* Use the
*/
$useSessionDates = Input::get('useSession') == 'true' ? true : false;
$filters = [];
if (!is_null(Input::get('rep'))) {
$repetitionId = intval(Input::get('rep'));
$repetitions = $this->_budgets->organizeRepetition($repetitionId);
$filters[] = $repetitions[0]['limit'];
$filters[] = $repetitions[0]['limitrepetition'];
} else {
if (Input::get('noenvelope') == 'true') {
$repetitions = $this->_budgets->outsideRepetitions($budget);
$filters[] = 'no_envelope';
} else {
// grab all limit repetitions, order them, show them:
$repetitions = $this->_budgets->organizeRepetitions($budget, $useSessionDates);
}
$view = null;
$title = null;
\Log::debug('Is envelope true? ' . (Input::get('noenvelope') == 'true'));
switch (true) {
case (!is_null($repetition)):
$data = $this->_budgets->organizeRepetition($repetition);
$view = 1;
$title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf(
$repetition->limit->amount,
false
);
break;
case (Input::get('noenvelope') == 'true'):
$data = $this->_budgets->outsideRepetitions($budget);
$view = 2;
$title = $budget->name . ', transactions outside an envelope';
break;
default:
$data = $this->_budgets->organizeRepetitions($budget, $useSessionDates);
$view = $useSessionDates ? 3 : 4;
$title = $useSessionDates ? $budget->name . ' in session period' : $budget->name;
break;
}
return View::make('budgets.show')->with('budget', $budget)->with('repetitions', $repetitions)->with(
'filters', $filters
)->with('highlight', Input::get('highlight'))->with('useSessionDates', $useSessionDates);
return View::make('budgets.show')
->with('budget', $budget)
->with('repetitions', $data)
->with('view', $view)
->with('highlight', Input::get('highlight'))
->with('useSessionDates', $useSessionDates)
->with('title', $title);
}
/**

View File

@@ -5,6 +5,8 @@ use Firefly\Storage\Category\CategoryRepositoryInterface as CRI;
/**
* Class CategoryController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class CategoryController extends BaseController
{
@@ -26,7 +28,7 @@ class CategoryController extends BaseController
*/
public function create()
{
return View::make('categories.create');
return View::make('categories.create')->with('title', 'Create a new category');
}
/**
@@ -36,7 +38,8 @@ class CategoryController extends BaseController
*/
public function delete(Category $category)
{
return View::make('categories.delete')->with('category', $category);
return View::make('categories.delete')->with('category', $category)
->with('title', 'Delete category "' . $category->name . '"');
}
/**
@@ -46,13 +49,8 @@ class CategoryController extends BaseController
*/
public function destroy(Category $category)
{
$result = $this->_repository->destroy($category);
if ($result === true) {
$this->_repository->destroy($category);
Session::flash('success', 'The category was deleted.');
} else {
Session::flash('error', 'Could not delete the category. Check the logs to be sure.');
}
return Redirect::route('categories.index');
}
@@ -63,7 +61,8 @@ class CategoryController extends BaseController
*/
public function edit(Category $category)
{
return View::make('categories.edit')->with('category', $category);
return View::make('categories.edit')->with('category', $category)
->with('title', 'Edit category "' . $category->name . '"');
}
/**
@@ -73,7 +72,8 @@ class CategoryController extends BaseController
{
$categories = $this->_repository->get();
return View::make('categories.index')->with('categories', $categories);
return View::make('categories.index')->with('categories', $categories)
->with('title', 'All your categories');
}
/**
@@ -91,7 +91,7 @@ class CategoryController extends BaseController
return View::make('categories.show')->with('category', $category)->with('journals', $journals)->with(
'highlight', Input::get('highlight')
);
)->with('title', 'Overview for category "' . $category->name . '"');
}
/**

View File

@@ -6,6 +6,9 @@ use Firefly\Storage\Account\AccountRepositoryInterface;
/**
* Class ChartController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ChartController extends BaseController
{
@@ -25,35 +28,28 @@ class ChartController extends BaseController
}
/**
* This method takes a budget, all limits and all their repetitions and displays three numbers per repetition:
* the amount of money in the repetition (represented as "an envelope"), the amount spent and the spent percentage.
*
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetDefault(\Budget $budget)
{
$expense = [];
$left = [];
$envelope = [];
// get all limit repetitions for this budget.
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $rep */
foreach ($limit->limitrepetitions as $rep) {
$spentInRep = \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', '>=', $rep->startdate->format('Y-m-d')
)->where('transaction_journals.date', '<=', $rep->enddate->format('Y-m-d'))->where(
'amount', '>', 0
)->sum('amount');
$pct = round(($spentInRep / $limit->amount) * 100, 2);
// get the amount of money spent in this period on this budget.
$spentInRep = $rep->amount - $rep->left();
$pct = round((floatval($spentInRep) / floatval($limit->amount)) * 100, 2);
$name = $rep->periodShow();
$envelope[] = [$name, floatval($limit->amount)];
$expense[] = [$name, floatval($spentInRep)];
$left[] = [$name, $pct];
}
@@ -63,6 +59,12 @@ class ChartController extends BaseController
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' => 'All envelopes',
'series' => [
[
'type' => 'line',
'yAxis' => 1,
'name' => 'Amount in envelope',
'data' => $envelope
],
[
'type' => 'column',
'name' => 'Expenses in envelope',
@@ -75,6 +77,7 @@ class ChartController extends BaseController
'data' => $left
]
]
];
@@ -82,7 +85,12 @@ class ChartController extends BaseController
}
/**
* This method takes a single limit repetition (so a single "envelope") and displays the amount of money spent
* per day and subsequently how much money is left.
*
* @param LimitRepetition $rep
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetLimit(\LimitRepetition $rep)
{
@@ -92,14 +100,7 @@ class ChartController extends BaseController
$leftInLimit = [];
$currentLeftInLimit = floatval($rep->limit->amount);
while ($current <= $rep->enddate) {
$spent = \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', $current->format('Y-m-d')
)->where('amount', '>', 0)->sum('amount');
$spent = $this->_chart->spentOnDay($budget, $current);
$spent = floatval($spent) == 0 ? null : floatval($spent);
$entry = [$current->timestamp * 1000, $spent];
$expense[] = $entry;
@@ -132,49 +133,40 @@ class ChartController extends BaseController
}
/**
* This method takes a budget and gets all transactions in it which haven't got an envelope (limit).
*
* Usually this means that very old and unorganized or very NEW transactions get displayed; there was never an
* envelope or it hasn't been created (yet).
*
*
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetNoLimits(\Budget $budget)
{
$inRepetitions = [];
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$set = $budget->transactionjournals()->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Withdrawal')->where(
'date', '>=', $repetition->startdate->format('Y-m-d')
)->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->get(
['transaction_journals.id']
);
foreach ($set as $item) {
$inRepetitions[] = $item->id;
}
}
/*
* Firefly can go about this two ways. Either it finds all transactions which definitely are IN an envelope
* and exclude them or it searches for transactions outside of the range of any of the envelopes there are.
*
* Since either is kinda shitty Firefly uses the first one because it's easier to build.
*/
$inRepetitions = $this->_chart->allJournalsInBudgetEnvelope($budget);
}
$query = $budget->transactionjournals()->whereNotIn(
'transaction_journals.id', $inRepetitions
)->orderBy('date', 'DESC')->orderBy(
'transaction_journals.id', 'DESC'
);
/*
* With this set of id's, Firefly can search for all journals NOT in that set.
* BUT they have to be in the budget (duh).
*/
$set = $this->_chart->journalsNotInSet($budget, $inRepetitions);
/*
* Next step: get all transactions for those journals.
*/
$transactions = $this->_chart->transactionsByJournals($set);
$result = $query->get(['transaction_journals.id']);
$set = [];
foreach ($result as $entry) {
$set[] = $entry->id;
}
// all transactions for these journals, grouped by date and SUM
$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`')]
);
// this set builds the chart:
/*
* this set builds the chart:
*/
$expense = [];
foreach ($transactions as $t) {
@@ -186,126 +178,104 @@ class ChartController extends BaseController
'subtitle' => 'Not organized by an envelope',
'series' => [
[
'type' => 'spline',
'type' => 'column',
'name' => 'Expenses per day',
'data' => $expense
]
]
];
return Response::json($return);
}
/**
* This method gets all transactions within a budget within the period set by the current session
* start and end date. It also includes any envelopes which might exist within this period.
*
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetSession(\Budget $budget)
{
$expense = [];
$repetitionSeries = [];
$current = clone Session::get('start');
$series = [];
$end = clone Session::get('end');
while ($current <= $end) {
$spent = \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', $current->format('Y-m-d')
)->where('amount', '>', 0)->sum('amount');
$spent = floatval($spent) == 0 ? null : floatval($spent);
if (!is_null($spent)) {
$expense[] = [$current->timestamp * 1000, $spent];
}
$start = clone Session::get('start');
/*
* Expenses per day in the session's period. That's easy.
*/
$expense = [];
$current = clone Session::get('start');
while ($current <= $end) {
$spent = $this->_chart->spentOnDay($budget, $current);
$spent = floatval($spent) == 0 ? null : floatval($spent);
$expense[] = [$current->timestamp * 1000, $spent];
$current->addDay();
}
// find all limit repetitions (for this budget) between start and end.
$start = clone Session::get('start');
$repetitionSeries[] = [
$series[] = [
'type' => 'column',
'name' => 'Expenses per day',
'data' => $expense
];
unset($expense, $spent, $current);
/*
* Find all limit repetitions (for this budget) between start and end. This is
* quite a complex query.
*/
$reps = $this->_chart->limitsInRange($budget, $start, $end);
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
$reps = $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();
$currentLeftInLimit = floatval($limit->amount);
/*
* For each limitrepetition Firefly creates a serie that contains the amount left in
* the limitrepetition for its entire date-range. Entries are only actually included when they
* fall into the charts date range.
*
* So example: the user has a session date from Jan 15 to Jan 30. The limitrepetition
* starts at 1 Jan until 1 Feb.
*
* Firefly loops from 1 Jan to 1 Feb but only includes Jan 15 / Jan 30.
* But it does keep count of the amount outside of these dates because otherwise the line might be wrong.
*/
/** @var \LimitRepetition $repetition */
foreach ($reps as $repetition) {
$limitAmount = $repetition->limit->amount;
// create a serie for the repetition.
$currentSerie = [
'type' => 'spline',
'id' => 'rep-' . $repetition->id,
'yAxis' => 1,
'name' => 'Envelope in ' . $repetition->periodShow(),
'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(),
'data' => []
];
$current = clone $repetition->startdate;
while ($current <= $repetition->enddate) {
if ($current >= Session::get('start') && $current <= Session::get('end')) {
if ($current >= $start && $current <= $end) {
// spent on limit:
$spentSoFar = \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', '>=', $repetition->startdate->format('Y-m-d')
)->where('transaction_journals.date', '<=', $current->format('Y-m-d'))->where(
'amount', '>', 0
)->sum('amount');
$spent = floatval($spent) == 0 ? null : floatval($spent);
$currentLeftInLimit = floatval($limit->amount) - floatval($spentSoFar);
$spentSoFar = $this->_chart->spentOnLimitRepetitionBetweenDates(
$repetition, $repetition->startdate, $current
);
$leftInLimit = floatval($limitAmount) - floatval($spentSoFar);
$currentSerie['data'][] = [$current->timestamp * 1000, $currentLeftInLimit];
$currentSerie['data'][] = [$current->timestamp * 1000, $leftInLimit];
}
$current->addDay();
}
// do something here.
$repetitionSeries[] = $currentSerie;
$series[] = $currentSerie;
}
}
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' =>
'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format(
'M jS, Y'
),
'series' => $repetitionSeries
'series' => $series
];
return Response::json($return);
@@ -371,7 +341,6 @@ class ChartController extends BaseController
];
foreach ($accounts as $account) {
\Log::debug('Now building series for ' . $account->name);
$data['series'][] = $this->_chart->account($account, $start, $end);
}

View File

@@ -1,5 +1,4 @@
<?php
use Carbon\Carbon;
use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
@@ -7,6 +6,8 @@ use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as
/**
* Class HomeController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class HomeController extends BaseController
{
@@ -15,6 +16,12 @@ class HomeController extends BaseController
protected $_journal;
protected $_reminders;
/**
* @param ARI $accounts
* @param PHI $preferences
* @param TJRI $journal
* @param RRI $reminders
*/
public function __construct(ARI $accounts, PHI $preferences, TJRI $journal, RRI $reminders)
{
$this->_accounts = $accounts;
@@ -38,13 +45,11 @@ class HomeController extends BaseController
*/
public function index()
{
Event::fire('limits.check');
Event::fire('piggybanks.check');
Event::fire('recurring.check');
\Event::fire('limits.check');
\Event::fire('piggybanks.check');
\Event::fire('recurring.check');
// count, maybe we need some introducing text to show:
// count, maybe Firefly needs some introducing text to show:
$count = $this->_accounts->count();
$start = Session::get('start');
$end = Session::get('end');
@@ -74,13 +79,7 @@ class HomeController extends BaseController
$transactions = array_chunk($transactions, 3);
}
// get the users reminders:
$reminders = $this->_reminders->getCurrentRecurringReminders();
// build the home screen:
return View::make('index')->with('count', $count)->with('transactions', $transactions)->with(
'reminders', $reminders
);
return View::make('index')->with('count', $count)->with('transactions', $transactions);
}
}

View File

@@ -7,6 +7,8 @@ use Firefly\Storage\Component\ComponentRepositoryInterface as CRI;
/**
* Class JsonController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class JsonController extends BaseController
{

View File

@@ -5,6 +5,8 @@ use Firefly\Storage\Limit\LimitRepositoryInterface as LRI;
/**
* Class LimitController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class LimitController extends BaseController
{
@@ -98,7 +100,7 @@ class LimitController extends BaseController
public function store(Budget $budget = null)
{
// find a limit with these properties, as we might already have one:
// find a limit with these properties, as Firefly might already have one:
$limit = $this->_limits->store(Input::all());
if ($limit->validate()) {
Session::flash('success', 'Envelope created!');

View File

@@ -0,0 +1,54 @@
<?php
/**
* Class MigrateController
*/
class MigrateController extends BaseController
{
/**
* @return $this
*/
public function index()
{
return View::make('migrate.index')->with('index', 'Migration');
}
/**
*
*/
public function upload()
{
if (Input::hasFile('file') && Input::file('file')->isValid()) {
// move file to storage:
// ->move($destinationPath, $fileName);
$path = storage_path();
$fileName = 'firefly-iii-import-' . date('Y-m-d-H-i') . '.json';
$fullName = $path . DIRECTORY_SEPARATOR . $fileName;
if (is_writable($path)) {
Input::file('file')->move($path, $fileName);
// so now Firefly pushes something in a queue and does something with it! Yay!
\Log::debug('Pushed a job to start the import.');
Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => \Auth::user()->id]);
if (Config::get('queue.default') == 'sync') {
Session::flash('success', 'Your data has been imported!');
} else {
Session::flash(
'success',
'The import job has been queued. Please be patient. Data will appear slowly. Please be patient.'
);
}
return Redirect::route('index');
}
Session::flash('error', 'Could not save file to storage.');
return Redirect::route('migrate.index');
} else {
Session::flash('error', 'Please upload a file.');
return Redirect::route('migrate.index');
}
}
}

View File

@@ -7,6 +7,10 @@ use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI;
/**
* Class PiggybankController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyMethods)
*
*/
class PiggybankController extends BaseController
{
@@ -26,6 +30,8 @@ class PiggybankController extends BaseController
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function addMoney(Piggybank $piggyBank)
{
@@ -115,7 +121,7 @@ class PiggybankController extends BaseController
$piggybanks = $this->_repository->get();
// get the accounts with each piggy bank and check their balance; we might need to
// get the accounts with each piggy bank and check their balance; Fireflyy might needs to
// show the user a correction.
$accounts = [];
@@ -175,6 +181,8 @@ class PiggybankController extends BaseController
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function removeMoney(Piggybank $piggyBank)
{
@@ -258,6 +266,8 @@ class PiggybankController extends BaseController
}
/**
* @param Piggybank $piggyBank
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(Piggybank $piggyBank)

View File

@@ -5,6 +5,8 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
/**
* Class PreferencesController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class PreferencesController extends BaseController
{

View File

@@ -4,6 +4,8 @@ use Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface
/**
* Class RecurringController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class RecurringController extends BaseController
{
@@ -114,6 +116,8 @@ class RecurringController extends BaseController
/**
* @param RecurringTransaction $recurringTransaction
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(RecurringTransaction $recurringTransaction)
{

View File

@@ -5,12 +5,17 @@ use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
/**
* Class ReminderController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class ReminderController extends BaseController
{
protected $_repository;
/**
* @param RRI $repository
*/
public function __construct(RRI $repository)
{
$this->_repository = $repository;
@@ -34,7 +39,7 @@ class ReminderController extends BaseController
public function modalDialog()
{
$today = new Carbon;
$reminders = $this->_repository->get();
$reminders = $this->_repository->getPiggybankReminders();
/** @var \Reminder $reminder */
foreach ($reminders as $index => $reminder) {
@@ -66,6 +71,8 @@ class ReminderController extends BaseController
/**
* @param Reminder $reminder
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function redirect(\Reminder $reminder)
{
@@ -83,6 +90,7 @@ class ReminderController extends BaseController
route('transactions.create', ['what' => 'transfer']) . '?' . http_build_query($parameters)
);
}
return View::make('error')->with('message', 'No such reminder.');
}

View File

@@ -6,6 +6,8 @@ use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as
/**
* Class TransactionController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class TransactionController extends BaseController
{
@@ -197,6 +199,7 @@ class TransactionController extends BaseController
}
// trigger the creation for recurring transactions.
Event::fire('journals.store', [$journal]);
if (Input::get('create') == '1') {
return Redirect::route('transactions.create', [$what])->withInput();
@@ -225,6 +228,7 @@ class TransactionController extends BaseController
if ($journal->validate()) {
// has been saved, return to index:
Session::flash('success', 'Transaction updated!');
Event::fire('journals.update', [$journal]);
return Redirect::route('transactions.index');
} else {

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateAccountTypesTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('account_types');
}
/**
* Run the migrations.
*
@@ -22,19 +32,12 @@ class CreateAccountTypesTable extends Migration
'account_types', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('description', 50);
$table->string('type', 50);
$table->boolean('editable');
$table->unique('type');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('account_types');
}
}

View File

@@ -25,8 +25,8 @@ class CreateRecurringTransactionsTable extends Migration
$table->integer('user_id')->unsigned();
$table->string('name', 50);
$table->string('match', 255);
$table->decimal('amount_max', 10, 2);
$table->decimal('amount_min', 10, 2);
$table->decimal('amount_max', 10, 2);
$table->date('date');
$table->boolean('active');

View File

@@ -24,6 +24,7 @@ class CreateTransactionJournalsTable extends Migration
$table->timestamps();
$table->integer('user_id')->unsigned();
$table->integer('transaction_type_id')->unsigned();
$table->integer('recurring_transaction_id')->unsigned()->nullable();
$table->integer('transaction_currency_id')->unsigned();
$table->string('description', 255)->nullable();
$table->boolean('completed');
@@ -34,6 +35,11 @@ class CreateTransactionJournalsTable extends Migration
->references('id')->on('transaction_types')
->onDelete('cascade');
// connect transaction journals to recurring transactions
$table->foreign('recurring_transaction_id')
->references('id')->on('recurring_transactions')
->onDelete('set null');
// connect transaction journals to transaction currencies
$table->foreign('transaction_currency_id')
->references('id')->on('transaction_currencies')

View File

@@ -32,7 +32,7 @@ class CreateRemindersTable extends Migration
$table->integer('recurring_transaction_id')->unsigned()->nullable();
$table->integer('user_id')->unsigned();
$table->date('startdate');
$table->date('enddate');
$table->date('enddate')->nullable();
$table->boolean('active');

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateImportmapsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('importmaps', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->integer('user_id')->unsigned();
$table->string('file',500);
// connect maps to users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('importmaps');
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateImportentriesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('importentries', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->string('class',200);
$table->integer('importmap_id')->unsigned();
$table->integer('old')->unsigned();
$table->integer('new')->unsigned();
// map import entries to import map.
// connect accounts to account_types
$table->foreign('importmap_id')
->references('id')->on('importmaps')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('importentries');
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateFailedJobsTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('failed_jobs');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->increments('id');
$table->text('connection');
$table->text('queue');
$table->text('payload');
$table->timestamp('failed_at');
});
}
}

View File

@@ -11,16 +11,20 @@ class AccountTypeSeeder extends Seeder
DB::table('account_types')->delete();
AccountType::create(
['description' => 'Default account']
['type' => 'Default account', 'editable' => true]
);
AccountType::create(
['description' => 'Cash account']
['type' => 'Cash account', 'editable' => false]
);
AccountType::create(
['description' => 'Initial balance account']
['type' => 'Initial balance account', 'editable' => false]
);
AccountType::create(
['description' => 'Beneficiary account']
['type' => 'Beneficiary account', 'editable' => true]
);
AccountType::create(
['type' => 'Import account', 'editable' => false]
);
}

View File

@@ -1,6 +1,6 @@
<?php
return array(
return [
/*
|--------------------------------------------------------------------------
@@ -87,6 +87,7 @@ return array(
'rule-name' => 'custom-message',
),
),
'alphabasic' => 'The :attribute field must consist of basic alphanumeric characters.',
/*
|--------------------------------------------------------------------------
@@ -101,4 +102,4 @@ return array(
'attributes' => array(),
);
];

View File

@@ -73,7 +73,7 @@ abstract class SingleTableInheritanceEntity extends Ardent
// newEloquentBuilder() was added in 4.1
$builder = $this->newEloquentBuilder($this->newBaseQueryBuilder());
// Once we have the query builders, we will set the model instances so the
// 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);

View File

@@ -2,8 +2,6 @@
namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection;
/**
* Class Account
*
@@ -11,166 +9,129 @@ use Illuminate\Database\Eloquent\Collection;
*/
class Account implements AccountInterface
{
/**
* @param Collection $accounts
*
* @return array|mixed
*/
public function index(Collection $accounts)
{
$list = [
'personal' => [],
'beneficiaries' => [],
'initial' => [],
'cash' => []
];
foreach ($accounts as $account) {
switch ($account->accounttype->description) {
case 'Default account':
$list['personal'][] = $account;
break;
case 'Cash account':
$list['cash'][] = $account;
break;
case 'Initial balance account':
$list['initial'][] = $account;
break;
case 'Beneficiary account':
$list['beneficiaries'][] = $account;
break;
}
}
return $list;
}
/**
* @param \Account $account
*
* @return mixed
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
$transactionType = \TransactionType::where('type', 'Opening balance')->first();
return \TransactionJournal::
with(
['transactions' => function ($q) {
$q->orderBy('amount', 'ASC');
}]
)->where('transaction_type_id', $transactionType->id)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)->first(['transaction_journals.*']);
return \TransactionJournal::withRelevantData()
->account($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 mixed|void
* @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 = [
'budgets' => [],
'categories' => [],
'accounts' => []
];
$items = [];
// build a query:
$query = \TransactionJournal::with(
['transactions' => function ($q) {
$q->orderBy('amount', 'ASC');
}, 'transactiontype', 'components' => function ($q) {
$q->orderBy('class');
}, 'transactions.account.accounttype']
)->orderBy('date', 'DESC')->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->where('transactions.account_id', $account->id)->where('date', '>=', $start->format('Y-m-d'))->where(
'date', '<=', $end->format('Y-m-d')
)->orderBy('transaction_journals.id', 'DESC');
$query = \TransactionJournal::withRelevantData()
->defaultSorting()
->account($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 = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1;
$page = max(1, intval(\Input::get('page')));
$skip = ($page - 1) * $perPage;
$result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']);
// in the mean time, build list of categories, budgets and other accounts:
// get the relevant budgets, categories and accounts from this list:
/** @var $item \TransactionJournal */
foreach ($result as $item) {
$items[] = $item;
foreach ($result as $index => $item) {
foreach ($item->components as $component) {
if ($component->class == 'Budget') {
$stats['budgets'][$component->id] = $component;
}
if ($component->class == 'Category') {
$stats['categories'][$component->id] = $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;
}
unset($result, $page);
$paginator = \Paginator::make($items, $totalItems, $perPage);
// statistics
$stats['period']['in'] = floatval(
\Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount')
);
unset($result, $page, $item, $fromAccount, $toAccount);
$stats['period']['out'] = floatval(
\Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount')
);
$stats['period']['diff'] = $stats['period']['in'] + $stats['period']['out'];
// statistics (transactions)
$trIn = floatval(\Transaction::before($end)->after($start)->account($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
$trOut = floatval(\Transaction::before($end)->after($start)->account($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
$trDiff = $trIn + $trOut;
$stats['period']['t_in'] = floatval(
\Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Transfer')->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount')
);
// statistics (transfers)
$trfIn = floatval(\Transaction::before($end)->after($start)->account($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount'));
$trfOut = floatval(\Transaction::before($end)->after($start)->account($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount'));
$trfDiff = $trfIn + $trfOut;
$stats['period']['t_out'] = floatval(
\Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Transfer')->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount')
);
$stats['period']['t_diff'] = $stats['period']['t_in'] + $stats['period']['t_out'];
$stats['period'] = [
'in' => $trIn,
'out' => $trOut,
'diff' => $trDiff,
't_in' => $trfIn,
't_out' => $trfOut,
't_diff' => $trfDiff
];
$return = [
'journals' => $paginator,
@@ -178,7 +139,5 @@ class Account implements AccountInterface
];
return $return;
}
}

View File

@@ -12,15 +12,6 @@ use Illuminate\Database\Eloquent\Collection;
interface AccountInterface
{
/**
* Build the index:
*
* @param Collection $accounts
*
* @return mixed
*/
public function index(Collection $accounts);
/**
* @param \Account $account
*

View File

@@ -13,6 +13,11 @@ 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
@@ -21,32 +26,30 @@ class Budget implements BudgetInterface
{
$return = [];
/** @var \Budget $budget */
foreach ($budgets as $budget) {
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $rep */
foreach ($limit->limitrepetitions as $rep) {
$periodOrder = $rep->periodOrder();
$period = $rep->periodShow();
$return[$periodOrder] = isset($return[$periodOrder])
? $return[$periodOrder]
: ['date' => $period,
'dateObject' => $rep->startdate,
'start' => $rep->startdate,
'end' => $rep->enddate,
'budget_id' => $limit->budget_id];
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
$repetition->left = $repetition->left();
$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;
}
}
}
// put all the budgets under their respective date:
foreach ($budgets as $budget) {
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $rep) {
$rep->left = $rep->left();
$month = $rep->periodOrder();
$return[$month]['limitrepetitions'][] = $rep;
}
}
}
@@ -56,30 +59,24 @@ class Budget implements BudgetInterface
}
/**
* Get a repetition (complex because of user check)
* and then get the transactions in it.
* @param $repetitionId
*
* @return array
*/
public function organizeRepetition($repetitionId)
public function organizeRepetition(\LimitRepetition $repetition)
{
$result = [];
$repetition = \LimitRepetition::with('limit', 'limit.budget')->leftJoin(
'limits', 'limit_repetitions.limit_id', '=', 'limits.id'
)->leftJoin('components', 'limits.component_id', '=', 'components.id')->where(
'components.user_id', \Auth::user()->id
)
->where('limit_repetitions.id', $repetitionId)->first(['limit_repetitions.*']);
// get transactions:
$set = $repetition->limit->budget->transactionjournals()->with(
'transactions', 'transactions.account', 'components', 'transactiontype'
)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Withdrawal')->where(
'date', '>=', $repetition->startdate->format('Y-m-d')
)->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->orderBy(
'id', 'DESC'
)->get(['transaction_journals.*']);
$set = $repetition->limit->budget
->transactionjournals()
->withRelevantData()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.*']);
$result[0] = [
'date' => $repetition->periodShow(),
@@ -93,6 +90,8 @@ class Budget implements BudgetInterface
}
/**
*
*
* @param \Budget $budget
* @param bool $useSessionDates
*
@@ -128,15 +127,13 @@ class Budget implements BudgetInterface
'paginated' => false
];
$transactions = [];
$set = $budget->transactionjournals()->with(
'transactions', 'transactions.account', 'components', 'transactiontype'
)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Withdrawal')->where(
'date', '>=', $repetition->startdate->format('Y-m-d')
)->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->orderBy(
'id', 'DESC'
)->get(['transaction_journals.*']);
$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;
@@ -146,22 +143,9 @@ class Budget implements BudgetInterface
}
if ($useSessionDates === false) {
$query = $budget->transactionjournals()->withRelevantData()->defaultSorting();
if (count($inRepetition) > 0) {
$query = $budget->transactionjournals()->with(
'transactions', 'transactions.account', 'components', 'transactiontype',
'transactions.account.accounttype'
)->whereNotIn(
'transaction_journals.id', $inRepetition
)->orderBy('date', 'DESC')->orderBy(
'transaction_journals.id', 'DESC'
);
} else {
$query = $budget->transactionjournals()->with(
'transactions', 'transactions.account', 'components', 'transactiontype',
'transactions.account.accounttype'
)->orderBy('date', 'DESC')->orderBy(
'transaction_journals.id', 'DESC'
);
$query->whereNotIn('transaction_journals.id', $inRepetition);
}
// build paginator:
@@ -178,7 +162,10 @@ class Budget implements BudgetInterface
$items[] = $item;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
$result['0000'] = ['date' => 'Not in an envelope', 'limit' => null, 'paginated' => true,
$result['0000'] = [
'date' => 'Not in an envelope',
'limit' => null,
'paginated' => true,
'journals' => $paginator];
}
krsort($result);
@@ -196,13 +183,12 @@ class Budget implements BudgetInterface
$inRepetitions = [];
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $repetition) {
$set = $budget->transactionjournals()->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Withdrawal')->where(
'date', '>=', $repetition->startdate->format('Y-m-d')
)->where('date', '<=', $repetition->enddate->format('Y-m-d'))->orderBy('date', 'DESC')->get(
['transaction_journals.id']
);
$set = $budget->transactionjournals()
->transactionTypes(['Withdrawal'])
->after($repetition->startdate)
->before($repetition->enddate)
->defaultSorting()
->get(['transaction_journals.id']);
foreach ($set as $item) {
$inRepetitions[] = $item->id;
}
@@ -210,14 +196,10 @@ class Budget implements BudgetInterface
}
$query = $budget->transactionjournals()->with(
'transactions', 'transactions.account', 'components', 'transactiontype',
'transactions.account.accounttype'
)->whereNotIn(
'transaction_journals.id', $inRepetitions
)->orderBy('date', 'DESC')->orderBy(
'transaction_journals.id', 'DESC'
);
$query = $budget->transactionjournals()
->withRelevantData()
->whereNotIn('transaction_journals.id', $inRepetitions)
->defaultSorting();
// build paginator:
$perPage = 25;
@@ -233,8 +215,12 @@ class Budget implements BudgetInterface
$items[] = $item;
}
$paginator = \Paginator::make($items, $totalItems, $perPage);
$result = [0 => ['date' => 'Not in an envelope', 'limit' => null, 'paginated' => true,
'journals' => $paginator]];
$result = [0 => [
'date' => 'Not in an envelope',
'limit' => null,
'paginated' => true,
'journals' => $paginator
]];
return $result;
}

View File

@@ -23,7 +23,7 @@ interface BudgetInterface
*
* @return mixed
*/
public function organizeRepetition($repetitionId);
public function organizeRepetition(\LimitRepetition $repetition);
/**

View File

@@ -4,6 +4,7 @@ namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Support\Collection;
/**
* Class Chart
@@ -24,13 +25,20 @@ class Chart implements ChartInterface
{
$current = clone $start;
$today = new Carbon;
$return = ['name' => $account->name, 'id' => $account->id, 'data' => []];
$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'][] = [$current->timestamp * 1000, $account->predict(clone $current)];
$return['data'][] = $account->predict(clone $current);
} else {
$return['data'][] = [$current->timestamp * 1000, $account->balance(clone $current)];
$return['data'][] = $account->balance(clone $current);
}
$current->addDay();
@@ -117,6 +125,7 @@ class Chart implements ChartInterface
$limitInPeriod = '';
$spentInPeriod = '';
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$budget->count = 0;
foreach ($budget->limits as $limit) {
@@ -144,6 +153,23 @@ class Chart implements ChartInterface
}
$budget->count += count($limit->limitrepetitions);
}
if ($budget->count == 0) {
// get expenses in period until today, starting at $start.
$end = \Session::get('end');
$expenses = $budget->transactionjournals()->after($start)->before($end)
->transactionTypes(
['Withdrawal']
)->get();
$budget->spentInPeriod = 0;
/** @var \TransactionJournal $expense */
foreach ($expenses as $expense) {
$transaction = $expense->transactions[1];
if (!is_null($transaction)) {
$budget->spentInPeriod += floatval($transaction->amount);
}
}
}
}
@@ -161,8 +187,7 @@ class Chart implements ChartInterface
foreach ($budgets as $budget) {
if ($budget->count > 0) {
$data['labels'][] = wordwrap($budget->name, 12, "<br>");
}
$data['labels'][] = $budget->name;
foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $rep) {
//0: envelope for period:
@@ -170,7 +195,17 @@ class Chart implements ChartInterface
$spent = $rep->spent;
$color = $spent > $amount ? '#FF0000' : null;
$data['series'][0]['data'][] = ['y' => $amount, 'id' => 'amount-' . $rep->id];
$data['series'][1]['data'][] = ['y' => $rep->spent, 'color' => $color, 'id' => 'spent-' . $rep->id];
$data['series'][1]['data'][] = ['y' => $rep->spent, 'color' => $color,
'id' => 'spent-' . $rep->id];
}
}
} else {
// add for "empty" budget:
if ($budget->spentInPeriod > 0) {
$data['labels'][] = $budget->name;
$data['series'][0]['data'][] = ['y' => null, 'id' => 'amount-norep-' . $budget->id];
$data['series'][1]['data'][] = ['y' => $budget->spentInPeriod,
'id' => 'spent-norep-' . $budget->id];
}
}
@@ -401,4 +436,153 @@ class Chart implements ChartInterface
}
/**
* @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

@@ -54,4 +54,62 @@ interface ChartInterface
* @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

@@ -38,7 +38,7 @@ class EmailHelper implements EmailHelperInterface
{
$password = \Str::random(12);
$user->password = \Hash::make($password);
$user->password = $password;
$user->reset = \Str::random(32); // new one.
$user->forceSave();
$email = $user->email;

View File

@@ -1,47 +0,0 @@
<?php
namespace Firefly\Helper\Form;
/**
* Class FormHelper
*
* @package Firefly\Form
*/
class FormHelper
{
/**
* @param null $value
*
* @return string
*/
public function budget($value = null)
{
$str = '<select name="budget_id" class="form-control">';
$str .= '<option value="0" label="(no budget)"';
if (is_null($value) || intval($value) == 0) {
$str .= ' selected="selected"';
}
$str .= '</option>';
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgets */
$budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$list = $budgets->getAsSelectList();
foreach ($list as $id => $name) {
$str .= '<option value="' . e($id) . '" label="' . e($name) . '"';
if ($id == intval($value)) {
$str .= ' selected="selected"';
}
$str .= '>' . e($name) . '</option>';
}
$str .= '</select>';
return $str;
}
}

View File

@@ -1,35 +0,0 @@
<?php
namespace Firefly\Helper\Form;
use Illuminate\Events\Dispatcher;
/**
* Class FormTrigger
*
* @package Firefly\Helper\Form
*/
class FormTrigger
{
public function registerFormExtensions()
{
\Form::macro(
'budget', function () {
$helper = new FormHelper;
return $helper->budget();
}
);
}
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen('laravel.booted', 'Firefly\Helper\Form\FormTrigger@registerFormExtensions');
}
}

View File

@@ -1,327 +0,0 @@
<?php
namespace Firefly\Helper\Migration;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
/**
* Class MigrationHelper
*
* @package Firefly\Helper\Migration
*/
class MigrationHelper implements MigrationHelperInterface
{
protected $path;
protected $JSON;
protected $map = [];
/**
* @param $path
*
* @return mixed|void
*/
public function loadFile($path)
{
$this->path = $path;
}
/**
* @return bool
*/
public function validFile()
{
// file does not exist:
if (!file_exists($this->path)) {
\Log::error('Migration file ' . $this->path . ' does not exist!');
return false;
}
// load the content:
$content = file_get_contents($this->path);
if ($content === false) {
return false;
}
// parse the content
$this->JSON = json_decode($content);
if (is_null($this->JSON)) {
return false;
}
\Log::info('Migration file ' . $this->path . ' is valid!');
return true;
}
/**
* @return bool
*/
public function migrate()
{
\Log::info('Start of migration.');
\DB::beginTransaction();
try {
// create cash account:
$this->_createCashAccount();
$this->_importAccounts();
$this->_importComponents();
//$this->_importPiggybanks();
// create transactions:
$this->_importTransactions();
// create transfers:
$this->_importTransfers();
// create limits:
$this->_importLimits();
} catch (FireflyException $e) {
\DB::rollBack();
\Log::error('Rollback because of error!');
\Log::error($e->getMessage());
return false;
}
\DB::commit();
\Log::info('Done!');
return true;
}
/**
*
*/
protected function _createCashAccount()
{
$cashAT = \AccountType::where('description', 'Cash account')->first();
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$cash = $accounts->store(['name' => 'Cash account', 'account_type' => $cashAT, 'active' => 0]);
\Log::info('Created cash account (#' . $cash->id . ')');
$this->map['cash'] = $cash;
}
/**
*
*/
protected function _importAccounts()
{
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
\Log::info('Going to import ' . count($this->JSON->accounts) . ' accounts.');
foreach ($this->JSON->accounts as $entry) {
// create account:
if ($entry->openingbalance == 0) {
$account = $accounts->store(['name' => $entry->name]);
} else {
$account = $accounts->storeWithInitialBalance(
['name' => $entry->name],
new Carbon($entry->openingbalancedate),
floatval($entry->openingbalance)
);
}
$this->map['accounts'][$entry->id] = $account;
\Log::info('Imported account "' . $entry->name . '" with balance ' . $entry->openingbalance);
}
}
/**
*
*/
protected function _importComponents()
{
$beneficiaryAT = \AccountType::where('description', 'Beneficiary account')->first();
foreach ($this->JSON->components as $entry) {
switch ($entry->type->type) {
case 'beneficiary':
/** @noinspection PhpParamsInspection */
$beneficiary = $this->_importBeneficiary($entry, $beneficiaryAT);
$this->map['accounts'][$entry->id] = $beneficiary;
break;
case 'category':
$component = $this->_importCategory($entry);
$this->map['categories'][$entry->id] = $component;
break;
case 'budget':
$component = $this->_importBudget($entry);
$this->map['budgets'][$entry->id] = $component;
break;
}
}
}
/**
* @param $component
* @param \AccountType $beneficiaryAT
*
* @return mixed
*/
protected function _importBeneficiary($component, \AccountType $beneficiaryAT)
{
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
return $accounts->store(
[
'name' => $component->name,
'account_type' => $beneficiaryAT
]
);
}
/**
* @param $component
*
* @return mixed
*/
protected function _importCategory($component)
{
/** @var \Firefly\Storage\Component\ComponentRepositoryInterface $components */
$components = \App::make('Firefly\Storage\Component\ComponentRepositoryInterface');
return $components->store(['name' => $component->name, 'class' => 'Category']);
}
/**
* @param $component
*
* @return mixed
*/
protected function _importBudget($component)
{
/** @var \Firefly\Storage\Component\ComponentRepositoryInterface $components */
$components = \App::make('Firefly\Storage\Component\ComponentRepositoryInterface');
return $components->store(['name' => $component->name, 'class' => 'Budget']);
}
/**
*
*/
protected function _importTransactions()
{
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
// loop component_transaction to find beneficiaries, categories and budgets:
$beneficiaries = [];
$categories = [];
$budgets = [];
foreach ($this->JSON->component_transaction as $entry) {
// beneficiaries
if (isset($this->map['accounts'][$entry->component_id])) {
$beneficiaries[$entry->transaction_id] = $this->map['accounts'][$entry->component_id];
}
// categories
if (isset($this->map['categories'][$entry->component_id])) {
$categories[$entry->transaction_id] = $this->map['categories'][$entry->component_id];
}
// budgets:
if (isset($this->map['budgets'][$entry->component_id])) {
$budgets[$entry->transaction_id] = $this->map['budgets'][$entry->component_id];
}
}
foreach ($this->JSON->transactions as $entry) {
// to properly save the amount, do it times -1:
$amount = $entry->amount * -1;
/** @var \Account $fromAccount */
$fromAccount = isset($this->map['accounts'][$entry->account_id])
? $this->map['accounts'][$entry->account_id] : false;
/** @var \Account $toAccount */
$toAccount = isset($beneficiaries[$entry->id]) ? $beneficiaries[$entry->id] : $this->map['cash'];
$date = new Carbon($entry->date);
$journal = $journals->createSimpleJournal($fromAccount, $toAccount, $entry->description, $amount, $date);
// save budgets and categories, on the journal
if (isset($budgets[$entry->id])) {
$budget = $budgets[$entry->id];
$journal->budgets()->save($budget);
}
if (isset($categories[$entry->id])) {
$category = $categories[$entry->id];
$journal->categories()->save($category);
}
}
}
/**
*
*/
protected function _importTransfers()
{
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */
$journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
foreach ($this->JSON->transfers as $entry) {
// to properly save the amount, do it times 1 (?):
$amount = $entry->amount * -1;
/** @var \Account $fromAccount */
$fromAccount = isset($this->map['accounts'][$entry->accountfrom_id])
? $this->map['accounts'][$entry->accountto_id] : false;
/** @var \Account $toAccount */
$toAccount = isset($this->map['accounts'][$entry->accountto_id])
? $this->map['accounts'][$entry->accountfrom_id] : false;
$date = new Carbon($entry->date);
$journals->createSimpleJournal($fromAccount, $toAccount, $entry->description, $amount, $date);
}
}
/**
*
*/
protected function _importLimits()
{
\Log::info('Importing limits');
foreach ($this->JSON->limits as $entry) {
\Log::debug(
'Now at #' . $entry->id . ': EUR ' . $entry->amount . ' for month ' . $entry->date
. ' and componentID: ' . $entry->component_id
);
$budget = isset($this->map['budgets'][$entry->component_id]) ? $this->map['budgets'][$entry->component_id]
: null;
if (!is_null($budget)) {
\Log::debug('Found budget for this limit: #' . $budget->id . ', ' . $budget->name);
$limit = new \Limit;
$limit->budget()->associate($budget);
$limit->startdate = new Carbon($entry->date);
$limit->amount = floatval($entry->amount);
$limit->repeats = 0;
$limit->repeat_freq = 'monthly';
try {
$limit->save();
} catch (\Exception $e) {
}
} else {
\Log::warning('No budget for this limit!');
}
// create repeat thing should not be necessary.
}
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace Firefly\Helper\Migration;
/**
* Interface MigrationHelperInterface
*
* @package Firefly\Helper\Migration
*/
interface MigrationHelperInterface
{
/**
* @param $path
*
* @return mixed
*/
public function loadFile($path);
/**
* @return mixed
*/
public function validFile();
/**
* @return mixed
*/
public function migrate();
}

View File

@@ -0,0 +1,674 @@
<?php
namespace Firefly\Queue;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
/**
* Class Import
*
* @package Firefly\Queue
*/
class Import
{
/** @var \Firefly\Storage\Account\AccountRepositoryInterface */
protected $_accounts;
/** @var \Firefly\Storage\Import\ImportRepositoryInterface */
protected $_repository;
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface */
protected $_budgets;
/** @var \Firefly\Storage\Category\CategoryRepositoryInterface */
protected $_categories;
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface */
protected $_journals;
/** @var \Firefly\Storage\Limit\LimitRepositoryInterface */
protected $_limits;
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface */
protected $_piggybanks;
/** @var \Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface */
protected $_recurring;
/**
*
*/
public function __construct()
{
$this->_accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
$this->_budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$this->_categories = \App::make('Firefly\Storage\Category\CategoryRepositoryInterface');
$this->_journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$this->_limits = \App::make('Firefly\Storage\Limit\LimitRepositoryInterface');
$this->_piggybanks = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
$this->_recurring = \App::make('Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface');
}
/**
* @param Job $job
* @param array $payload
*/
public function cleanImportAccount(Job $job, array $payload)
{
$importAccountType = $this->_accounts->findAccountType('Import account');
$importAccounts = $this->_accounts->getByAccountType($importAccountType);
if (count($importAccounts) == 0) {
$job->delete();
} else if (count($importAccounts) == 1) {
/** @var \Account $importAccount */
$importAccount = $importAccounts[0];
$transactions = $importAccount->transactions()->get();
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
$transaction->account()->associate($importAccount);
$transaction->save();
}
\Log::debug('Updated ' . count($transactions) . ' transactions from Import Account to cash.');
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*
* @throws \Firefly\Exception\FireflyException
*/
public function importComponent(Job $job, array $payload)
{
\Log::debug('Going to import component "' . $payload['data']['name'] . '".');
switch ($payload['data']['type']['type']) {
case 'beneficiary':
$payload['class'] = 'Account';
$payload['data']['account_type'] = 'Beneficiary account';
$this->importAccount($job, $payload);
break;
case 'budget':
$this->importBudget($job, $payload);
break;
case 'category':
$this->importCategory($job, $payload);
break;
case 'payer':
$job->delete();
break;
default:
$job->delete();
break;
}
}
/**
* Import a personal account or beneficiary as a new account.
*
* @param Job $job
* @param array $payload
*/
public function importAccount(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// maybe we've already imported this account:
$importEntry = $this->_repository->findImportEntry($importMap, 'Account', intval($payload['data']['id']));
// if so, delete job and return:
if (!is_null($importEntry)) {
$job->delete();
return;
}
// if Firefly tries to import a beneficiary, Firefly will "merge" already existing ones,
// so we don't care:
if (isset($payload['data']['account_type']) && $payload['data']['account_type'] == 'Beneficiary account') {
// store beneficiary
$acct = $this->_accounts->createOrFindBeneficiary($payload['data']['name']);
\Log::debug('Imported ' . $payload['class'] . ' "' . $payload['data']['name'] . '".');
$this->_repository->store($importMap, 'Account', $payload['data']['id'], $acct->id);
$job->delete();
return;
}
// but we cannot merge accounts, so we need to search first:
$acct = $this->_accounts->findByName($payload['data']['name']);
if (is_null($acct)) {
// store new one!
$acct = $this->_accounts->store((array)$payload['data']);
\Log::debug('Imported ' . $payload['class'] . ' "' . $payload['data']['name'] . '".');
$this->_repository->store($importMap, 'Account', $payload['data']['id'], $acct->id);
} else {
// use previous one!
\Log::debug('Already imported ' . $payload['class'] . ' "' . $payload['data']['name'] . '".');
$this->_repository->store($importMap, 'Account', $payload['data']['id'], $acct->id);
}
// and delete the job
$job->delete();
}
/**
* @param \User $user
*/
protected function overruleUser(\User $user)
{
$this->_accounts->overruleUser($user);
$this->_budgets->overruleUser($user);
$this->_categories->overruleUser($user);
$this->_journals->overruleUser($user);
$this->_limits->overruleUser($user);
$this->_repository->overruleUser($user);
$this->_piggybanks->overruleUser($user);
$this->_recurring->overruleUser($user);
}
/**
* Import a budget into Firefly.
*
* @param Job $job
* @param array $payload
*/
public function importBudget(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// maybe we've already imported this budget:
$bdg = $this->_budgets->findByName($payload['data']['name']);
if (is_null($bdg)) {
// we have not!
$bdg = $this->_budgets->store((array)$payload['data']);
$this->_repository->store($importMap, 'Budget', $payload['data']['id'], $bdg->id);
\Log::debug('Imported budget "' . $payload['data']['name'] . '".');
} else {
// we have!
$this->_repository->store($importMap, 'Budget', $payload['data']['id'], $bdg->id);
\Log::debug('Already had budget "' . $payload['data']['name'] . '".');
}
// delete job.
$job->delete();
}
/**
* Import a category into Firefly.
*
* @param Job $job
* @param array $payload
*/
public function importCategory(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// try to find budget:
$current = $this->_categories->findByName($payload['data']['name']);
if (is_null($current)) {
$cat = $this->_categories->store((array)$payload['data']);
$this->_repository->store($importMap, 'Category', $payload['data']['id'], $cat->id);
\Log::debug('Imported category "' . $payload['data']['name'] . '".');
} else {
$this->_repository->store($importMap, 'Category', $payload['data']['id'], $current->id);
\Log::debug('Already had category "' . $payload['data']['name'] . '".');
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importComponentTransaction(Job $job, array $payload)
{
if ($job->attempts() > 1) {
\Log::info('importComponentTransaction Job running for ' . $job->attempts() . 'th time!');
}
if ($job->attempts() > 30) {
\Log::error('importComponentTransaction Job running for ' . $job->attempts() . 'th time, so KILL!');
$job->delete();
return;
}
$oldComponentId = intval($payload['data']['component_id']);
$oldTransactionId = intval($payload['data']['transaction_id']);
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
$oldTransactionMap = $this->_repository->findImportEntry($importMap, 'Transaction', $oldTransactionId);
// we don't know what the component is, so we need to search for it in a set
// of possible types (Account / Beneficiary, Budget, Category)
/** @var \Importentry $oldComponentMap */
$oldComponentMap = $this->_repository->findImportComponentMap($importMap, $oldComponentId);
if (is_null($oldComponentMap)) {
\Log::debug('importComponentTransaction Could not run this one, waiting for five minutes...');
$job->release(300);
return;
}
$journal = $this->_journals->find($oldTransactionMap->new);
\Log::debug('Going to update ' . $journal->description);
// find the cash account:
switch ($oldComponentMap->class) {
case 'Budget':
// budget thing link:
$budget = $this->_budgets->find($oldComponentMap->new);
\Log::debug('Updating transactions budget.');
$journal->budgets()->save($budget);
$journal->save();
\Log::debug('Updated transactions budget.');
break;
case 'Category':
$category = $this->_categories->find($oldComponentMap->new);
$journal = $this->_journals->find($oldTransactionMap->new);
\Log::info('Updating transactions category (old id is #' . $oldComponentMap->old . ').');
if (!is_null($category)) {
$journal->categories()->save($category);
$journal->save();
\Log::info('Updated transactions category.');
} else {
\Log::error('No category mapping to old id #' . $oldComponentMap->old . ' found. Release for 5m!');
$job->release(300);
return;
}
break;
case 'Account':
\Log::info('Updating transactions Account.');
$account = $this->_accounts->find($oldComponentMap->new);
$journal = $this->_journals->find($oldTransactionMap->new);
if (is_null($account)) {
\Log::debug('Cash account is needed.');
$account = $this->_accounts->getCashAccount();
\Log::info($account);
}
foreach ($journal->transactions as $transaction) {
if ($transaction->account()->first()->account_type_id == 5) {
$transaction->account()->associate($account);
$transaction->save();
\Log::debug(
'Updated transactions (#' . $journal->id . '), #' . $transaction->id . '\'s Account.'
);
}
}
break;
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importLimit(Job $job, array $payload)
{
if ($job->attempts() > 30) {
\Log::error('importLimit Job running for ' . $job->attempts() . 'th time, so KILL!');
$job->delete();
return;
}
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// find the budget this limit is part of:
$importEntry = $this->_repository->findImportEntry(
$importMap, 'Budget',
intval($payload['data']['component_id'])
);
// budget is not yet imported:
if (is_null($importEntry)) {
\Log::debug(
'importLimit Cannot import limit #' . $payload['data']['id'] .
' because the budget is not here yet. #' . $job->attempts()
);
$job->release(300);
return;
}
// find similar limit:
\Log::debug('Trying to find budget with ID #' . $importEntry->new . ', based on entry #' . $importEntry->id);
$budget = $this->_budgets->find($importEntry->new);
if (!is_null($budget)) {
$current = $this->_limits->findByBudgetAndDate($budget, new Carbon($payload['data']['date']));
if (is_null($current)) {
// create it!
$payload['data']['budget_id'] = $budget->id;
$payload['data']['startdate'] = $payload['data']['date'];
$payload['data']['period'] = 'monthly';
$lim = $this->_limits->store((array)$payload['data']);
$this->_repository->store($importMap, 'Limit', $payload['data']['id'], $lim->id);
\Event::fire('limits.store', [$lim]);
\Log::debug('Imported ' . $payload['class'] . ', for ' . $budget->name . ' (' . $lim->startdate . ').');
} else {
// already has!
$this->_repository->store($importMap, 'Budget', $payload['data']['id'], $current->id);
\Log::debug(
'Already had ' . $payload['class'] . ', for ' . $budget->name . ' (' . $current->startdate . ').'
);
}
} else {
// cannot import component limit, no longer supported.
\Log::error('Cannot import limit for other than budget!');
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importPiggybank(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// try to find related piggybank:
$current = $this->_piggybanks->findByName($payload['data']['name']);
// we need an account to go with this piggy bank:
$set = $this->_accounts->getActiveDefault();
if (count($set) > 0) {
$account = $set[0];
$payload['data']['account_id'] = $account->id;
} else {
\Log::debug('Released job for work in five minutes...');
$job->release(300);
return;
}
if (is_null($current)) {
$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;
$piggy = $this->_piggybanks->store((array)$payload['data']);
$this->_repository->store($importMap, 'Piggybank', $payload['data']['id'], $piggy->id);
\Log::debug('Imported ' . $payload['class'] . ' "' . $payload['data']['name'] . '".');
\Event::fire('piggybanks.store', [$piggy]);
} else {
$this->_repository->store($importMap, 'Piggybank', $payload['data']['id'], $current->id);
\Log::debug('Already had ' . $payload['class'] . ' "' . $payload['data']['name'] . '".');
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importPredictable(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// try to find related recurring transaction:
$current = $this->_recurring->findByName($payload['data']['description']);
if (is_null($current)) {
$payload['data']['name'] = $payload['data']['description'];
$payload['data']['match'] = join(',', explode(' ', $payload['data']['description']));
$pct = intval($payload['data']['pct']);
$payload['data']['amount_min'] = floatval($payload['data']['amount']) * ($pct / 100) * -1;
$payload['data']['amount_max'] = floatval($payload['data']['amount']) * (1 + ($pct / 100)) * -1;
$payload['data']['date'] = date('Y-m-') . $payload['data']['dom'];
$payload['data']['repeat_freq'] = 'monthly';
$payload['data']['active'] = intval($payload['data']['inactive']) == 1 ? 0 : 1;
$payload['data']['automatch'] = 1;
$recur = $this->_recurring->store((array)$payload['data']);
$this->_repository->store($importMap, 'RecurringTransaction', $payload['data']['id'], $recur->id);
\Log::debug('Imported ' . $payload['class'] . ' "' . $payload['data']['name'] . '".');
} else {
$this->_repository->store($importMap, 'RecurringTransaction', $payload['data']['id'], $current->id);
\Log::debug('Already had ' . $payload['class'] . ' "' . $payload['data']['description'] . '".');
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importSetting(Job $job, array $payload)
{
switch ($payload['data']['name']) {
default:
$job->delete();
return;
break;
case 'piggyAccount':
// if we have this account, update all piggy banks:
$accountID = intval($payload['data']['value']);
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
$importEntry = $this->_repository->findImportEntry($importMap, 'Account', $accountID);
if ($importEntry) {
$all = $this->_piggybanks->get();
$account = $this->_accounts->find($importEntry->new);
\Log::debug('Updating all piggybanks, found the right setting.');
foreach ($all as $piggy) {
$piggy->account()->associate($account);
unset($piggy->leftInAccount); //??
$piggy->save();
}
} else {
\Log::debug('importSetting wait five minutes and try again...');
$job->release(300);
}
break;
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importTransaction(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// find or create the account type for the import account.
// find or create the account for the import account.
$accountType = $this->_accounts->findAccountType('Import account');
$importAccount = $this->_accounts->createOrFind('Import account', $accountType);
// if amount is more than zero, move from $importAccount
$amount = floatval($payload['data']['amount']);
$accountEntry = $this->_repository->findImportEntry(
$importMap, 'Account',
intval($payload['data']['account_id'])
);
$personalAccount = $this->_accounts->find($accountEntry->new);
if ($amount < 0) {
// if amount is less than zero, move to $importAccount
$accountFrom = $personalAccount;
$accountTo = $importAccount;
} else {
$accountFrom = $importAccount;
$accountTo = $personalAccount;
}
$amount = $amount < 0 ? $amount * -1 : $amount;
$date = new Carbon($payload['data']['date']);
// find a journal?
$current = $this->_repository->findImportEntry($importMap, 'Transaction', intval($payload['data']['id']));
if (is_null($current)) {
$journal = $this->_journals->createSimpleJournal(
$accountFrom, $accountTo,
$payload['data']['description'], $amount, $date
);
$this->_repository->store($importMap, 'Transaction', $payload['data']['id'], $journal->id);
\Log::debug(
'Imported transaction "' . $payload['data']['description'] . '" (' . $journal->date->format('Y-m-d')
. ').'
);
} else {
// do nothing.
\Log::debug('ALREADY imported transaction "' . $payload['data']['description'] . '".');
}
$job->delete();
}
/**
* @param Job $job
* @param array $payload
*/
public function importTransfer(Job $job, array $payload)
{
/** @var \Importmap $importMap */
$importMap = $this->_repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
// from account:
$oldFromAccountID = intval($payload['data']['accountfrom_id']);
$oldFromAccountEntry = $this->_repository->findImportEntry($importMap, 'Account', $oldFromAccountID);
$accountFrom = $this->_accounts->find($oldFromAccountEntry->new);
// to account:
$oldToAccountID = intval($payload['data']['accountto_id']);
$oldToAccountEntry = $this->_repository->findImportEntry($importMap, 'Account', $oldToAccountID);
$accountTo = $this->_accounts->find($oldToAccountEntry->new);
if (!is_null($accountFrom) && !is_null($accountTo)) {
$amount = floatval($payload['data']['amount']);
$date = new Carbon($payload['data']['date']);
$journal = $this->_journals->createSimpleJournal(
$accountFrom, $accountTo, $payload['data']['description'],
$amount, $date
);
\Log::debug('Imported transfer "' . $payload['data']['description'] . '".');
$job->delete();
} else {
$job->release(5);
}
}
/**
* @param Job $job
* @param $payload
*/
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)) {
// we are able to process the file!
// make an import map. Which is some kind of object because we use queues.
$importMap = new \Importmap;
$importMap->user()->associate($user);
$importMap->file = $filename;
$importMap->save();
// we can now launch a billion jobs importing every little thing into Firefly III
$raw = file_get_contents($filename);
$JSON = json_decode($raw);
$classes = ['accounts', 'components', 'limits', 'piggybanks',
'predictables', 'settings', 'transactions', 'transfers'];
foreach ($classes as $classes_plural) {
$class = ucfirst(\Str::singular($classes_plural));
\Log::debug('Create job to import all ' . $classes_plural);
foreach ($JSON->$classes_plural as $entry) {
\Log::debug('Create job to import single ' . $class);
$fn = 'import' . $class;
$jobFunction = 'Firefly\Queue\Import@' . $fn;
\Queue::push($jobFunction, ['data' => $entry, 'class' => $class, 'mapID' => $importMap->id]);
}
}
// , components, limits, piggybanks, predictables, settings, transactions, transfers
// component_predictables, component_transactions, component_transfers
$count = count($JSON->component_transaction);
foreach ($JSON->component_transaction as $index => $entry) {
\Log::debug('Create job to import components_transaction! Yay! (' . $index . '/' . $count . ') ');
$fn = 'importComponentTransaction';
$jobFunction = 'Firefly\Queue\Import@' . $fn;
\Queue::push($jobFunction, ['data' => $entry, 'mapID' => $importMap->id]);
}
// queue a job to clean up the "import account", it should properly fall back
// to the cash account (which it doesn't always do for some reason).
\Queue::push('Firefly\Queue\Import@cleanImportAccount', ['mapID' => $importMap->id]);
}
\Log::debug('Done with job "start"');
// this is it, close the job:
$job->delete();
}
}

View File

@@ -46,11 +46,23 @@ interface AccountRepositoryInterface
public function find($accountId);
/**
* @param $name
*
* @param $type
* @return mixed
*/
public function findByName($name);
public function findAccountType($type);
/**
* @param $name
* @param \AccountType $type
* @return mixed
*/
public function findByName($name, \AccountType $type = null);
/**
* @param $name
* @return mixed
*/
public function findByNameAny($name);
/**
* @return mixed
@@ -89,6 +101,18 @@ interface AccountRepositoryInterface
*/
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
*

View File

@@ -12,11 +12,15 @@ use Carbon\Carbon;
*/
class EloquentAccountRepository implements AccountRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
@@ -24,7 +28,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function count()
{
return \Auth::user()->accounts()->count();
return $this->_user->accounts()->count();
}
@@ -36,7 +40,6 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function createOrFind($name, \AccountType $type = null)
{
$account = $this->findByName($name, $type);
if (!$account) {
$data = [
@@ -60,10 +63,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
if (is_null($name) || strlen($name) == 0) {
return null;
}
$type = \AccountType::where('description', 'Beneficiary account')->first();
/** @noinspection PhpParamsInspection */
$type = \AccountType::where('type', 'Beneficiary account')->first();
return $this->createOrFind($name, $type);
}
@@ -74,38 +74,29 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function destroy(\Account $account)
{
// find the oldest transaction which also is a "Opening balance"
$first = \Transaction::
leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_journals.user_id', \Auth::user()->id)
->where('transaction_types.type', 'Opening balance')
->where('account_id', '!=', $account->id)
->orderBy('transactions.id', 'DESC')->first(['transactions.*']);
// find all transaction journals related to this account:
$journals = \TransactionJournal::withRelevantData()->account($account)->get(['transaction_journals.*']);
$accountIDs = [];
$initialbalanceAccount = null;
if (!is_null($first)) {
$initialbalanceAccount = $first->account()->first();
}
// loop the account, find all transaction journals, and delete them:
$transactions = $account->transactions()->with('transactionjournal')->get();
$journals = [];
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
$journals[$transaction->transaction_journal_id] = $transaction->transactionJournal;
}
/** @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();
}
if (!is_null($initialbalanceAccount)) {
$initialbalanceAccount->delete();
}
$account->delete();
/**
@@ -124,7 +115,16 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function find($accountId)
{
return \Auth::user()->accounts()->where('id', $accountId)->first();
return $this->_user->accounts()->where('id', $accountId)->first();
}
/**
* @param $type
* @return mixed
*/
public function findAccountType($type)
{
return \AccountType::where('type', $type)->first();
}
/**
@@ -135,9 +135,24 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function findByName($name, \AccountType $type = null)
{
$type = is_null($type) ? \AccountType::where('description', 'Default account')->first() : $type;
$type = is_null($type) ? \AccountType::where('type', 'Default account')->first() : $type;
return \Auth::user()->accounts()->where('account_type_id', $type->id)->where('name', 'like', '%' . $name . '%')
return $this->_user->accounts()->where('account_type_id', $type->id)
->where('name', 'like', '%' . $name . '%')
->first();
}
/**
* Used for import
*
* @param $name
*
* @return mixed
*/
public function findByNameAny($name)
{
return $this->_user->accounts()
->where('name', 'like', '%' . $name . '%')
->first();
}
@@ -146,7 +161,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function get()
{
return \Auth::user()->accounts()->with('accounttype')->orderBy('name', 'ASC')->get();
return $this->_user->accounts()->with('accounttype')->orderBy('name', 'ASC')->get();
}
/**
@@ -154,8 +169,8 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function getActiveDefault()
{
return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.description', 'Default account')->where('accounts.active', 1)
return $this->_user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.type', 'Default account')->where('accounts.active', 1)
->get(['accounts.*']);
}
@@ -165,10 +180,10 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function getActiveDefaultAsSelectList()
{
$list = \Auth::user()->accounts()->leftJoin(
$list = $this->_user->accounts()->leftJoin(
'account_types', 'account_types.id', '=', 'accounts.account_type_id'
)
->where('account_types.description', 'Default account')->where('accounts.active', 1)
->where('account_types.type', 'Default account')->where('accounts.active', 1)
->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$return = [];
@@ -184,16 +199,22 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function getBeneficiaries()
{
$list = \Auth::user()->accounts()->leftJoin(
$list = $this->_user->accounts()->leftJoin(
'account_types', 'account_types.id', '=', 'accounts.account_type_id'
)
->where('account_types.description', 'Beneficiary account')->where('accounts.active', 1)
->where('account_types.type', 'Beneficiary account')->where('accounts.active', 1)
->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
return $list;
}
public function getByAccountType(\AccountType $type)
{
return $this->_user->accounts()->with('accounttype')->orderBy('name', 'ASC')
->where('account_type_id', $type->id)->get();
}
/**
* @param $ids
*
@@ -202,7 +223,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
public function getByIds(array $ids)
{
if (count($ids) > 0) {
return \Auth::user()->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name', 'ASC')->get();
return $this->_user->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name', 'ASC')->get();
} else {
return $this->getActiveDefault();
}
@@ -213,8 +234,16 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function getCashAccount()
{
$type = \AccountType::where('description', 'Cash account')->first();
$cash = \Auth::user()->accounts()->where('account_type_id', $type->id)->first();
$type = \AccountType::where('type', 'Cash account')->first();
$cash = $this->_user->accounts()->where('account_type_id', $type->id)->first();
if (is_null($cash)) {
$cash = new \Account;
$cash->accountType()->associate($type);
$cash->user()->associate($this->_user);
$cash->name = 'Cash account';
$cash->active = 1;
$cash->save();
}
return $cash;
@@ -225,12 +254,22 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function getDefault()
{
return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.description', 'Default account')
return $this->_user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.type', 'Default account')
->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $data
*
@@ -239,13 +278,27 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/
public function store($data)
{
$defaultAccountType = \AccountType::where('description', 'Default account')->first();
$accountType = isset($data['account_type']) ? $data['account_type'] : $defaultAccountType;
/**
* 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'])) {
$accountType = \AccountType::where('type', $data['account_type'])->first();
// create Account:
} else {
$accountType = \AccountType::where('type', 'Default account')->first();
}
/**
* Create new account:
*/
$account = new \Account;
$account->accountType()->associate($accountType);
$account->user()->associate(\Auth::user());
$account->user()->associate($this->_user);
$account->name = $data['name'];
$account->active
= isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval(
@@ -258,9 +311,11 @@ class EloquentAccountRepository implements AccountRepositoryInterface
if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) {
$amount = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
if ($amount != 0) {
$this->_createInitialBalance($account, $amount, $date);
}
}
}
// whatever the result, return the account.
@@ -286,7 +341,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
/** @var \Firefly\Helper\Controllers\AccountInterface $interface */
$interface = \App::make('Firefly\Helper\Controllers\AccountInterface');
if ($account->accounttype->description == 'Default account') {
if ($account->accounttype->type == 'Default account') {
$journal = $interface->openingBalanceTransaction($account);
@@ -315,12 +370,12 @@ class EloquentAccountRepository implements AccountRepositoryInterface
protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date)
{
// get account type:
$initialBalanceAT = \AccountType::where('description', 'Initial balance account')->first();
$initialBalanceAT = \AccountType::where('type', 'Initial balance account')->first();
// create new account:
$initial = new \Account;
$initial->accountType()->associate($initialBalanceAT);
$initial->user()->associate(\Auth::user());
$initial->user()->associate($this->_user);
$initial->name = $account->name . ' initial balance';
$initial->active = 0;
if ($initial->validate()) {
@@ -330,6 +385,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
$transactionJournal = \App::make(
'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'
);
$transactionJournal->overruleUser($this->_user);
$transactionJournal->createSimpleJournal(
$initial, $account, 'Initial Balance for ' . $account->name, $amount, $date

View File

@@ -23,6 +23,19 @@ interface BudgetRepositoryInterface
*/
public function find($budgetId);
/**
* @param $budgetName
* @return mixed
*/
public function findByName($budgetName);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
/**
* @return mixed
*/

View File

@@ -3,19 +3,32 @@
namespace Firefly\Storage\Budget;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
/**
* Class EloquentBudgetRepository
*
* @package Firefly\Storage\Budget
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*
*/
class EloquentBudgetRepository implements BudgetRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param \Budget $budget
*
* @return bool|mixed
* @return bool
*/
public function destroy(\Budget $budget)
{
@@ -27,20 +40,30 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface
/**
* @param $budgetId
*
* @return mixed
* @return \Budget|null
*/
public function find($budgetId)
{
return \Auth::user()->budgets()->find($budgetId);
return $this->_user->budgets()->find($budgetId);
}
/**
* @return mixed
* @param $budgetName
* @return \Budget|null
*/
public function findByName($budgetName)
{
return $this->_user->budgets()->whereName($budgetName)->first();
}
/**
* @return Collection
*/
public function get()
{
$set = \Auth::user()->budgets()->with(
$set = $this->_user->budgets()->with(
['limits' => function ($q) {
$q->orderBy('limits.startdate', 'DESC');
}, 'limits.limitrepetitions' => function ($q) {
@@ -59,11 +82,11 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface
}
/**
* @return array|mixed
* @return array
*/
public function getAsSelectList()
{
$list = \Auth::user()->budgets()->with(
$list = $this->_user->budgets()->with(
['limits', 'limits.limitrepetitions']
)->orderBy('name', 'ASC')->get();
$return = [];
@@ -74,56 +97,45 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface
return $return;
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $data
*
* @return \Budget|mixed
* @return \Budget
*/
public function store($data)
{
$budget = new \Budget;
$budget->name = $data['name'];
$budget->user()->associate(\Auth::user());
$budget->user()->associate($this->_user);
$budget->save();
// if limit, create limit (repetition itself will be picked up elsewhere).
if (floatval($data['amount']) > 0) {
$limit = new \Limit;
$limit->budget()->associate($budget);
if (isset($data['amount']) && floatval($data['amount']) > 0) {
$startDate = new Carbon;
switch ($data['repeat_freq']) {
case 'daily':
$startDate->startOfDay();
break;
case 'weekly':
$startDate->startOfWeek();
break;
case 'monthly':
$startDate->startOfMonth();
break;
case 'quarterly':
$startDate->firstOfQuarter();
break;
case 'half-year':
$startDate->startOfYear();
if (intval($startDate->format('m')) >= 7) {
$startDate->addMonths(6);
}
break;
case 'yearly':
$startDate->startOfYear();
break;
}
$limit->startdate = $startDate;
$limit->amount = $data['amount'];
$limit->repeats = isset($data['repeats']) ? $data['repeats'] : 0;
$limit->repeat_freq = $data['repeat_freq'];
if ($limit->validate()) {
$limit->save();
$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();
}

View File

@@ -29,6 +29,12 @@ interface CategoryRepositoryInterface
*/
public function createOrFind($name);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
/**
* @param $name
*

View File

@@ -9,6 +9,16 @@ namespace Firefly\Storage\Category;
*/
class EloquentCategoryRepository implements CategoryRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param $name
*
@@ -48,7 +58,7 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface
*/
public function find($categoryId)
{
return \Auth::user()->categories()->find($categoryId);
return $this->_user->categories()->find($categoryId);
}
/**
@@ -62,7 +72,7 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface
return null;
}
return \Auth::user()->categories()->where('name', 'LIKE', '%' . $name . '%')->first();
return $this->_user->categories()->where('name', 'LIKE', '%' . $name . '%')->first();
}
@@ -71,7 +81,7 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface
*/
public function get()
{
return \Auth::user()->categories()->orderBy('name', 'ASC')->get();
return $this->_user->categories()->orderBy('name', 'ASC')->get();
}
/**
@@ -84,7 +94,7 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface
$category = new \Category;
$category->name = $data['name'];
$category->user()->associate(\Auth::user());
$category->user()->associate($this->_user);
$category->save();
return $category;
@@ -106,4 +116,14 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface
return $category;
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
}

View File

@@ -28,4 +28,10 @@ interface ComponentRepositoryInterface
*/
public function store($data);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -14,12 +14,14 @@ use Illuminate\Database\QueryException;
class EloquentComponentRepository implements ComponentRepositoryInterface
{
public $validator;
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
@@ -27,7 +29,7 @@ class EloquentComponentRepository implements ComponentRepositoryInterface
*/
public function count()
{
return \Auth::user()->components()->count();
return $this->_user->components()->count();
}
@@ -40,6 +42,16 @@ class EloquentComponentRepository implements ComponentRepositoryInterface
throw new FireflyException('No implementation.');
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $data
*
@@ -62,7 +74,7 @@ class EloquentComponentRepository implements ComponentRepositoryInterface
}
$component->name = $data['name'];
$component->user()->associate(\Auth::user());
$component->user()->associate($this->_user);
try {
$component->save();
} catch (QueryException $e) {

View File

@@ -0,0 +1,58 @@
<?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

@@ -0,0 +1,33 @@
<?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);
public function findImportMap($id);
public function findImportEntry(\Importmap $map, $class, $oldID);
public function findImportComponentMap(\Importmap $map, $oldComponentId);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -12,7 +12,15 @@ use Carbon\Carbon;
*/
class EloquentLimitRepository implements LimitRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param \Limit $limit
@@ -26,24 +34,6 @@ class EloquentLimitRepository implements LimitRepositoryInterface
return true;
}
/**
* @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;
}
/**
* @param $limitId
*
@@ -54,7 +44,12 @@ class EloquentLimitRepository implements LimitRepositoryInterface
return \Limit::with('limitrepetitions')->where('limits.id', $limitId)->leftJoin(
'components', 'components.id', '=', 'limits.component_id'
)
->where('components.user_id', \Auth::user()->id)->first(['limits.*']);
->where('components.user_id', $this->_user->id)->first(['limits.*']);
}
public function findByBudgetAndDate(\Budget $budget, Carbon $date)
{
return \Limit::whereComponentId($budget->id)->where('startdate', $date->format('Y-m-d'))->first();
}
/**
@@ -72,6 +67,16 @@ class EloquentLimitRepository implements LimitRepositoryInterface
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $data
*
@@ -116,7 +121,7 @@ class EloquentLimitRepository implements LimitRepositoryInterface
// find existing:
$count = \Limit::
leftJoin('components', 'components.id', '=', 'limits.component_id')->where(
'components.user_id', \Auth::user()->id
'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();
@@ -139,4 +144,22 @@ class EloquentLimitRepository implements LimitRepositoryInterface
return $limit;
}
/**
* @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

@@ -12,6 +12,36 @@ use Carbon\Carbon;
interface LimitRepositoryInterface
{
/**
* @param \Limit $limit
*
* @return mixed
*/
public function destroy(\Limit $limit);
/**
* @param $limitId
*
* @return mixed
*/
public function find($limitId);
/**
* @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
*
@@ -28,25 +58,8 @@ interface LimitRepositoryInterface
public function update(\Limit $limit, $data);
/**
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @param \User $user
* @return mixed
*/
public function getTJByBudgetAndDateRange(\Budget $budget, Carbon $start, Carbon $end);
/**
* @param $limitId
*
* @return mixed
*/
public function find($limitId);
/**
* @param \Limit $limit
*
* @return mixed
*/
public function destroy(\Limit $limit);
public function overruleUser(\User $user);
}

View File

@@ -14,6 +14,15 @@ use Firefly\Exception\FireflyException;
class EloquentPiggybankRepository implements PiggybankRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @return mixed
@@ -21,14 +30,14 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
public function count()
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', \Auth::user()->id
'accounts.user_id', $this->_user->id
)->count();
}
public function countNonrepeating()
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', \Auth::user()->id
'accounts.user_id', $this->_user->id
)->where('repeats', 0)->count();
}
@@ -36,7 +45,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
public function countRepeating()
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', \Auth::user()->id
'accounts.user_id', $this->_user->id
)->where('repeats', 1)->count();
}
@@ -60,16 +69,23 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
public function find($piggyBankId)
{
return \Piggybank::leftJoin('accounts', 'accounts.id', '=', 'piggybanks.account_id')->where(
'accounts.user_id', \Auth::user()->id
'accounts.user_id', $this->_user->id
)->where('piggybanks.id', $piggyBankId)->first(['piggybanks.*']);
}
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.*']);
}
/**
* @return mixed
*/
public function get()
{
$piggies = \Auth::user()->piggybanks()->with(['account', 'piggybankrepetitions'])->get();
$piggies = $this->_user->piggybanks()->with(['account', 'piggybankrepetitions'])->get();
foreach ($piggies as $pig) {
$pig->leftInAccount = $this->leftOnAccount($pig->account);
@@ -95,7 +111,6 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
}
/**
* @param \Piggybank $piggyBank
* @param $amount
@@ -115,6 +130,16 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param $data
*
@@ -122,18 +147,19 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
*/
public function store($data)
{
if ($data['targetdate'] == '') {
if (isset($data['targetdate']) && $data['targetdate'] == '') {
unset($data['targetdate']);
}
if ($data['reminder'] == 'none') {
if (isset($data['reminder']) && $data['reminder'] == 'none') {
unset($data['reminder']);
}
if ($data['startdate'] == '') {
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;
@@ -142,7 +168,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
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;
}
@@ -150,6 +176,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
if ($piggyBank->repeats && !isset($data['targetdate'])) {
$piggyBank->errors()->add('targetdate', 'Target date is mandatory!');
\Log::error('PiggyBank create-error: ' . $piggyBank->errors()->first());
return $piggyBank;
}
@@ -161,6 +188,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
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;
}
@@ -190,6 +218,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
$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;
}
@@ -197,6 +226,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
$piggyBank->save();
}
return $piggyBank;
}
@@ -210,6 +240,7 @@ class EloquentPiggybankRepository implements PiggybankRepositoryInterface
{
/** @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)) {

View File

@@ -40,6 +40,8 @@ interface PiggybankRepositoryInterface
*/
public function find($piggyBankId);
public function findByName($piggyBankName);
/**
* @return mixed
*/
@@ -77,5 +79,11 @@ interface PiggybankRepositoryInterface
*/
public function update(\Piggybank $piggy, $data);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -12,6 +12,27 @@ use Carbon\Carbon;
*/
class EloquentRecurringTransactionRepository implements RecurringTransactionRepositoryInterface
{
/**
* @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();
}
/**
* @param \RecurringTransaction $recurringTransaction
*
@@ -24,12 +45,17 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo
return true;
}
public function findByName($name)
{
return $this->_user->recurringtransactions()->where('name', 'LIKE', '%' . $name . '%')->first();
}
/**
* @return mixed
*/
public function get()
{
return \Auth::user()->recurringtransactions()->get();
return $this->_user->recurringtransactions()->get();
}
/**
@@ -40,7 +66,7 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo
public function store($data)
{
$recurringTransaction = new \RecurringTransaction;
$recurringTransaction->user()->associate(\Auth::user());
$recurringTransaction->user()->associate($this->_user);
$recurringTransaction->name = $data['name'];
$recurringTransaction->match = join(' ', explode(',', $data['match']));
$recurringTransaction->amount_max = floatval($data['amount_max']);

View File

@@ -16,6 +16,12 @@ interface RecurringTransactionRepositoryInterface
*/
public function get();
/**
* @param $name
* @return mixed
*/
public function findByName($name);
/**
* @param $data
*
@@ -38,5 +44,11 @@ interface RecurringTransactionRepositoryInterface
*/
public function update(\RecurringTransaction $recurringTransaction, $data);
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -12,6 +12,26 @@ use Carbon\Carbon;
*/
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();
}
/**
* @param \Reminder $reminder
*
@@ -42,7 +62,17 @@ class EloquentReminderRepository implements ReminderRepositoryInterface
{
$today = new Carbon;
return \Auth::user()->reminders()->validOn($today)->get();
return $this->_user->reminders()->validOn($today)->get();
}
/**
* @return mixed
*/
public function getPiggybankReminders()
{
$today = new Carbon;
return $this->_user->reminders()->where('class','PiggybankReminder')->validOn($today)->get();
}
/**
@@ -52,7 +82,7 @@ class EloquentReminderRepository implements ReminderRepositoryInterface
{
$today = new Carbon;
return \Auth::user()->reminders()->with('recurringtransaction')->validOn($today)->where(
return $this->_user->reminders()->with('recurringtransaction')->validOn($today)->where(
'class', 'RecurringTransactionReminder'
)->get();

View File

@@ -39,4 +39,10 @@ interface ReminderRepositoryInterface
public function getCurrentRecurringReminders();
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -25,6 +25,11 @@ class StorageServiceProvider extends ServiceProvider
'Firefly\Storage\Transaction\TransactionRepositoryInterface',
'Firefly\Storage\Transaction\EloquentTransactionRepository'
);
$this->app->bind(
'Firefly\Storage\Import\ImportRepositoryInterface',
'Firefly\Storage\Import\EloquentImportRepository'
);
$this->app->bind(
'Firefly\Storage\Piggybank\PiggybankRepositoryInterface',

View File

@@ -9,5 +9,24 @@ namespace Firefly\Storage\Transaction;
*/
class EloquentTransactionRepository implements TransactionRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
}

View File

@@ -9,6 +9,11 @@ namespace Firefly\Storage\Transaction;
*/
interface TransactionRepositoryInterface
{
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
}

View File

@@ -14,6 +14,16 @@ use Firefly\Exception\FireflyException;
class EloquentTransactionJournalRepository implements TransactionJournalRepositoryInterface
{
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
*
* We're building this thinking the money goes from A to B.
@@ -64,8 +74,8 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
}
// account types for both:
$toAT = $toAccount->accountType->description;
$fromAT = $from->accountType->description;
$toAT = $toAccount->accountType->type;
$fromAT = $from->accountType->type;
$journalType = null;
@@ -105,7 +115,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$journal->transactionType()->associate($journalType);
$journal->transactionCurrency()->associate($currency);
$journal->user()->associate(\Auth::user());
$journal->user()->associate($this->_user);
$journal->completed = false;
$journal->description = $description;
$journal->date = $date;
@@ -121,8 +131,8 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$fromTransaction->description = null;
$fromTransaction->amount = $amountFrom;
if (!$fromTransaction->validate()) {
throw new FireflyException('Cannot create valid transaction (from): ' . $fromTransaction->errors()->first(
));
throw new FireflyException('Cannot create valid transaction (from): ' . $fromTransaction->errors()
->first());
}
$fromTransaction->save();
@@ -149,7 +159,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
*/
public function find($journalId)
{
return \Auth::user()->transactionjournals()->with(
return $this->_user->transactionjournals()->with(
['transactions' => function ($q) {
return $q->orderBy('amount', 'ASC');
}, 'transactioncurrency', 'transactiontype', 'components', 'transactions.account',
@@ -175,7 +185,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
public function getByAccountAndDate(\Account $account, Carbon $date)
{
$accountID = $account->id;
$query = \Auth::user()->transactionjournals()->with(
$query = $this->_user->transactionjournals()->with(
[
'transactions',
'transactions.account',
@@ -184,7 +194,8 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
]
)
->distinct()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('transactions.account_id', $accountID)
->where('transaction_journals.date', $date->format('Y-m-d'))
@@ -206,14 +217,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
public function getByAccountInDateRange(\Account $account, $count = 25, Carbon $start, Carbon $end)
{
$accountID = $account->id;
$query = \Auth::user()->transactionjournals()->with(
$query = $this->_user->transactionjournals()->with(
[
'transactions',
'transactioncurrency',
'transactiontype'
]
)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('accounts.id', $accountID)
->where('date', '>=', $start->format('Y-m-d'))
@@ -226,6 +238,16 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
return $query;
}
/**
* @param \User $user
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
/**
* @param int $count
*
@@ -233,17 +255,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
*/
public function paginate($count = 25, Carbon $start = null, Carbon $end = null)
{
$query = \Auth::user()->transactionjournals()->with(
[
'transactions' => function ($q) {
return $q->orderBy('amount', 'ASC');
},
'transactions.account',
'transactions.account.accounttype',
'transactioncurrency',
'transactiontype'
]
)
$query = $this->_user->transactionjournals()->WithRelevantData()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC');
if (!is_null($start)) {
@@ -274,12 +286,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accountRepository->overruleUser($this->_user);
/** @var \Firefly\Storage\Category\CategoryRepositoryInterface $catRepository */
$catRepository = \App::make('Firefly\Storage\Category\CategoryRepositoryInterface');
$catRepository->overruleUser($this->_user);
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budRepository */
$budRepository = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$budRepository->overruleUser($this->_user);
switch ($what) {
@@ -287,6 +302,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$fromAccount = $accountRepository->find(intval($data['account_id']));
$toAccount = $accountRepository->createOrFindBeneficiary($data['beneficiary']);
break;
case 'deposit':
$fromAccount = $accountRepository->createOrFindBeneficiary($data['beneficiary']);
$toAccount = $accountRepository->find(intval($data['account_id']));
@@ -295,7 +311,6 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$fromAccount = $accountRepository->find(intval($data['account_from_id']));
$toAccount = $accountRepository->find(intval($data['account_to_id']));
break;
}
// fall back to cash if necessary:
@@ -323,6 +338,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
if ($what == 'transfer') {
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
$piggyRepository->overruleUser($this->_user);
if (isset($data['piggybank_id'])) {
/** @var \Piggybank $piggyBank */
@@ -375,12 +391,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
{
/** @var \Firefly\Storage\Category\CategoryRepositoryInterface $catRepository */
$catRepository = \App::make('Firefly\Storage\Category\CategoryRepositoryInterface');
$catRepository->overruleUser($this->_user);
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */
$budRepository = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$budRepository->overruleUser($this->_user);
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accountRepository->overruleUser($this->_user);
// update basics first:
@@ -455,6 +474,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
// attach the new piggy bank, if valid:
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
$piggyRepository->overruleUser($this->_user);
if (isset($data['piggybank_id'])) {
/** @var \Piggybank $piggyBank */

View File

@@ -27,6 +27,12 @@ interface TransactionJournalRepositoryInterface
*/
public function get();
/**
* @param \User $user
* @return mixed
*/
public function overruleUser(\User $user);
/**
* @param $what
* @param $data

View File

@@ -0,0 +1,96 @@
<?php
namespace Firefly\Trigger\Journals;
use Carbon\Carbon;
use Illuminate\Events\Dispatcher;
/**
* Class EloquentJournalTrigger
*
* @package Firefly\Trigger\Journals
*/
class EloquentJournalTrigger
{
/**
* @param \TransactionJournal $journal
*
* @return bool
*/
public function store(\TransactionJournal $journal)
{
// select all reminders for recurring transactions:
if ($journal->transaction_type->type == 'Withdrawal') {
\Log::debug('Trigger on the creation of a withdrawal');
$transaction = $journal->transactions()->orderBy('amount', 'DESC')->first();
$amount = floatval($transaction->amount);
$description = strtolower($journal->description);
$beneficiary = strtolower($transaction->account->name);
// make an array of parts:
$parts = explode(' ', $description);
$parts[] = $beneficiary;
$today = new Carbon;
$set = \RecurringTransactionReminder::
leftJoin(
'recurring_transactions', 'recurring_transactions.id', '=', 'reminders.recurring_transaction_id'
)
->where('startdate', '<', $today->format('Y-m-d'))
->where('enddate', '>', $today->format('Y-m-d'))
->where('amount_min', '<=', $amount)
->where('amount_max', '>=', $amount)->get(['reminders.*']);
/** @var \RecurringTransctionReminder $reminder */
\Log::debug('Have these parts to search for: ' . join('/',$parts));
\Log::debug('Found ' . count($set).' possible matching recurring transactions');
foreach ($set as $index => $reminder) {
/** @var \RecurringTransaction $RT */
$RT = $reminder->recurring_transaction;
$matches = explode(' ', strtolower($RT->match));
\Log::debug($index.': ' . join('/',$matches));
$matchCount = 0;
foreach ($parts as $part) {
if (in_array($part, $matches)) {
$matchCount++;
}
}
if ($matchCount >= count($matches)) {
// we have a match!
\Log::debug(
'Match between new journal "' . join('/', $parts) . '" and RT ' . join('/', $matches) . '.'
);
$journal->recurringTransaction()->associate($RT);
$journal->save();
// also update the reminder.
$reminder->active = 0;
$reminder->save();
return true;
}
}
}
return true;
}
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen('journals.store', 'Firefly\Trigger\Journals\EloquentJournalTrigger@store');
$events->listen('journals.update', 'Firefly\Trigger\Journals\EloquentJournalTrigger@update');
}
/**
* @param \TransactionJournal $journal
*
* @return bool
*/
public function update(\TransactionJournal $journal)
{
return true;
}
}

View File

@@ -14,7 +14,9 @@ use Illuminate\Events\Dispatcher;
class EloquentPiggybankTrigger
{
/**
*
* This method checks every repeating piggy bank the user has (these are called repeated expenses) and makes
* sure each repeated expense has a "repetition" for the current time period. For example, if the user has
* a weekly repeated expense of E 40,- this method will fire every week and create a new repetition.
*/
public function checkRepeatingPiggies()
{
@@ -25,26 +27,29 @@ class EloquentPiggybankTrigger
$piggies = [];
}
\Log::debug('Now in checkRepeatingPiggies with ' . count($piggies) . ' piggies');
\Log::debug('Now in checkRepeatingPiggies with ' . count($piggies) . ' piggies found.');
/** @var \Piggybank $piggyBank */
foreach ($piggies as $piggyBank) {
\Log::debug('Now working on ' . $piggyBank->name);
// get the latest repetition, see if we need to "append" more:
/*
* Get the latest repetition, see if Firefly needs to create more.
*/
/** @var \PiggybankRepetition $primer */
$primer = $piggyBank->piggybankrepetitions()->orderBy('targetdate', 'DESC')->first();
\Log::debug('Last target date is: ' . $primer->targetdate);
// for repeating piggy banks, the target date is mandatory:
$today = new Carbon;
$end = clone $primer->targetdate;
// the next repetition must be created starting at the day after the target date:
// the next repetition must be created starting at the day after the target date of the previous one.
/*
* A repeated expense runs from day 1 to day X. Since it repeats, the next repetition starts at day X+1
* until however often the repeated expense is set to repeat: a month, a week, a year.
*/
$start = clone $primer->targetdate;
$start->addDay();
while ($start <= $today) {
\Log::debug('Looping! Start is: ' . $start);
@@ -104,15 +109,26 @@ class EloquentPiggybankTrigger
/**
* Whenever a repetition is made, the decision is there to make reminders for it. Or not.
*
* Some combinations are "invalid" or impossible and will never trigger reminders. Others do.
*
* @param \PiggybankRepetition $rep
* The numbers below refer to a small list I made in a text-file (it no longer exists) which contained the eight
* binary combinations that can be made of three properties each piggy bank has (among others):
*
* - Whether or not it has a start date.
* - Whether or not it has an end date.
* - Whether or not the piggy bank repeats itself.
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param \PiggybankRepetition $repetition
*
* @return null
*/
public function createdRepetition(\PiggybankRepetition $repetition)
{
\Log::debug('TRIGGER on createdRepetition() for repetition #' . $repetition->id);
$piggyBank = $repetition->piggybank;
@@ -120,49 +136,70 @@ class EloquentPiggybankTrigger
// no reminders needed (duh)
if (is_null(($piggyBank->reminder))) {
\Log::debug('No reminders because no reminder needed.');
return null;
}
// no start, no target, no repeat (#1):
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 0) {
\Log::debug('No reminders because no start, no target, no repeat (#1)');
return null;
}
// no start, but repeats (#5):
if (is_null($piggyBank->startdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, but repeats (#5)');
return null;
}
// no start, no end, but repeats (#6)
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, no end, but repeats (#6)');
return null;
}
// no end, but repeats (#7)
if (is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no end, but repeats (#7)');
return null;
}
// #2, #3, #4 and #8 are valid combo's.
\Log::debug('Will continue...');
/*
* #2, #3, #4 and #8 are valid combo's.
*
* We add two years to the end when the repetition has no target date; we "pretend" there is a target date.
*
*/
if (is_null($repetition->targetdate)) {
$end = new Carbon;
$end->addYears(2);
} else {
$end = $repetition->targetdate;
}
/*
* If there is no start date, the start dat becomes right now.
*/
if (is_null($repetition->startdate)) {
$start = new Carbon;
} else {
$start = $repetition->startdate;
}
/*
* Firefly checks every period X between $start and $end and if necessary creates a reminder. Firefly
* only creates reminders if the $current date is after today. Piggy banks may have their start in the past.
*
* This loop will jump a month when the reminder is set monthly, a week when it's set weekly, etcetera.
*/
$current = $start;
$today = new Carbon;
$today->startOfDay();
while ($current <= $end) {
// when do we start reminding?
// X days before $current:
\Log::debug('Looping reminder dates; now at ' . $current);
/*
* Piggy bank reminders start X days before the actual date of the event.
*/
$reminderStart = clone $current;
switch ($piggyBank->reminder) {
case 'day':
@@ -179,20 +216,32 @@ class EloquentPiggybankTrigger
break;
}
/*
* If the date is past today we create a reminder, otherwise we don't. The end date is the date
* the reminder is due; after that it is invalid.
*/
if ($current >= $today) {
$reminder = new \PiggybankReminder;
$reminder->piggybank()->associate($piggyBank);
$reminder->user()->associate(\Auth::user());
$reminder->startdate = $reminderStart;
$reminder->enddate = $current;
$reminder->active = 1;
\Log::debug('Will create a reminder. Is it valid?');
\Log::debug($reminder->validate());
try {
$reminder->save();
} catch (QueryException $e) {
\Log::error('Could not save reminder: ' . $e->getMessage());
}
} else {
\Log::debug('Current is before today, will not make a reminder.');
}
/*
* Here Firefly jumps ahead to the next reminder period.
*/
switch ($piggyBank->reminder) {
case 'day':
$current->addDays($piggyBank->reminder_skip);

View File

@@ -0,0 +1,22 @@
<?php
namespace Firefly\Validation;
use Illuminate\Validation\Validator;
/**
* Class FireflyValidator
*
* @package Firefly\Validation
*/
class FireflyValidator extends Validator
{
public function validateAlphabasic($attribute, $value, $parameters)
{
$pattern = '/[^a-z_\-0-9 ]/i';
if (preg_match($pattern, $value)) {
return false;
} else {
return true;
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Firefly\Validation;
use Illuminate\Support\ServiceProvider;
class ValidationServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->validator->resolver(
function ($translator, $data, $rules, $messages) {
return new FireflyValidator($translator, $data, $rules, $messages);
}
);
}
public function register()
{
}
}

View File

@@ -1,5 +1,6 @@
<?php
use LaravelBook\Ardent\Ardent as Ardent;
use LaravelBook\Ardent\Builder;
/**
* Account
@@ -22,6 +23,7 @@ use LaravelBook\Ardent\Ardent as Ardent;
* @method static \Illuminate\Database\Query\Builder|\Account whereAccountTypeId($value)
* @method static \Illuminate\Database\Query\Builder|\Account whereName($value)
* @method static \Illuminate\Database\Query\Builder|\Account whereActive($value)
* @method static \Account accountTypeIn($types)
*/
class Account extends Ardent
{
@@ -33,7 +35,7 @@ class Account extends Ardent
*/
public static $rules
= [
'name' => 'required|between:1,100',
'name' => ['required', 'between:1,100', 'alphabasic'],
'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id',
'active' => 'required|boolean'
@@ -70,6 +72,20 @@ class Account extends Ardent
);
}
public function balanceBeforeJournal(TransactionJournal $journal)
{
return floatval(
$this->transactions()
->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)
->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
->where('transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s'))
->where('transaction_journals.id','!=',$journal->id)
->sum('transactions.amount')
);
}
/**
* Transactions.
*
@@ -96,7 +112,8 @@ class Account extends Ardent
public function predict(
/** @noinspection PhpUnusedParameterInspection */
\Carbon\Carbon $date
) {
)
{
return null;
}
@@ -110,4 +127,13 @@ class Account extends Ardent
return $this->belongsTo('User');
}
public function scopeAccountTypeIn(Builder $query, array $types) {
if(is_null($this->joinedAccountTypes)) {
$query->leftJoin('account_types','account_types.id','=','accounts.account_type_id');
$this->joinedAccountTypes = true;
}
$query->whereIn('account_types.type',$types);
}
}

View File

@@ -1,5 +1,6 @@
<?php
use Illuminate\Database\Eloquent\Model as Eloquent;
/**
* AccountType
@@ -7,15 +8,23 @@
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $description
* @property string $type
* @property boolean $editable
* @property-read \Illuminate\Database\Eloquent\Collection|\Account[] $accounts
* @method static \Illuminate\Database\Query\Builder|\AccountType whereId($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereType($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereEditable($value)
*/
class AccountType extends Eloquent
{
public static $rules
= [
'type' => ['required', 'between:1,50', 'alphabasic'],
'editable' => 'required|boolean',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany

View File

@@ -33,7 +33,7 @@ class Budget extends Component
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|\TransactionJournal
*/
public function transactionjournals()
{

View File

@@ -27,7 +27,7 @@ class Component extends SingleTableInheritanceEntity
public static $rules
= [
'user_id' => 'exists:users,id|required',
'name' => 'required|between:1,255',
'name' => ['required', 'between:1,100', 'alphabasic'],
'class' => 'required',
];
protected $table = 'components';

View File

@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Eloquent\Model as Eloquent;
/**
* Importentry
*
* @property-read \Importmap $importmap
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $class
* @property integer $importmap_id
* @property integer $old
* @property integer $new
* @method static \Illuminate\Database\Query\Builder|\Importentry whereId($value)
* @method static \Illuminate\Database\Query\Builder|\Importentry whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Importentry whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Importentry whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\Importentry whereImportmapId($value)
* @method static \Illuminate\Database\Query\Builder|\Importentry whereOld($value)
* @method static \Illuminate\Database\Query\Builder|\Importentry whereNew($value)
*/
class Importentry extends Eloquent {
public function importmap()
{
return $this->belongsTo('Importmap');
}
}

29
app/models/Importmap.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Eloquent\Model as Eloquent;
/**
* Class Importmap
*
* @property-read \User $user
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property string $file
* @method static \Illuminate\Database\Query\Builder|\Importmap whereId($value)
* @method static \Illuminate\Database\Query\Builder|\Importmap whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Importmap whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Importmap whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\Importmap whereFile($value)
*/
class Importmap extends Eloquent
{
/**
* User
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('User');
}
}

View File

@@ -23,6 +23,7 @@ use LaravelBook\Ardent\Ardent as Ardent;
* @property-read \Account $account
* @property-read \Illuminate\Database\Eloquent\Collection|\PiggybankRepetition[] $piggybankrepetitions
* @property-read \Illuminate\Database\Eloquent\Collection|\PiggybankEvent[] $piggybankevents
* @property-read \Illuminate\Database\Eloquent\Collection|\PiggybankReminder[] $piggybankreminders
* @property-read \Illuminate\Database\Eloquent\Collection|\Transaction[] $transactions
* @method static \Illuminate\Database\Query\Builder|\Piggybank whereId($value)
* @method static \Illuminate\Database\Query\Builder|\Piggybank whereCreatedAt($value)
@@ -39,7 +40,6 @@ use LaravelBook\Ardent\Ardent as Ardent;
* @method static \Illuminate\Database\Query\Builder|\Piggybank whereReminder($value)
* @method static \Illuminate\Database\Query\Builder|\Piggybank whereReminderSkip($value)
* @method static \Illuminate\Database\Query\Builder|\Piggybank whereOrder($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\PiggybankReminder[] $piggybankreminders
*/
class Piggybank extends Ardent
{

View File

@@ -2,7 +2,7 @@
use Carbon\Carbon;
/**
* Class PiggybankReminder
* PiggybankReminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at
@@ -35,8 +35,8 @@ class PiggybankReminder extends Reminder
protected $isSubclass = true;
/**
* This method will render a string telling you something about what to save or something.
* @return string
* @throws Firefly\Exception\FireflyException
*/
public function render()
{
@@ -85,7 +85,7 @@ class PiggybankReminder extends Reminder
$toSave = 0;
switch ($piggyBank->reminder) {
case 'day':
throw new \Firefly\Exception\FireflyException('No impl day reminder/ PiggyBankReminder Render');
$toSave = $left;// / ($diff->days / $piggyBank->reminder_skip);
break;
case 'week':
$weeks = ceil($diff->days / 7);

View File

@@ -1,7 +1,6 @@
<?php
use LaravelBook\Ardent\Ardent as Ardent;
/**
* PiggybankRepetition
*

View File

@@ -2,7 +2,6 @@
use LaravelBook\Ardent\Ardent;
/**
* Preference
*

View File

@@ -18,6 +18,7 @@ use LaravelBook\Ardent\Ardent;
* @property boolean $automatch
* @property string $repeat_freq
* @property integer $skip
* @property-read \Illuminate\Database\Eloquent\Collection|\RecurringTransactionReminder[] $reminders
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\RecurringTransaction whereId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransaction whereCreatedAt($value)
@@ -32,7 +33,6 @@ use LaravelBook\Ardent\Ardent;
* @method static \Illuminate\Database\Query\Builder|\RecurringTransaction whereAutomatch($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransaction whereRepeatFreq($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransaction whereSkip($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\RecurringTransactionReminder[] $reminders
*/
class RecurringTransaction extends Ardent
{

View File

@@ -1,7 +1,7 @@
<?php
/**
* Class RecurringTransactionReminder
* RecurringTransactionReminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at
@@ -33,5 +33,10 @@ class RecurringTransactionReminder extends Reminder
{
protected $isSubclass = true;
public function render()
{
return '123';
}
}

View File

@@ -3,10 +3,8 @@
use Carbon\Carbon;
use Firefly\Database\SingleTableInheritanceEntity;
/**
* Class Reminder
* // reminder for: recurring, piggybank.
* Reminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at

View File

@@ -1,6 +1,8 @@
<?php
use Carbon\Carbon;
use LaravelBook\Ardent\Ardent;
use LaravelBook\Ardent\Builder;
/**
@@ -28,6 +30,12 @@ use LaravelBook\Ardent\Ardent;
* @method static \Illuminate\Database\Query\Builder|\Transaction whereTransactionJournalId($value)
* @method static \Illuminate\Database\Query\Builder|\Transaction whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\Transaction whereAmount($value)
* @method static \Transaction account($account)
* @method static \Transaction after($date)
* @method static \Transaction before($date)
* @method static \Transaction lessThan($amount)
* @method static \Transaction moreThan($amount)
* @method static \Transaction transactionTypes($types)
*/
class Transaction extends Ardent
{
@@ -81,6 +89,56 @@ class Transaction extends Ardent
return $this->belongsTo('Piggybank');
}
public function scopeAccount(Builder $query, Account $account)
{
$query->where('transactions.account_id', $account->id);
}
public function scopeAfter(Builder $query, Carbon $date)
{
if (is_null($this->joinedJournals)) {
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=',
'transactions.transaction_journal_id');
$this->joinedJournals = true;
}
$query->where('transaction_journals.date', '>=', $date->format('Y-m-d'));
}
public function scopeBefore(Builder $query, Carbon $date)
{
if (is_null($this->joinedJournals)) {
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=',
'transactions.transaction_journal_id');
$this->joinedJournals = true;
}
$query->where('transaction_journals.date', '<=', $date->format('Y-m-d'));
}
public function scopeLessThan(Builder $query, $amount)
{
$query->where('amount', '<', $amount);
}
public function scopeMoreThan(Builder $query, $amount)
{
$query->where('amount', '>', $amount);
}
public function scopeTransactionTypes(Builder $query, array $types)
{
if (is_null($this->joinedJournals)) {
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=',
'transactions.transaction_journal_id');
$this->joinedJournals = true;
}
if (is_null($this->joinedTransactionTypes)) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id');
$this->joinedTransactionTypes = true;
}
$query->whereIn('transaction_types.type', $types);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@@ -1,5 +1,5 @@
<?php
use Illuminate\Database\Eloquent\Model as Eloquent;
/**
* TransactionCurrency
*

View File

@@ -2,20 +2,20 @@
use Carbon\Carbon;
use LaravelBook\Ardent\Ardent;
use LaravelBook\Ardent\Builder;
/**
* TransactionJournal
*
* @property integer $id
* @property Carbon $created_at
* @property Carbon $updated_at
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property integer $transaction_type_id
* @property integer $transaction_currency_id
* @property string $description
* @property boolean $completed
* @property Carbon $date
* @property \Carbon\Carbon $date
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
@@ -34,13 +34,67 @@ use LaravelBook\Ardent\Ardent;
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCompleted($value)
* @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDate($value)
* @method static \TransactionJournal account($account)
* @method static \TransactionJournal after($date)
* @method static \TransactionJournal before($date)
* @method static \TransactionJournal defaultSorting()
* @method static \TransactionJournal moreThan($amount)
* @method static \TransactionJournal lessThan($amount)
* @method static \TransactionJournal onDate($date)
* @method static \TransactionJournal transactionTypes($types)
* @method static \TransactionJournal withRelevantData()
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @method static \TransactionJournal onDate($date)
*/
class TransactionJournal extends Ardent
{
@@ -83,6 +137,14 @@ class TransactionJournal extends Ardent
return $this->belongsToMany('Component');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function recurringTransaction()
{
return $this->belongsTo('RecurringTransaction');
}
/**
* @return array
*/
@@ -91,6 +153,15 @@ class TransactionJournal extends Ardent
return ['created_at', 'updated_at', 'date'];
}
public function scopeAccount(Builder $query, \Account $account)
{
if (!isset($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id');
$this->joinedTransactions = true;
}
$query->where('transactions.account_id', $account->id);
}
/**
* @param $query
* @param Carbon $date
@@ -113,6 +184,33 @@ class TransactionJournal extends Ardent
return $query->where('date', '<=', $date->format('Y-m-d'));
}
public function scopeDefaultSorting(Builder $query)
{
$query->orderBy('date', 'DESC')->orderBy('transaction_journals.id', 'DESC');
}
public function scopeMoreThan(Builder $query, $amount)
{
if (is_null($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id');
$this->joinedTransactions = true;
}
$query->where('transactions.amount', '>=', $amount);
}
public function scopeLessThan(Builder $query, $amount)
{
if (is_null($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id');
$this->joinedTransactions = true;
}
$query->where('transactions.amount', '<=', $amount);
}
/**
* @param $query
* @param Carbon $date
@@ -124,6 +222,33 @@ class TransactionJournal extends Ardent
return $query->where('date', '=', $date->format('Y-m-d'));
}
public function scopeTransactionTypes(Builder $query, array $types)
{
if (is_null($this->joinedTransactionTypes)) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id');
$this->joinedTransactionTypes = true;
}
$query->whereIn('transaction_types.type', $types);
}
/**
* Automatically includes the 'with' parameters to get relevant related
* objects.
*
* @param $query
*/
public function scopeWithRelevantData(Builder $query)
{
$query->with(
['transactions' => function ($q) {
$q->orderBy('amount', 'ASC');
}, 'transactiontype', 'components' => function ($q) {
$q->orderBy('class');
}, 'transactions.account.accounttype','recurringTransaction']
);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@@ -20,6 +20,8 @@ use LaravelBook\Ardent\Ardent;
* @property boolean $migrated
* @property-read \Illuminate\Database\Eloquent\Collection|\Account[] $accounts
* @property-read \Illuminate\Database\Eloquent\Collection|\Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\Reminder[] $reminders
* @property-read \Illuminate\Database\Eloquent\Collection|\PiggybankReminder[] $piggybankreminders
* @property-read \Illuminate\Database\Eloquent\Collection|\Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\Component[] $components
* @property-read \Illuminate\Database\Eloquent\Collection|\Preference[] $preferences
@@ -33,8 +35,6 @@ use LaravelBook\Ardent\Ardent;
* @method static \Illuminate\Database\Query\Builder|\User whereReset($value)
* @method static \Illuminate\Database\Query\Builder|\User whereRememberToken($value)
* @method static \Illuminate\Database\Query\Builder|\User whereMigrated($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\Reminder[] $reminders
* @property-read \Illuminate\Database\Eloquent\Collection|\PiggybankReminder[] $piggybankreminders
*/
class User extends Ardent implements UserInterface, RemindableInterface
{

View File

@@ -1,23 +1,33 @@
<?php
//use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
// models:
Route::bind('account', function($value, $route)
{
if(Auth::check()) {
return Account::
where('id', $value)->
where('user_id',Auth::user()->id)->first();
$account = Account::
leftJoin('account_types','account_types.id','=','accounts.account_type_id')->
where('account_types.editable',1)->
where('accounts.id', $value)->
where('user_id',Auth::user()->id)->
first(['accounts.*']);
if($account) {
return $account;
}
return null;
}
App::abort(404);
});
Route::bind('accountname', function($value, $route)
{
if(Auth::check()) {
$type = AccountType::where('description','Default account')->first();
return Account::
leftJoin('account_types','account_types.id','=','accounts.account_type_id')->
where('account_types.editable',1)->
where('name', $value)->
where('account_type_id',$type->id)->
where('user_id',Auth::user()->id)->first();
}
return null;
@@ -124,7 +134,7 @@ Route::group(['before' => 'auth'], function () {
Route::get('/budgets',['uses' => 'BudgetController@indexByDate','as' => 'budgets.index']);
Route::get('/budgets/create',['uses' => 'BudgetController@create', 'as' => 'budgets.create']);
Route::get('/budgets/budget',['uses' => 'BudgetController@indexByBudget','as' => 'budgets.index.budget']);
Route::get('/budgets/show/{budget}',['uses' => 'BudgetController@show', 'as' => 'budgets.show']);
Route::get('/budgets/show/{budget}/{limitrepetition?}',['uses' => 'BudgetController@show', 'as' => 'budgets.show']);
Route::get('/budgets/edit/{budget}',['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']);
Route::get('/budgets/delete/{budget}',['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']);
@@ -147,8 +157,6 @@ Route::group(['before' => 'auth'], function () {
Route::get('chart/budget/{budget}/session', ['uses' => 'ChartController@budgetSession', 'as' => 'chart.budget.session']);
Route::get('chart/budget/envelope/{limitrepetition}', ['uses' => 'ChartController@budgetLimit', 'as' => 'chart.budget.limit']);
// home controller
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']);
@@ -162,20 +170,19 @@ Route::group(['before' => 'auth'], function () {
Route::get('/budgets/limits/delete/{limit}',['uses' => 'LimitController@delete','as' => 'budgets.limits.delete']);
Route::get('/budgets/limits/edit/{limit}',['uses' => 'LimitController@edit','as' => 'budgets.limits.edit']);
Route::get('/migrate',['uses' => 'MigrateController@index', 'as' => 'migrate.index']);
// piggy bank controller
Route::get('/piggybanks',['uses' => 'PiggybankController@index','as' => 'piggybanks.index']);
Route::get('/piggybanks/create/piggybank', ['uses' => 'PiggybankController@createPiggybank','as' => 'piggybanks.create.piggybank']);
Route::get('/piggybanks/create/repeated', ['uses' => 'PiggybankController@createRepeated','as' => 'piggybanks.create.repeated']);
Route::get('/piggybanks/addMoney/{piggybank}', ['uses' => 'PiggybankController@addMoney','as' => 'piggybanks.amount.add']);
Route::get('/piggybanks/removeMoney/{piggybank}', ['uses' => 'PiggybankController@removeMoney','as' => 'piggybanks.amount.remove']);
Route::get('/piggybanks/show/{piggybank}', ['uses' => 'PiggybankController@show','as' => 'piggybanks.show']);
Route::get('/piggybanks/edit/{piggybank}', ['uses' => 'PiggybankController@edit','as' => 'piggybanks.edit']);
Route::get('/piggybanks/delete/{piggybank}', ['uses' => 'PiggybankController@delete','as' => 'piggybanks.delete']);
Route::post('/piggybanks/updateAmount/{piggybank}',['uses' => 'PiggybankController@updateAmount','as' => 'piggybanks.updateAmount']);
// preferences controller
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
@@ -237,6 +244,8 @@ Route::group(['before' => 'csrf|auth'], function () {
Route::post('/budgets/limits/destroy/{limit}',['uses' => 'LimitController@destroy','as' => 'budgets.limits.destroy']);
Route::post('/budgets/limits/update/{limit}',['uses' => 'LimitController@update','as' => 'budgets.limits.update']);
Route::post('/migrate/upload',['uses' => 'MigrateController@upload', 'as' => 'migrate.upload']);
// piggy bank controller
Route::post('/piggybanks/store/piggybank',['uses' => 'PiggybankController@storePiggybank','as' => 'piggybanks.store.piggybank']);

View File

@@ -1,452 +0,0 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class AccountControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @coversDefaultClass \AccountController
*/
class AccountControllerTest extends TestCase
{
protected $_repository;
protected $_user;
protected $_accounts;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_repository = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_accounts = $this->mock('Firefly\Helper\Controllers\AccountInterface');
$this->_user = m::mock('User', 'Eloquent');
}
public function tearDown()
{
Mockery::close();
}
/**
* @covers ::create
*/
public function testCreate()
{
View::shouldReceive('make')->with('accounts.create')->once();
$this->action('GET', 'AccountController@create');
$this->assertResponseOk();
}
/**
* @covers ::delete
*/
public function testDelete()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'AccountController@delete', $account->id);
$this->assertViewHas('account');
$this->assertResponseOk();
}
/**
* @covers ::delete
*/
public function testDeleteWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'AccountController@delete', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
/**
* @covers ::destroy
*/
public function testDestroy()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->once()->andReturn($this->_user);
Auth::shouldReceive('check')->once()->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('destroy')->once()->andReturn(true);
$this->action('POST', 'AccountController@destroy', $account->id);
$this->assertRedirectedToRoute('accounts.index');
$this->assertSessionHas('success');
}
/**
* @covers ::destroy
*/
public function testDestroyWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->once()->andReturn($this->_user);
Auth::shouldReceive('check')->once()->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->action('POST', 'AccountController@destroy', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
/**
* @covers ::destroy
*/
public function testDestroyFails()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->once()->andReturn($this->_user);
Auth::shouldReceive('check')->once()->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('destroy')->once()->andReturn(false);
$this->action('POST', 'AccountController@destroy', $account->id);
$this->assertRedirectedToRoute('accounts.index');
$this->assertSessionHas('error');
}
public function testEdit()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_accounts->shouldReceive('openingBalanceTransaction')->once()->andReturn(null);
// test if the view works:
View::shouldReceive('make')->with('accounts.edit')->once()->andReturn(m::self())->shouldReceive('with')->with(
'account', m::any()
)
->andReturn(m::self())->shouldReceive('with')->with('openingBalance', null)->andReturn(m::self());
$this->action('GET', 'AccountController@edit', $account->id);
$this->assertResponseOk();
}
public function testEditWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'AccountController@edit', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
public function testIndex()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
$collection = new Collection();
$collection->add($account);
$list = [
'personal' => [],
'beneficiaries' => [],
'initial' => [],
'cash' => []
];
$this->_repository->shouldReceive('get')->once()->andReturn($collection);
$this->_accounts->shouldReceive('index')->with($collection)->once()->andReturn($list);
$this->action('GET', 'AccountController@index');
$this->assertResponseOk();
}
public function testShow()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($account->email);
$this->session(['start' => new Carbon, 'end' => new Carbon]);
// some more mockery
$paginator = \Paginator::make([], 0, 10);
$data = [
'statistics' => [
'period' => [
'in' => 0,
'out' => 0,
'diff' => 0,
't_in' => 0,
't_out' => 0,
't_diff' => 0
],
'categories' => [],
'budgets' => [],
'accounts' => []
],
'journals' => $paginator,
];
$this->_accounts->shouldReceive('show')->once()->andReturn($data);
$this->action('GET', 'AccountController@show', $account->id);
$this->assertResponseOk();
}
public function testShowWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn($account->email);
$this->action('GET', 'AccountController@show', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
public function testStore()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
$this->_repository->shouldReceive('store')->andReturn($account);
$this->action('POST', 'AccountController@store');
$this->assertRedirectedToRoute('accounts.index');
}
public function testStoreFails()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
unset($account->name);
$this->_repository->shouldReceive('store')->andReturn($account);
$this->action('POST', 'AccountController@store');
$this->assertRedirectedToRoute('accounts.create');
}
public function testStoreRecreate()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
$this->_repository->shouldReceive('store')->andReturn($account);
$this->action('POST', 'AccountController@store', ['create' => '1']);
$this->assertRedirectedToRoute('accounts.create');
}
public function testUpdate()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('update')->andReturn($account);
$this->action('POST', 'AccountController@update', $account->id);
$this->assertRedirectedToRoute('accounts.index');
}
public function testUpdateWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('update')->andReturn($account);
$this->action('POST', 'AccountController@update', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
public function testUpdateFails()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
unset($account->name);
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('update')->andReturn($account);
$this->action('POST', 'AccountController@update', $account->id);
$this->assertRedirectedToRoute('accounts.edit', $account->id);
}
}

View File

@@ -1,274 +0,0 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class BudgetControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class BudgetControllerTest extends TestCase
{
protected $_repository;
protected $_user;
protected $_budgets;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_repository = $this->mock('Firefly\Storage\Budget\BudgetRepositoryInterface');
$this->_budgets = $this->mock('Firefly\Helper\Controllers\BudgetInterface');
$this->_user = m::mock('User', 'Eloquent');
}
public function tearDown()
{
Mockery::close();
}
public function testCreate()
{
$this->action('GET', 'BudgetController@create');
$this->assertResponseOk();
}
public function testDelete()
{
$budget = f::create('Budget');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'BudgetController@delete', $budget->id);
$this->assertResponseOk();
}
public function testDestroy()
{
$budget = f::create('Budget');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
Event::shouldReceive('fire')->once()->with('budgets.destroy', [$budget]);
$this->_repository->shouldReceive('destroy')->once()->andReturn(true);
$this->action('POST', 'BudgetController@destroy', $budget->id);
$this->assertRedirectedToRoute('budgets.index.budget');
$this->assertSessionHas('success');
}
public function testDestroyByDate()
{
$budget = f::create('Budget');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
Event::shouldReceive('fire')->once()->with('budgets.destroy', [$budget]);
$this->_repository->shouldReceive('destroy')->once()->andReturn(true);
$this->action('POST', 'BudgetController@destroy', [$budget->id, 'from' => 'date']);
$this->assertRedirectedToRoute('budgets.index');
$this->assertSessionHas('success');
}
public function testDestroyFails()
{
$budget = f::create('Budget');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
Event::shouldReceive('fire')->once()->with('budgets.destroy', [$budget]);
$this->_repository->shouldReceive('destroy')->once()->andReturn(false);
$this->action('POST', 'BudgetController@destroy', $budget->id);
$this->assertRedirectedToRoute('budgets.index');
$this->assertSessionHas('error');
}
public function testEdit()
{
$budget = f::create('Budget');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'BudgetController@edit', $budget->id);
$this->assertResponseOk();
}
public function testIndexByBudget()
{
$this->_repository->shouldReceive('get')->once()->andReturn([]);
$this->action('GET', 'BudgetController@indexByBudget');
$this->assertResponseOk();
}
public function testIndexByDate()
{
$collection = new Collection();
$this->_repository->shouldReceive('get')->once()->andReturn($collection);
$this->_budgets->shouldReceive('organizeByDate')->with($collection)->andReturn([]);
$this->action('GET', 'BudgetController@indexByDate');
$this->assertResponseOk();
}
public function testShow()
{
$budget = f::create('Budget');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($budget->email);
$this->session(['start' => new Carbon, 'end' => new Carbon]);
$this->_budgets->shouldReceive('organizeRepetitions')->once()->andReturn([]);
$this->action('GET', 'BudgetController@show', $budget->id);
$this->assertResponseOk();
}
public function testShowNoEnvelope()
{
$budget = f::create('Budget');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($budget->email);
$this->session(['start' => new Carbon, 'end' => new Carbon]);
$this->_budgets->shouldReceive('outsideRepetitions')->once()->andReturn([]);
$this->action('GET', 'BudgetController@show', [$budget->id, 'noenvelope' => 'true']);
$this->assertResponseOk();
}
public function testShowWithRep()
{
$budget = f::create('Budget');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($budget->email);
$this->session(['start' => new Carbon, 'end' => new Carbon]);
// $this->_budgets->shouldReceive('show')->once()->andReturn([]);
$arr = [0 => ['limitrepetition' => null, 'limit' => null, 'date' => '']];
$this->_budgets->shouldReceive('organizeRepetition')->once()->andReturn($arr);
$this->action('GET', 'BudgetController@show', [$budget->id, 'rep' => '1']);
$this->assertResponseOk();
}
public function testStore()
{
$budget = f::create('Budget');
$this->_repository->shouldReceive('store')->andReturn($budget);
$this->action('POST', 'BudgetController@store');
$this->assertRedirectedToRoute('budgets.index.budget');
}
public function testStoreFromDate()
{
$budget = f::create('Budget');
$this->_repository->shouldReceive('store')->andReturn($budget);
$this->action('POST', 'BudgetController@store', ['from' => 'date']);
$this->assertRedirectedToRoute('budgets.index');
}
public function testStoreFails()
{
$budget = f::create('Budget');
unset($budget->name);
$this->_repository->shouldReceive('store')->andReturn($budget);
$this->action('POST', 'BudgetController@store', ['from' => 'budget']);
$this->assertRedirectedToRoute('budgets.create');
}
public function testStoreRecreate()
{
$budget = f::create('Budget');
$this->_repository->shouldReceive('store')->andReturn($budget);
$this->action('POST', 'BudgetController@store', ['from' => 'budget', 'create' => '1']);
$this->assertRedirectedToRoute('budgets.create', ['from' => 'budget']);
}
public function testUpdate()
{
$budget = f::create('Budget');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_repository->shouldReceive('update')->andReturn($budget);
Event::shouldReceive('fire')->with('budgets.update', [$budget]);
$this->action('POST', 'BudgetController@update', $budget->id);
$this->assertRedirectedToRoute('budgets.index.budget');
}
public function testUpdateFromDate()
{
$budget = f::create('Budget');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_repository->shouldReceive('update')->andReturn($budget);
Event::shouldReceive('fire')->with('budgets.update', [$budget]);
//$this->_user->shouldReceive('budgets')->andReturn([]); // trigger
$this->action('POST', 'BudgetController@update', [$budget->id, 'from' => 'date']);
$this->assertRedirectedToRoute('budgets.index');
}
public function testUpdateFails()
{
$budget = f::create('Budget');
unset($budget->name);
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($budget->user_id);
$this->_repository->shouldReceive('update')->andReturn($budget);
$this->action('POST', 'BudgetController@update', $budget->id);
$this->assertRedirectedToRoute('budgets.edit', $budget->id);
}
}

View File

@@ -1,183 +0,0 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class CategoryControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class CategoryControllerTest extends TestCase
{
protected $_repository;
protected $_user;
protected $_category;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_repository = $this->mock('Firefly\Storage\Category\CategoryRepositoryInterface');
$this->_category = $this->mock('Firefly\Helper\Controllers\CategoryInterface');
$this->_user = m::mock('User', 'Eloquent');
}
public function tearDown()
{
Mockery::close();
}
public function testCreate()
{
$this->action('GET', 'CategoryController@create');
$this->assertResponseOk();
}
public function testDelete()
{
$category = f::create('Category');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'CategoryController@delete', $category->id);
$this->assertResponseOk();
}
public function testDestroy()
{
$category = f::create('Category');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_repository->shouldReceive('destroy')->once()->andReturn(true);
$this->action('POST', 'CategoryController@destroy', $category->id);
$this->assertRedirectedToRoute('categories.index');
$this->assertSessionHas('success');
}
public function testDestroyFails()
{
$category = f::create('Category');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_repository->shouldReceive('destroy')->once()->andReturn(false);
$this->action('POST', 'CategoryController@destroy', $category->id);
$this->assertRedirectedToRoute('categories.index');
$this->assertSessionHas('error');
}
public function testEdit()
{
$category = f::create('Category');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'CategoryController@edit', $category->id);
$this->assertResponseOk();
}
public function testIndex()
{
$category = f::create('Category');
$collection = new Collection();
$collection->add($category);
$this->_repository->shouldReceive('get')->with()->once()->andReturn($collection);
$this->action('GET', 'CategoryController@index');
$this->assertResponseOk();
}
public function testShow()
{
$category = f::create('Category');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($category->email);
$this->session(['start' => new Carbon, 'end' => new Carbon]);
$this->_category->shouldReceive('journalsInRange')->once()->andReturn([]);
$this->action('GET', 'CategoryController@show', $category->id);
$this->assertResponseOk();
}
public function testStore()
{
$category = f::create('Category');
$this->_repository->shouldReceive('store')->andReturn($category);
$this->action('POST', 'CategoryController@store');
$this->assertRedirectedToRoute('categories.index');
}
public function testStoreFails()
{
$category = f::create('Category');
unset($category->name);
$this->_repository->shouldReceive('store')->andReturn($category);
$this->action('POST', 'CategoryController@store');
$this->assertRedirectedToRoute('categories.create');
}
public function testStoreRecreate()
{
$category = f::create('Category');
$this->_repository->shouldReceive('store')->andReturn($category);
$this->action('POST', 'CategoryController@store', ['create' => '1']);
$this->assertRedirectedToRoute('categories.create');
}
public function testUpdate()
{
$category = f::create('Category');
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_repository->shouldReceive('update')->andReturn($category);
$this->action('POST', 'CategoryController@update', $category->id);
$this->assertRedirectedToRoute('categories.index');
}
public function testUpdateFails()
{
$category = f::create('Category');
unset($category->name);
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_repository->shouldReceive('update')->andReturn($category);
$this->action('POST', 'CategoryController@update', [$category->id]);
$this->assertResponseStatus(302);
}
}

View File

@@ -1,140 +0,0 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class ChartControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class ChartControllerTest extends TestCase
{
protected $_user;
// protected $_repository;
protected $_accounts;
protected $_charts;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_charts = $this->mock('Firefly\Helper\Controllers\ChartInterface');
// $this->_category = $this->mock('Firefly\Helper\Controllers\CategoryInterface');
$this->_user = m::mock('User', 'Eloquent');
}
public function tearDown()
{
Mockery::close();
}
public function testCategoryShowChart()
{
$this->session(['start' => new Carbon, 'end' => new Carbon, 'range' => '1M']);
$category = f::create('Category');
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($category->user_id);
$this->_charts->shouldReceive('categoryShowChart')->once()->andReturn([]);
$this->action('GET', 'ChartController@categoryShowChart', $category->id);
$this->assertResponseOk();
}
public function testHomeAccount()
{
$account = f::create('Account');
$collection = new Collection();
$collection->add($account);
$this->session(['start' => new Carbon, 'end' => new Carbon, 'range' => '1M']);
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('bla@bla');
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn(1);
$this->_accounts->shouldReceive('getByIds')->andReturn($collection);
$this->_charts->shouldReceive('account')->once()->andReturn([]);
$this->action('GET', 'ChartController@homeAccount');
$this->assertResponseOk();
}
public function testHomeAccountInfo()
{
$account = f::create('Account');
$type = f::create('AccountType');
$type->description = 'Default account';
$type->save();
$account->accounttype()->associate($type);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->andReturn($account->user()->first());
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('bla@bla');
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($account->user_id);
$this->_accounts->shouldReceive('findByName')->andReturn($account);
$this->_charts->shouldReceive('accountDailySummary')->once()->andReturn(['rows' => [], 'sum' => 0]);
$this->call('GET', 'chart/home/info/' . $account->name . '/01/08/2014');
$this->assertResponseOk();
}
public function testHomeAccountWithAccount()
{
$account = f::create('Account');
$this->session(['start' => new Carbon, 'end' => new Carbon, 'range' => '1M']);
// for successful binding:
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('bla@bla');
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($account->user_id);
$this->_charts->shouldReceive('account')->once()->andReturn([]);
$this->action('GET', 'ChartController@homeAccount', $account->id);
$this->assertResponseOk();
}
public function testHomeBudgets()
{
$date = new Carbon;
$this->session(['start' => $date]);
$this->_charts->shouldReceive('budgets')->once()->with($date)->andReturn([]);
$this->action('GET', 'ChartController@homeBudgets');
$this->assertResponseOk();
}
public function testHomeCategories()
{
$start = new Carbon;
$end = new Carbon;
$this->_charts->shouldReceive('categories')->once()->with($start, $end)->andReturn([]);
$this->session(['start' => $start, 'end' => $end]);
$this->action('GET', 'ChartController@homeCategories');
$this->assertResponseOk();
}
}

View File

@@ -1,142 +0,0 @@
<?php
use Carbon\Carbon as Carbon;
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class HomeControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class HomeControllerTest extends TestCase
{
protected $_accounts;
protected $_repository;
protected $_preferences;
protected $_journals;
protected $_reminders;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_accounts = $this->mock('Firefly\Helper\Controllers\AccountInterface');
$this->_repository = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_preferences = $this->mock('Firefly\Helper\Preferences\PreferencesHelperInterface');
$this->_journals = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$this->_reminders = $this->mock('Firefly\Storage\Reminder\ReminderRepositoryInterface');
}
public function tearDown()
{
Mockery::close();
}
public function testFlush()
{
$this->action('GET', 'HomeController@flush');
$this->assertRedirectedToRoute('index');
}
public function testIndex()
{
// mock preference:
$preference = $this->mock('Preference');
$preference->shouldReceive('getAttribute')->with('data')->andReturn([]);
Event::shouldReceive('fire')->with('limits.check');
Event::shouldReceive('fire')->with('piggybanks.check');
Event::shouldReceive('fire')->with('recurring.check');
$this->_reminders->shouldReceive('getCurrentRecurringReminders')->once()->andReturn([]);
// mock accounts:
$this->_repository->shouldReceive('count')->once()->andReturn(0);
$this->_repository->shouldReceive('getActiveDefault')->once()->andReturn([]);
// mock preferences:
$this->_preferences->shouldReceive('get')->with('frontpageAccounts', [])->andReturn($preference);
$this->action('GET', 'HomeController@index');
$this->assertResponseOk();
}
public function testIndexWithAccount()
{
$account = f::create('Account');
$start = new Carbon;
$end = new Carbon;
$this->session(['start' => $start, 'end' => $end]);
// mock preference:
$preference = $this->mock('Preference');
$preference->shouldReceive('getAttribute')->with('data')->andReturn([$account->id]);
Event::shouldReceive('fire')->with('limits.check');
Event::shouldReceive('fire')->with('piggybanks.check');
Event::shouldReceive('fire')->with('recurring.check');
$this->_reminders->shouldReceive('getCurrentRecurringReminders')->once()->andReturn([]);
// mock accounts:
$this->_repository->shouldReceive('count')->once()->andReturn(0);
$this->_repository->shouldReceive('getByIds')->with([$account->id])->once()->andReturn([$account]);
// mock preferences:
$this->_preferences->shouldReceive('get')->with('frontpageAccounts', [])->andReturn($preference);
// mock journals:
$this->_journals->shouldReceive('getByAccountInDateRange')->once()->with($account, 10, $start, $end)->andReturn(
[1, 2]
);
$this->action('GET', 'HomeController@index');
$this->assertResponseOk();
}
public function testIndexWithAccounts()
{
$accountOne = f::create('Account');
$accountTwo = f::create('Account');
$accounThree = f::create('Account');
$set = [$accountOne, $accountTwo, $accounThree];
$ids = [$accountOne->id, $accountTwo->id, $accounThree->id];
$start = new Carbon;
$end = new Carbon;
$this->session(['start' => $start, 'end' => $end]);
// mock preference:
$preference = $this->mock('Preference');
$preference->shouldReceive('getAttribute')->with('data')->andReturn($ids);
Event::shouldReceive('fire')->with('limits.check');
Event::shouldReceive('fire')->with('piggybanks.check');
Event::shouldReceive('fire')->with('recurring.check');
$this->_reminders->shouldReceive('getCurrentRecurringReminders')->once()->andReturn([]);
// mock accounts:
$this->_repository->shouldReceive('count')->once()->andReturn(0);
$this->_repository->shouldReceive('getByIds')->with($ids)->once()->andReturn(
$set
);
// mock preferences:
$this->_preferences->shouldReceive('get')->with('frontpageAccounts', [])->andReturn($preference);
// mock journals:
$this->_journals->shouldReceive('getByAccountInDateRange')->andReturn([1, 2]);
$this->action('GET', 'HomeController@index');
$this->assertResponseOk();
}
}

View File

@@ -1,45 +0,0 @@
<?php
use League\FactoryMuffin\Facade as f;
/**
* Class JsonControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class JsonControllerTest extends TestCase
{
protected $_accounts;
protected $_categories;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_categories = $this->mock('Firefly\Storage\Category\CategoryRepositoryInterface');
}
public function tearDown()
{
Mockery::close();
}
public function testBeneficiaries()
{
$beneficiary = f::create('Account');
$this->_accounts->shouldReceive('getBeneficiaries')->once()->andReturn([$beneficiary]);
$this->action('GET', 'JsonController@beneficiaries');
$this->assertResponseOk();
}
public function testCategories()
{
$category = f::create('Category');
$this->_categories->shouldReceive('get')->once()->andReturn([$category]);
$this->action('GET', 'JsonController@categories');
$this->assertResponseOk();
}
}

View File

@@ -1,245 +0,0 @@
<?php
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class LimitControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class LimitControllerTest extends TestCase
{
protected $_budgets;
protected $_limits;
protected $_user;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_user = m::mock('User', 'Eloquent');
$this->_budgets = $this->mock('Firefly\Storage\Budget\BudgetRepositoryInterface');
$this->_limits = $this->mock('Firefly\Storage\Limit\LimitRepositoryInterface');
}
public function tearDown()
{
Mockery::close();
}
public function testCreate()
{
$this->_budgets->shouldReceive('getAsSelectList')->andReturn([]);
$this->action('GET', 'LimitController@create');
$this->assertResponseOk();
}
public function testDelete()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'LimitController@delete', $limit->id);
$this->assertResponseOk();
}
public function testDestroy()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_limits->shouldReceive('destroy')->once()->andReturn(true);
$this->action('POST', 'LimitController@destroy', $limit->id);
$this->assertResponseStatus(302);
}
public function testDestroyFails()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_limits->shouldReceive('destroy')->once()->andReturn(false);
$this->action('POST', 'LimitController@destroy', $limit->id);
$this->assertResponseStatus(302);
}
public function testDestroyRedirect()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_limits->shouldReceive('destroy')->once()->andReturn(true);
$this->action('POST', 'LimitController@destroy', [$limit->id, 'from' => 'date']);
$this->assertResponseStatus(302);
}
public function testEdit()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
$this->_budgets->shouldReceive('getAsSelectList')->andReturn([]);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'LimitController@edit', $limit->id);
$this->assertResponseOk();
}
public function testStore()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
$this->_limits->shouldReceive('store')->once()->andReturn($limit);
$this->action('POST', 'LimitController@store');
$this->assertRedirectedToRoute('budgets.index.budget');
$this->assertResponseStatus(302);
}
public function testStoreFails()
{
$budget = f::create('Budget');
$limit = f::create('Limit');
$limit->budget()->associate($budget);
$limit->save();
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
unset($limit->startdate);
unset($limit->component_id);
$this->_limits->shouldReceive('store')->once()->andReturn($limit);
$this->action('POST', 'LimitController@store', $budget->id);
$this->assertResponseStatus(302);
}
public function testStoreRedirect()
{
$budget = f::create('Budget');
$limit = f::create('Limit');
$limit->budget()->associate($budget);
$limit->save();
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
$this->_limits->shouldReceive('store')->once()->andReturn($limit);
$this->action('POST', 'LimitController@store', [$budget->id, 'from' => 'date']);
$this->assertResponseStatus(302);
}
public function testUpdate()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_limits->shouldReceive('update')->once()->andReturn($limit);
$this->action(
'POST', 'LimitController@update',
[$limit->id,
'date' => '02-02-2012',
'period' => 'monthly',
'repeats' => 0,
'amount' => '0.01'
]
);
$this->assertResponseStatus(302);
}
public function testUpdateFails()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
unset($limit->amount);
$this->_limits->shouldReceive('update')->once()->andReturn($limit);
$this->action(
'POST', 'LimitController@update',
$limit->id
);
$this->assertResponseStatus(302);
}
public function testUpdateRedirect()
{
$limit = f::create('Limit');
$limitrepetition = f::create('LimitRepetition');
$limit->limitrepetitions()->save($limitrepetition);
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($limit->budget()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_limits->shouldReceive('update')->once()->andReturn($limit);
$this->action(
'POST', 'LimitController@update',
[$limit->id,
'date' => '02-02-2012',
'period' => 'monthly',
'repeats' => 0,
'amount' => '0.01',
'from' => 'date'
]
);
$this->assertResponseStatus(302);
}
}

View File

@@ -1,440 +0,0 @@
<?php
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class PiggybankControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class PiggybankControllerTest extends TestCase
{
protected $_accounts;
protected $_piggybanks;
protected $_user;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_user = m::mock('User', 'Eloquent');
$this->_accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface');
$this->_piggybanks = $this->mock('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
}
public function tearDown()
{
m::close();
}
public function testAddMoneyGET()
{
$piggyBank = f::create('Piggybank');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_piggybanks->shouldReceive('leftOnAccount')->andReturn(1);
$this->action('GET', 'PiggybankController@addMoney', $piggyBank->id);
$this->assertResponseOk();
}
public function testCreatePiggybank()
{
$this->_accounts->shouldReceive('getActiveDefaultAsSelectList')->once()->andReturn([]);
$this->action('GET', 'PiggybankController@createPiggybank');
$this->assertResponseOk();
}
public function testCreateRepeated()
{
$this->_accounts->shouldReceive('getActiveDefaultAsSelectList')->once()->andReturn([]);
$this->action('GET', 'PiggybankController@createRepeated');
$this->assertResponseOk();
}
public function testDelete()
{
$piggyBank = f::create('Piggybank');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'PiggybankController@delete', $piggyBank->id);
$this->assertResponseOk();
}
public function testDestroy()
{
$piggyBank = f::create('Piggybank');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_piggybanks->shouldReceive('destroy')->andReturn(true);
Event::shouldReceive('fire')->with('piggybanks.destroy', [$piggyBank]);
$this->action('POST', 'PiggybankController@destroy', $piggyBank->id);
$this->assertResponseStatus(302);
}
public function testEdit()
{
$piggyBank = f::create('Piggybank');
$this->_accounts->shouldReceive('getActiveDefaultAsSelectList')->once()->andReturn([]);
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'PiggybankController@edit', $piggyBank->id);
$this->assertResponseOk();
}
public function testEditRepeated()
{
$piggyBank = f::create('Piggybank');
$piggyBank->repeats = 1;
$piggyBank->save();
$this->_accounts->shouldReceive('getActiveDefaultAsSelectList')->once()->andReturn([]);
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'PiggybankController@edit', $piggyBank->id);
$this->assertResponseOk();
}
public function testIndex()
{
$aOne = f::create('Account');
$aTwo = f::create('Account');
$one = f::create('Piggybank');
$one->account()->associate($aOne);
$two = f::create('Piggybank');
$two->account()->associate($aOne);
$three = f::create('Piggybank');
$three->account()->associate($aTwo);
$this->_piggybanks->shouldReceive('get')->andReturn([$one, $two, $three]);
$this->_piggybanks->shouldReceive('countRepeating')->andReturn(0);
$this->_piggybanks->shouldReceive('leftOnAccount')->andReturn(0);
$this->_piggybanks->shouldReceive('countNonrepeating')->andReturn(0);
Event::shouldReceive('fire')->with('piggybanks.change');
$this->action('GET', 'PiggybankController@index');
$this->assertResponseOk();
}
public function testModifyMoneyAddPOST()
{
$piggyBank = f::create('Piggybank');
$piggyBank->targetamount = 200;
$piggyBank->save();
$input = [
$piggyBank->id,
'amount' => 10.0,
'what' => 'add'
];
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
Event::shouldReceive('fire'); //->with('piggybanks.modifyAmountAdd', [$piggyBank, 10.0]);
$this->_piggybanks->shouldReceive('modifyAmount')->once();
$this->_piggybanks->shouldReceive('leftOnAccount')->once()->andReturn(200);
$this->action('POST', 'PiggybankController@modMoney', $input);
$this->assertSessionHas('success');
$this->assertResponseStatus(302);
}
public function testModifyMoneyAddPOSTFails()
{
$piggyBank = f::create('Piggybank');
$piggyBank->targetamount = 200;
$piggyBank->save();
$input = [
$piggyBank->id,
'amount' => 10.0,
'what' => 'add'
];
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($piggyBank->account()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
Event::shouldReceive('fire')->with('piggybanks.modifyAmountAdd', [$piggyBank, -10.0]);
$this->_piggybanks->shouldReceive('leftOnAccount')->once()->andReturn(5);
$this->action('POST', 'PiggybankController@modMoney', $input);
$this->assertSessionHas('warning');
$this->assertResponseStatus(302);
}
/**
* @expectedException \Firefly\Exception\FireflyException
*/
public function testModifyMoneyPOSTException()
{
$piggyBank = f::create('Piggybank');
$piggyBank->targetamount = 200;
$piggyBank->save();
$input = [
$piggyBank->id,
'amount' => 10.0,
'what' => 'yomoma'
];
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($piggyBank->account()->first()->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('POST', 'PiggybankController@modMoney', $input);
$this->assertSessionHas('warning');
$this->assertResponseStatus(302);
}
public function testModifyMoneyRemovePOST()
{
$pig = $this->mock('Piggybank');
$piggybank = f::create('Piggybank');
$rep = f::create('PiggybankRepetition');
$rep->piggybank_id = $piggybank->id;
$rep->save();
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$rep->piggybank()->first()->account()->first()->user_id
);
$pig->shouldReceive('currentRelevantRep')->andReturn($rep);
$this->_piggybanks->shouldReceive('leftOnAccount')->andReturn(11);
$this->_piggybanks->shouldReceive('modifyAmount')->once();
$input = [
$rep->piggybank()->first()->id,
'amount' => 10.0,
'what' => 'remove'
];
$this->action('POST', 'PiggybankController@modMoney', $input);
$this->assertSessionHas('success');
$this->assertResponseStatus(302);
}
public function testModifyMoneyRemovePOSTFails()
{
$pig = $this->mock('Piggybank');
$piggybank = f::create('Piggybank');
$rep = f::create('PiggybankRepetition');
$rep->piggybank_id = $piggybank->id;
$rep->currentAmount = 5;
$rep->save();
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$rep->piggybank()->first()->account()->first()->user_id
);
$pig->shouldReceive('currentRelevantRep')->andReturn($rep);
$input = [
$rep->piggybank()->first()->id,
'amount' => 10.0,
'what' => 'remove'
];
$this->action('POST', 'PiggybankController@modMoney', $input);
$this->assertSessionHas('warning');
$this->assertResponseStatus(302);
}
public function testRemoveMoneyGET()
{
$pig = $this->mock('Piggybank');
$piggybank = f::create('Piggybank');
$rep = f::create('PiggybankRepetition');
$rep->piggybank_id = $piggybank->id;
$rep->save();
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$rep->piggybank()->first()->account()->first()->user_id
);
$pig->shouldReceive('currentRelevantRep')->andReturn($rep);
$this->_piggybanks->shouldReceive('leftOnAccount')->andReturn(1)->once();
$this->action('GET', 'PiggybankController@removeMoney', $piggybank->id);
$this->assertResponseOk();
}
public function testShow()
{
$pig = $this->mock('Piggybank');
$piggybank = f::create('Piggybank');
$rep = f::create('PiggybankRepetition');
$rep->piggybank_id = $piggybank->id;
$rep->save();
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn(
$piggybank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->andReturn('some@email');
$pig->shouldReceive('currentRelevantRep')->andReturn($rep);
// repos:
$this->_piggybanks->shouldReceive('leftOnAccount');
$this->action('GET', 'PiggybankController@show', $piggybank->id);
$this->assertResponseOk();
}
public function testStoreRepeated()
{
$piggy = f::create('Piggybank');
$piggy->repeats = 1;
$piggy->save();
Event::shouldReceive('fire')->with('piggybanks.store', [$piggy])->once();
$this->_piggybanks->shouldReceive('store')->once()->andReturn($piggy);
$this->action('POST', 'PiggybankController@storeRepeated');
$this->assertResponseStatus(302);
}
public function testStoreRepeatedFails()
{
$piggy = f::create('Piggybank');
unset($piggy->id);
$this->_piggybanks->shouldReceive('store')->once()->andReturn($piggy);
$this->action('POST', 'PiggybankController@storeRepeated');
$this->assertResponseStatus(302);
}
public function testUpdate()
{
$piggyBank = f::create('Piggybank');
$this->_piggybanks->shouldReceive('update')->andReturn($piggyBank);
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
Event::shouldReceive('fire')->with('piggybanks.update', [$piggyBank]);
$this->action('POST', 'PiggybankController@update', $piggyBank->id);
$this->assertResponseStatus(302);
}
public function testUpdateFails()
{
$piggyBank = f::create('Piggybank');
unset($piggyBank->name);
$this->_piggybanks->shouldReceive('update')->andReturn($piggyBank);
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn(
$piggyBank->account()->first()->user_id
);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
Event::shouldReceive('fire')->with('piggybanks.change');
$this->action('POST', 'PiggybankController@update', $piggyBank->id);
$this->assertResponseStatus(302);
}
public function teststorePiggybank()
{
$piggy = f::create('Piggybank');
$piggy->repeats = 0;
$piggy->save();
Event::shouldReceive('fire')->with('piggybanks.store', [$piggy])->once();
$this->_piggybanks->shouldReceive('store')->once()->andReturn($piggy);
$this->action('POST', 'PiggybankController@storePiggybank');
$this->assertResponseStatus(302);
}
public function teststorePiggybankFails()
{
$piggy = f::create('Piggybank');
unset($piggy->id);
$this->_piggybanks->shouldReceive('store')->once()->andReturn($piggy);
$this->action('POST', 'PiggybankController@storePiggybank');
$this->assertResponseStatus(302);
}
}

View File

@@ -1,56 +0,0 @@
<?php
use Mockery as m;
/**
* Class PreferencesControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class PreferencesControllerTest extends TestCase
{
protected $_user;
protected $_helper;
protected $_accounts;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_user = m::mock('User', 'Eloquent');
$this->_helper = $this->mock('Firefly\Helper\Preferences\PreferencesHelperInterface');
$this->_accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface');
}
public function tearDown()
{
m::close();
}
public function testIndex()
{
$viewRange = $this->mock('Preference');
$viewRange->shouldReceive('getAttribute')->with('data')->andReturn('1M');
$this->_accounts->shouldReceive('getDefault')->andReturn([]);
$this->_helper->shouldReceive('get')->with('viewRange', '1M')->andReturn($viewRange);
$this->_helper->shouldReceive('get')->with('frontpageAccounts', [])->andReturn([]);
$this->action('GET', 'PreferencesController@index');
$this->assertResponseOk();
}
public function testPostIndex()
{
$this->_helper->shouldReceive('set')->with('frontpageAccounts', [1]);
$this->_helper->shouldReceive('set')->with('viewRange', '1M');
$this->action('POST', 'PreferencesController@postIndex', ['frontpageAccounts' => [1], 'viewRange' => '1M']);
$this->assertResponseStatus(302);
}
}

View File

@@ -1,135 +0,0 @@
<?php
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class ProfileControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class ProfileControllerTest extends TestCase
{
protected $_user;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_user = m::mock('User', 'Eloquent');
}
public function tearDown()
{
m::close();
}
public function testChangePassword()
{
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($this->_user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'ProfileController@changePassword');
$this->assertResponseOk();
}
public function testIndex()
{
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($this->_user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'ProfileController@index');
$this->assertResponseOk();
}
public function testPostChangePasswordDifferentNew()
{
$user = f::create('User');
// for binding
Auth::shouldReceive('user')->andReturn($user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn($user->email);
$this->_user->shouldReceive('getAttribute')->with('password')->andReturn($user->password);
$this->action(
'POST', 'ProfileController@postChangePassword',
['old' => 'sander', 'new1' => 'sander1', 'new2' => 'sander2']
);
$this->assertResponseOk();
}
public function testPostChangePasswordOK()
{
$user = f::create('User');
$user->password = 'sander';
$user->save();
// for binding
Auth::shouldReceive('user')->andReturn($user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn($user->email);
$this->_user->shouldReceive('getAttribute')->with('password')->andReturn($user->password);
$this->action(
'POST', 'ProfileController@postChangePassword',
['old' => 'sander', 'new1' => 'sander2', 'new2' => 'sander2']
);
$this->assertSessionHas('success');
$this->assertResponseStatus(302);
}
public function testPostChangePasswordNoCurrent()
{
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($this->_user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_user->shouldReceive('getAttribute')->with('password')->andReturn('Blablabla');
$this->action('POST', 'ProfileController@postChangePassword', ['old' => '']);
$this->assertResponseOk();
}
public function testPostChangePasswordNoMatchNew()
{
$user = f::create('User');
// for binding
Auth::shouldReceive('user')->andReturn($user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn($user->email);
$this->_user->shouldReceive('getAttribute')->with('password')->andReturn($user->password);
$this->action(
'POST', 'ProfileController@postChangePassword', ['old' => 'sander', 'new1' => 'sander', 'new2' => 'sander']
);
$this->assertResponseOk();
}
public function testPostChangePasswordSame()
{
$user = f::create('User');
// for binding
Auth::shouldReceive('user')->andReturn($user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($user->id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn($user->email);
$this->_user->shouldReceive('getAttribute')->with('password')->andReturn($user->password);
$this->action('POST', 'ProfileController@postChangePassword', ['old' => 'sander']);
$this->assertResponseOk();
}
}

View File

@@ -1,178 +0,0 @@
<?php
use League\FactoryMuffin\Facade as f;
use Mockery as m;
/**
* Class RecurringControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class RecurringControllerTest extends TestCase
{
protected $_user;
protected $_repository;
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
Artisan::call('db:seed');
$this->_user = m::mock('User', 'Eloquent');
$this->_repository = $this->mock(
'Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface'
);
}
public function tearDown()
{
m::close();
}
public function testCreate()
{
$this->action('GET', 'RecurringController@create');
$this->assertResponseOk();
}
public function testDelete()
{
$recurringTransaction = f::create('RecurringTransaction');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($recurringTransaction->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'RecurringController@delete', $recurringTransaction->id);
$this->assertResponseOk();
}
public function testDestroy()
{
$recurringTransaction = f::create('RecurringTransaction');
Event::shouldReceive('fire')->with('recurring.destroy',m::any());
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($recurringTransaction->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_repository->shouldReceive('destroy')->andReturn(true);
$this->action('POST', 'RecurringController@destroy', $recurringTransaction->id);
$this->assertResponseStatus(302);
}
public function testDestroyFails()
{
$recurringTransaction = f::create('RecurringTransaction');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
Event::shouldReceive('fire')->with('recurring.destroy',m::any());
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($recurringTransaction->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->_repository->shouldReceive('destroy')->andReturn(false);
$this->action('POST', 'RecurringController@destroy', $recurringTransaction->id);
$this->assertResponseStatus(302);
}
public function testEdit()
{
$recurringTransaction = f::create('RecurringTransaction');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($recurringTransaction->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn('some@email');
$this->action('GET', 'RecurringController@edit', $recurringTransaction->id);
$this->assertResponseOk();
}
public function testIndex()
{
$this->_repository->shouldReceive('get')->andReturn([]);
$this->action('GET', 'RecurringController@index');
$this->assertResponseOk();
}
public function testShow()
{
$recurringTransaction = f::create('RecurringTransaction');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($recurringTransaction->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
$this->action('GET', 'RecurringController@show', $recurringTransaction->id);
$this->assertResponseOk();
}
public function testStore()
{
$recurringTransaction = f::create('RecurringTransaction');
Event::shouldReceive('fire')->with('recurring.store',m::any());
$this->_repository->shouldReceive('store')->andReturn($recurringTransaction);
$this->action('POST', 'RecurringController@store');
$this->assertResponseStatus(302);
}
public function testStoreRedirect()
{
$recurringTransaction = f::create('RecurringTransaction');
Event::shouldReceive('fire')->with('recurring.store',m::any());
$this->_repository->shouldReceive('store')->andReturn($recurringTransaction);
$this->action('POST', 'RecurringController@store', ['create' => '1']);
$this->assertResponseStatus(302);
}
public function testStoreFails()
{
$recurringTransaction = f::create('RecurringTransaction');
unset($recurringTransaction->active);
unset($recurringTransaction->automatch);
$this->_repository->shouldReceive('store')->andReturn($recurringTransaction);
$this->action('POST', 'RecurringController@store', ['create' => '1']);
$this->assertResponseStatus(302);
}
public function testUpdate()
{
$recurringTransaction = f::create('RecurringTransaction');
// for binding
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->andReturn($recurringTransaction->user_id);
$this->_user->shouldReceive('getAttribute')->with('email')->andReturn('some@email');
Event::shouldReceive('fire')->with('recurring.update',m::any());
$this->_repository->shouldReceive('update')->andReturn($recurringTransaction);
$this->action('POST', 'RecurringController@update', $recurringTransaction->id);
$this->assertResponseStatus(302);
}
}

View File

@@ -1,30 +0,0 @@
<?php
use Mockery as m;
/**
* Class ReportControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class ReportControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
}
public function tearDown()
{
m::close();
}
public function testIndex()
{
$this->action('GET', 'ReportController@index');
$this->assertResponseOk();
}
}

View File

@@ -1,30 +0,0 @@
<?php
use Mockery as m;
/**
* Class SearchControllerTest
*
* @SuppressWarnings(PHPMD.TooManyMethods)
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class SearchControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
}
public function tearDown()
{
m::close();
}
public function testIndex()
{
$this->action('GET', 'SearchController@index');
$this->assertResponseOk();
}
}

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