Compare commits

..

13 Commits
3.0.2 ... 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
33 changed files with 347 additions and 323 deletions

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ _ide_helper.php
index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json

View File

@@ -13,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.
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.
A lot of views have CSRF vulnerabilities. The general advice is NOT to use this tool in production.
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!

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

@@ -8,8 +8,6 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class AccountController extends \BaseController
{
@@ -18,7 +16,7 @@ class AccountController extends \BaseController
/**
* @param ARI $repository
* @param AI $accounts
* @param AI $accounts
*/
public function __construct(ARI $repository, AI $accounts)
{
@@ -42,7 +40,7 @@ class AccountController extends \BaseController
public function delete(Account $account)
{
return View::make('accounts.delete')->with('account', $account)
->with('title', 'Delete account "' . $account->name . '"');
->with('title', 'Delete account "' . $account->name . '"');
}
/**
@@ -69,7 +67,7 @@ class AccountController extends \BaseController
{
$openingBalance = $this->_accounts->openingBalanceTransaction($account);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)
->with('title', 'Edit account "' . $account->name . '"');
->with('title', 'Edit account "' . $account->name . '"');
}
/**
@@ -105,8 +103,10 @@ class AccountController extends \BaseController
{
$data = $this->_accounts->show($account, 40);
return View::make('accounts.show')->with('account', $account)->with('show', $data)->with('title',
'Details for account "' . $account->name . '"');
return View::make('accounts.show')->with('account', $account)->with('show', $data)->with(
'title',
'Details for account "' . $account->name . '"'
);
}
/**

View File

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

View File

@@ -8,6 +8,7 @@ use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
* Class BudgetController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
*/
class BudgetController extends BaseController
@@ -17,7 +18,7 @@ class BudgetController extends BaseController
protected $_repository;
/**
* @param BI $budgets
* @param BI $budgets
* @param BRI $repository
*/
public function __construct(BI $budgets, BRI $repository)
@@ -44,7 +45,7 @@ class BudgetController extends BaseController
public function delete(Budget $budget)
{
return View::make('budgets.delete')->with('budget', $budget)
->with('title', 'Delete budget "' . $budget->name . '"');
->with('title', 'Delete budget "' . $budget->name . '"');
}
/**
@@ -75,7 +76,7 @@ class BudgetController extends BaseController
public function edit(Budget $budget)
{
return View::make('budgets.edit')->with('budget', $budget)
->with('title', 'Edit budget "' . $budget->name . '"');
->with('title', 'Edit budget "' . $budget->name . '"');
}
@@ -87,7 +88,7 @@ class BudgetController extends BaseController
$budgets = $this->_repository->get();
return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon)
->with('title', 'All your budgets grouped by budget');
->with('title', 'All your budgets grouped by budget');
}
@@ -101,7 +102,7 @@ class BudgetController extends BaseController
$budgets = $this->_budgets->organizeByDate($set);
return View::make('budgets.indexByDate')->with('budgets', $budgets)
->with('title', 'All your budgets grouped by date');
->with('title', 'All your budgets grouped by date');
}
@@ -113,7 +114,7 @@ class BudgetController extends BaseController
* - Show a specific repetition.
* - Show everything shows NO repetition.
*
* @param Budget $budget
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return int
@@ -128,8 +129,10 @@ class BudgetController extends BaseController
case (!is_null($repetition)):
$data = $this->_budgets->organizeRepetition($repetition);
$view = 1;
$title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf($repetition->limit->amount,
false);
$title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf(
$repetition->limit->amount,
false
);
break;
case (Input::get('noenvelope') == 'true'):
$data = $this->_budgets->outsideRepetitions($budget);
@@ -144,12 +147,12 @@ class BudgetController extends BaseController
}
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);
->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
{
@@ -13,7 +15,7 @@ class CategoryController extends BaseController
/**
* @param CRI $repository
* @param CI $category
* @param CI $category
*/
public function __construct(CRI $repository, CI $category)
{
@@ -37,7 +39,7 @@ class CategoryController extends BaseController
public function delete(Category $category)
{
return View::make('categories.delete')->with('category', $category)
->with('title', 'Delete category "' . $category->name . '"');
->with('title', 'Delete category "' . $category->name . '"');
}
/**
@@ -88,8 +90,8 @@ class CategoryController extends BaseController
$journals = $this->_category->journalsInRange($category, $start, $end);
return View::make('categories.show')->with('category', $category)->with('journals', $journals)->with(
'highlight', Input::get('highlight')
)->with('title', 'Overview for category "'.$category->name.'"');;
'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
{
@@ -143,15 +146,15 @@ class ChartController extends BaseController
public function budgetNoLimits(\Budget $budget)
{
/*
* We can go about this two ways. Either we find all transactions which definitely are IN an envelope
* and exclude them or we search for transactions outside of the range of any of the envelopes we have.
* 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 shitty we go with the first one because it's easier to build.
* Since either is kinda shitty Firefly uses the first one because it's easier to build.
*/
$inRepetitions = $this->_chart->allJournalsInBudgetEnvelope($budget);
/*
* With this set of id's, we can search for all journals NOT in that set.
* 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);
@@ -200,7 +203,6 @@ class ChartController extends BaseController
$start = clone Session::get('start');
/*
* Expenses per day in the session's period. That's easy.
*/
@@ -227,14 +229,15 @@ class ChartController extends BaseController
$reps = $this->_chart->limitsInRange($budget, $start, $end);
/*
* For each limitrepetition we create a serie that contains the amount left in
* 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: we have a session date from Jan 15 to Jan 30. The limitrepetition starts at 1 Jan until 1 Feb.
* So example: the user has a session date from Jan 15 to Jan 30. The limitrepetition
* starts at 1 Jan until 1 Feb.
*
* We loop from 1 Jan to 1 Feb but only include Jan 15 / Jan 30. But we do keep count of the amount outside
* of these dates because otherwise the line might be wrong.
* 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) {
@@ -245,7 +248,7 @@ class ChartController extends BaseController
'type' => 'spline',
'id' => 'rep-' . $repetition->id,
'yAxis' => 1,
'name' => 'Envelope #'.$repetition->id.' in ' . $repetition->periodShow(),
'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(),
'data' => []
];
$current = clone $repetition->startdate;

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,12 +16,18 @@ 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;
$this->_accounts = $accounts;
$this->_preferences = $preferences;
$this->_journal = $journal;
$this->_reminders = $reminders;
$this->_journal = $journal;
$this->_reminders = $reminders;
}
/**
@@ -38,20 +45,14 @@ class HomeController extends BaseController
*/
public function index()
{
// Queue::push(function($job)
// {
// Log::debug('This is a job!');
// });
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');
$end = Session::get('end');
// get the preference for the home accounts to show:
@@ -78,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
{
@@ -24,9 +26,9 @@ class JsonController extends BaseController
public function __construct(ARI $accounts, CRI $components, Cat $categories, Bud $budgets)
{
$this->_components = $components;
$this->_accounts = $accounts;
$this->_accounts = $accounts;
$this->_categories = $categories;
$this->_budgets = $budgets;
$this->_budgets = $budgets;
}
/**
@@ -34,7 +36,7 @@ class JsonController extends BaseController
*/
public function beneficiaries()
{
$list = $this->_accounts->getBeneficiaries();
$list = $this->_accounts->getBeneficiaries();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
@@ -49,7 +51,7 @@ class JsonController extends BaseController
*/
public function categories()
{
$list = $this->_categories->get();
$list = $this->_categories->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;

View File

@@ -5,6 +5,8 @@ use Firefly\Storage\Limit\LimitRepositoryInterface as LRI;
/**
* Class LimitController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class LimitController extends BaseController
{
@@ -19,7 +21,7 @@ class LimitController extends BaseController
public function __construct(BRI $budgets, LRI $limits)
{
$this->_budgets = $budgets;
$this->_limits = $limits;
$this->_limits = $limits;
}
/**
@@ -29,7 +31,7 @@ class LimitController extends BaseController
*/
public function create(\Budget $budget = null)
{
$periods = \Config::get('firefly.periods_to_text');
$periods = \Config::get('firefly.periods_to_text');
$prefilled = [
'startdate' => \Input::get('startdate') ? : date('Y-m-d'),
'repeat_freq' => \Input::get('repeat_freq') ? : 'monthly',
@@ -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!');
@@ -110,7 +112,7 @@ class LimitController extends BaseController
}
} else {
Session::flash('error', 'Could not save new envelope.');
$budgetId = $budget ? $budget->id : null;
$budgetId = $budget ? $budget->id : null;
$parameters = [$budgetId, 'from' => Input::get('from')];
return Redirect::route('budgets.limits.create', $parameters)->withInput()

View File

@@ -27,10 +27,18 @@ class MigrateController extends BaseController
$fullName = $path . DIRECTORY_SEPARATOR . $fileName;
if (is_writable($path)) {
Input::file('file')->move($path, $fileName);
// so now we push something in a queue and do something with it! Yay!
// 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]);
Session::flash('success', 'The import job has been queued. Please be patient. Data will appear');
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.');

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
{
@@ -21,16 +25,18 @@ class PiggybankController extends BaseController
public function __construct(PRI $repository, ARI $accounts)
{
$this->_repository = $repository;
$this->_accounts = $accounts;
$this->_accounts = $accounts;
}
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function addMoney(Piggybank $piggyBank)
{
$what = 'add';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$what = 'add';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$maxRemove = null;
return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with(
@@ -43,7 +49,7 @@ class PiggybankController extends BaseController
*/
public function createPiggybank()
{
$periods = Config::get('firefly.piggybank_periods');
$periods = Config::get('firefly.piggybank_periods');
$accounts = $this->_accounts->getActiveDefaultAsSelectList();
return View::make('piggybanks.create-piggybank')->with('accounts', $accounts)->with('periods', $periods);
@@ -54,7 +60,7 @@ class PiggybankController extends BaseController
*/
public function createRepeated()
{
$periods = Config::get('firefly.piggybank_periods');
$periods = Config::get('firefly.piggybank_periods');
$accounts = $this->_accounts->getActiveDefaultAsSelectList();
return View::make('piggybanks.create-repeated')->with('accounts', $accounts)->with('periods', $periods);
@@ -93,7 +99,7 @@ class PiggybankController extends BaseController
public function edit(Piggybank $piggyBank)
{
$accounts = $this->_accounts->getActiveDefaultAsSelectList();
$periods = Config::get('firefly.piggybank_periods');
$periods = Config::get('firefly.piggybank_periods');
if ($piggyBank->repeats == 1) {
return View::make('piggybanks.edit-repeated')->with('piggybank', $piggyBank)->with('accounts', $accounts)
->with('periods', $periods);
@@ -110,19 +116,19 @@ class PiggybankController extends BaseController
*/
public function index()
{
$countRepeating = $this->_repository->countRepeating();
$countRepeating = $this->_repository->countRepeating();
$countNonRepeating = $this->_repository->countNonrepeating();
$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 = [];
/** @var \Piggybank $piggybank */
foreach ($piggybanks as $piggybank) {
$account = $piggybank->account;
$id = $account->id;
$id = $account->id;
if (!isset($accounts[$id])) {
$accounts[$id] = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)];
}
@@ -158,7 +164,7 @@ class PiggybankController extends BaseController
}
break;
case 'remove':
$rep = $piggyBank->currentRelevantRep();
$rep = $piggyBank->currentRelevantRep();
$maxRemove = $rep->currentamount;
if (round($amount, 2) <= round($maxRemove, 2)) {
Session::flash('success', 'Amount updated!');
@@ -175,11 +181,13 @@ class PiggybankController extends BaseController
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function removeMoney(Piggybank $piggyBank)
{
$what = 'remove';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$what = 'remove';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$maxRemove = $piggyBank->currentRelevantRep()->currentamount;
return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with(
@@ -193,7 +201,7 @@ class PiggybankController extends BaseController
public function show(Piggybank $piggyBank)
{
$leftOnAccount = $this->_repository->leftOnAccount($piggyBank->account);
$balance = $piggyBank->account->balance();
$balance = $piggyBank->account->balance();
return View::make('piggybanks.show')->with('piggyBank', $piggyBank)->with('leftOnAccount', $leftOnAccount)
->with('balance', $balance);
@@ -208,10 +216,10 @@ class PiggybankController extends BaseController
unset($data['_token']);
// extend the data array with the settings needed to create a piggy bank:
$data['repeats'] = 0;
$data['repeats'] = 0;
$data['rep_times'] = 1;
$data['rep_every'] = 1;
$data['order'] = 0;
$data['order'] = 0;
$piggyBank = $this->_repository->store($data);
if (!is_null($piggyBank->id)) {
@@ -240,7 +248,7 @@ class PiggybankController extends BaseController
// extend the data array with the settings needed to create a repeated:
$data['repeats'] = 1;
$data['order'] = 0;
$data['order'] = 0;
$piggyBank = $this->_repository->store($data);
if ($piggyBank->id) {
@@ -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
{
@@ -18,7 +20,7 @@ class PreferencesController extends BaseController
public function __construct(ARI $accounts, PHI $preferences)
{
$this->_accounts = $accounts;
$this->_accounts = $accounts;
$this->_preferences = $preferences;
}
@@ -29,7 +31,7 @@ class PreferencesController extends BaseController
{
$accounts = $this->_accounts->getDefault();
$viewRange = $this->_preferences->get('viewRange', '1M');
$viewRange = $this->_preferences->get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
// pref:

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;
@@ -33,8 +38,8 @@ class ReminderController extends BaseController
*/
public function modalDialog()
{
$today = new Carbon;
$reminders = $this->_repository->get();
$today = new Carbon;
$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
{
@@ -30,18 +32,18 @@ class TransactionController extends BaseController
// get accounts with names and id's.
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts = $accountRepository->getActiveDefaultAsSelectList();
$accounts = $accountRepository->getActiveDefaultAsSelectList();
// get budgets as a select list.
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */
$budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$budgets = $budgetRepository->getAsSelectList();
$budgets[0] = '(no budget)';
$budgets = $budgetRepository->getAsSelectList();
$budgets[0] = '(no budget)';
// get the piggy banks.
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
$piggies = $piggyRepository->get();
$piggies = $piggyRepository->get();
return View::make('transactions.create')->with('accounts', $accounts)->with('budgets', $budgets)->with(
'what', $what
@@ -88,18 +90,18 @@ class TransactionController extends BaseController
// get accounts with names and id's.
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts = $accountRepository->getActiveDefaultAsSelectList();
$accounts = $accountRepository->getActiveDefaultAsSelectList();
// get budgets as a select list.
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */
$budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$budgets = $budgetRepository->getAsSelectList();
$budgets[0] = '(no budget)';
$budgets = $budgetRepository->getAsSelectList();
$budgets[0] = '(no budget)';
// get the piggy banks.
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
$piggies = $piggyRepository->get();
$piggies = $piggyRepository->get();
// piggy bank id?
$piggyBankId = null;
foreach ($journal->transactions as $t) {
@@ -107,7 +109,7 @@ class TransactionController extends BaseController
}
// data to properly display form:
$data = [
$data = [
'date' => $journal->date->format('Y-m-d'),
'category' => '',
'budget_id' => 0,
@@ -119,23 +121,23 @@ class TransactionController extends BaseController
}
switch ($journal->transactiontype->type) {
case 'Withdrawal':
$data['account_id'] = $journal->transactions[0]->account->id;
$data['account_id'] = $journal->transactions[0]->account->id;
$data['beneficiary'] = $journal->transactions[1]->account->name;
$data['amount'] = floatval($journal->transactions[1]->amount);
$budget = $journal->budgets()->first();
$data['amount'] = floatval($journal->transactions[1]->amount);
$budget = $journal->budgets()->first();
if (!is_null($budget)) {
$data['budget_id'] = $budget->id;
}
break;
case 'Deposit':
$data['account_id'] = $journal->transactions[1]->account->id;
$data['account_id'] = $journal->transactions[1]->account->id;
$data['beneficiary'] = $journal->transactions[0]->account->name;
$data['amount'] = floatval($journal->transactions[1]->amount);
$data['amount'] = floatval($journal->transactions[1]->amount);
break;
case 'Transfer':
$data['account_from_id'] = $journal->transactions[1]->account->id;
$data['account_to_id'] = $journal->transactions[0]->account->id;
$data['amount'] = floatval($journal->transactions[1]->amount);
$data['account_to_id'] = $journal->transactions[0]->account->id;
$data['amount'] = floatval($journal->transactions[1]->amount);
break;
}
@@ -150,15 +152,15 @@ class TransactionController extends BaseController
public function index()
{
$start = is_null(Input::get('startdate')) ? null : new Carbon(Input::get('startdate'));
$end = is_null(Input::get('enddate')) ? null : new Carbon(Input::get('enddate'));
$end = is_null(Input::get('enddate')) ? null : new Carbon(Input::get('enddate'));
if ($start <= $end && !is_null($start) && !is_null($end)) {
$journals = $this->_repository->paginate(25, $start, $end);
$filtered = true;
$filters = ['start' => $start, 'end' => $end];
$filters = ['start' => $start, 'end' => $end];
} else {
$journals = $this->_repository->paginate(25);
$filtered = false;
$filters = null;
$filters = null;
}
@@ -192,12 +194,12 @@ class TransactionController extends BaseController
if (Input::get('reminder')) {
/** @var \Firefly\Storage\Reminder\ReminderRepositoryInterface $reminders */
$reminders = App::make('Firefly\Storage\Reminder\ReminderRepositoryInterface');
$reminder = $reminders->find(Input::get('reminder'));
$reminder = $reminders->find(Input::get('reminder'));
$reminders->deactivate($reminder);
}
// trigger the creation for recurring transactions.
Event::fire('journals.store',[$journal]);
Event::fire('journals.store', [$journal]);
if (Input::get('create') == '1') {
return Redirect::route('transactions.create', [$what])->withInput();
@@ -226,7 +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]);
Event::fire('journals.update', [$journal]);
return Redirect::route('transactions.index');
} else {

View File

@@ -17,7 +17,7 @@ class UserController extends BaseController
*/
public function __construct(URI $user, EHI $email)
{
$this->user = $user;
$this->user = $user;
$this->email = $email;
}
@@ -41,11 +41,11 @@ class UserController extends BaseController
public function postLogin()
{
$rememberMe = Input::get('remember_me') == '1';
$data = [
$data = [
'email' => Input::get('email'),
'password' => Input::get('password')
];
$result = Auth::attempt($data, $rememberMe);
$result = Auth::attempt($data, $rememberMe);
if ($result) {
Session::flash('success', 'Logged in!');

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

@@ -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

@@ -28,13 +28,16 @@ class Account implements AccountInterface
* 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, we remove $item and continue like nothing ever happened. This will however,
* mess up some statisics but we can live with that. We might be needing some cleanup routine in the future.
* 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.
*
* For now, we simply warn the user of this.
* Firefly might be needing some cleanup routine in the future.
*
* For now, Firefly simply warns the user of this.
*
* @param \Account $account
* @param $perPage
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)

View File

@@ -25,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();
@@ -550,9 +557,10 @@ class Chart implements ChartInterface
}
/**
* We check how much money has been spend on the limitrepetition (aka: the current envelope) in the period denoted.
* Aka, we have a certain amount of money in an envelope and we wish to know how much we've spent between the dates
* entered. This can be a partial match with the date range of the envelope or no match at all.
* 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
@@ -560,19 +568,21 @@ class Chart implements ChartInterface
*
* @return mixed
*/
public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end) {
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')) ;
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

@@ -98,9 +98,10 @@ interface ChartInterface
/**
* We check how much money has been spend on the limitrepetition (aka: the current envelope) in the period denoted.
* Aka, we have a certain amount of money in an envelope and we wish to know how much we've spent between the dates
* entered. This can be a partial match with the date range of the envelope or no match at all.
* 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

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

@@ -133,7 +133,7 @@ class Import
return;
}
// if we try to import a beneficiary, Firefly will "merge" already,
// 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

View File

@@ -65,6 +65,16 @@ class EloquentReminderRepository implements ReminderRepositoryInterface
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();
}
/**
*
*/

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 = 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->enddate = $current;
$reminder->active = 1;
\Log::debug('Will create a reminder. Is it valid?');
\Log::debug($reminder->validate());
try {
$reminder->save();
$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);
@@ -234,12 +283,12 @@ class EloquentPiggybankTrigger
*/
public function modifyAmountAdd(\Piggybank $piggyBank, $amount)
{
$rep = $piggyBank->currentRelevantRep();
$rep = $piggyBank->currentRelevantRep();
$today = new Carbon;
// create event:
$event = new \PiggybankEvent;
$event->date = new Carbon;
$event = new \PiggybankEvent;
$event->date = new Carbon;
$event->amount = $amount;
$event->piggybank()->associate($piggyBank);
@@ -259,8 +308,8 @@ class EloquentPiggybankTrigger
public function modifyAmountRemove(\Piggybank $piggyBank, $amount)
{
// create event:
$event = new \PiggybankEvent;
$event->date = new Carbon;
$event = new \PiggybankEvent;
$event->date = new Carbon;
$event->amount = $amount;
$event->piggybank()->associate($piggyBank);
$event->save();
@@ -359,7 +408,7 @@ class EloquentPiggybankTrigger
if (!is_null($rep->targetdate)) {
$eventSumQuery->where('date', '<=', $rep->targetdate->format('Y-m-d'));
}
$eventSum = floatval($eventSumQuery->sum('amount'));
$eventSum = floatval($eventSumQuery->sum('amount'));
$rep->currentamount = floatval($sum) + $eventSum;
$rep->save();

View File

@@ -72,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.
*

View File

@@ -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

@@ -33,5 +33,10 @@ class RecurringTransactionReminder extends Reminder
{
protected $isSubclass = true;
public function render()
{
return '123';
}
}

View File

@@ -71,66 +71,6 @@
@endforeach
@endif
@if(count($reminders) > 0)
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h4>Recurring transactions</h4>
<p class="text-info">These transactions are set to be expected between
{{Session::get('start')->format('j F Y')}} and {{Session::get('end')->format('j F Y')}}.</p>
<table class="table">
<tr>
<th>Name</th>
<th>Tags</th>
<th colspan="2">Amount</th>
<th>Repeats</th>
</tr>
<?php $max =0;$min = 0;?>
@foreach($reminders as $reminder)
<?php
$max += $reminder->recurringtransaction->amount_max;
$min += $reminder->recurringtransaction->amount_min;
?>
<tr>
<td>
<a href="{{route('recurring.show',$reminder->recurringtransaction->id)}}">
{{{$reminder->recurringtransaction->name}}}
</a>
</td>
<td>
@foreach(explode(' ',$reminder->recurringtransaction->match) as $word)
<span class="label label-info">{{{$word}}}</span>
@endforeach
</td>
<td>
{{mf($reminder->recurringtransaction->amount_min)}}
</td>
<td>
{{mf($reminder->recurringtransaction->amount_max)}}
</td>
<td>
{{$reminder->recurringtransaction->repeat_freq}}
</td>
<td>
<div class="btn-group btn-group-xs">
<a href="#" class="btn btn-default">postpone</a>
<a href="#" class="btn btn-default">dismiss</a>
<a href="#" class="btn btn-default">done!</a>
</div>
</td>
</tr>
@endforeach
<tr>
<td colspan="2">Sum</td>
<td>{{mf($max)}}</td>
<td colspan="3">{{mf($min)}}</td>
</tr>
</table>
</div>
</div>
@endif
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div id="budgets"></div>

View File

@@ -3,50 +3,36 @@
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
<small>Piggy bank "{{{$piggyBank->name}}}"</small>
<small>
@if($piggyBank->repeats == 1)
Repeated expense
@else
Piggy bank
@endif
"{{{$piggyBank->name}}}"</small>
</h1>
<p>
<a href="{{route('accounts.show',$piggyBank->account_id)}}">{{{$piggyBank->account->name}}}</a> has
a balance of {{mf($balance)}}.
Of that {{mf($balance)}}, you have {{mf($leftOnAccount)}} not yet locked up in other piggy banks.
You can add {{mf(min(max($balance,$leftOnAccount),$piggyBank->targetamount))}} to this piggy bank.
</p>
<p>
<div class="btn-group">
<a href="{{route('piggybanks.edit',$piggyBank->id)}}" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span> Edit</a>
<a href="{{route('piggybanks.delete',$piggyBank->id)}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> Delete</a>
@if(min(max($balance,$leftOnAccount),$piggyBank->targetamount) > 0)
<a data-toggle="modal" href="{{route('piggybanks.amount.add',$piggyBank->id)}}" data-target="#modal" class="btn btn-default"><span class="glyphicon glyphicon-plus-sign"></span> Add money</a>
@endif
@if($piggyBank->currentRelevantRep()->currentamount > 0)
<a data-toggle="modal" href="{{route('piggybanks.amount.remove',$piggyBank->id)}}" data-target="#modal" class="btn btn-default"><span class="glyphicon glyphicon-minus-sign"></span> Remove money</a>
@endif
</p>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<h3>Piggy bank info</h3>
<h3>General information</h3>
<table class="table table-bordered table-striped">
<tr>
<th>Field</th>
<th>Value</th>
</tr>
<tr>
<td>Type (repeats)</td>
<td>
@if($piggyBank->repeats == 1)
Repeated expense
@else
Piggy bank
@endif
</td>
</tr>
<tr>
<td>Name</td>
<td>{{{$piggyBank->name}}} (#{{$piggyBank->id}})</td>
</tr>
<tr>
<td>Account</td>
<td><a href="{{route('accounts.show',$piggyBank->account_id)}}">{{{$piggyBank->account->name}}}</a></td>

View File

@@ -46,12 +46,16 @@
<div class="col-lg-6 col-md-6 col-sm-12">
<h3>Transactions</h3>
@foreach($journal->transactions as $t)
<h4>{{{$t->account->name}}}<br /><small>{{{$t->account->accounttype->description}}}</small></h4>
<h4><a href="{{route('accounts.show',$t->account->id)}}">{{{$t->account->name}}}</a><br /><small>{{{$t->account->accounttype->description}}}</small></h4>
<table class="table">
<tr>
<td>Amount</td>
<td>{{mf($t->amount)}}</td>
</tr>
<tr>
<td>New balance</td>
<td>{{mf($t->account->balanceBeforeJournal($journal))}} &rarr; {{mf($t->account->balanceBeforeJournal($journal) + $t->amount)}}</td>
</tr>
@if(!is_null($t->description))
<tr>
<td>Description</td>