Merge branch 'release/3.3.6'

Conflicts:
	app/Http/Controllers/CategoryController.php
This commit is contained in:
James Cole 2015-04-03 23:31:29 +02:00
commit a115960411
103 changed files with 2651 additions and 1163 deletions

View File

@ -12,5 +12,7 @@ CACHE_DRIVER=file
SESSION_DRIVER=file
EMAIL_SMTP=
EMAIL_DRIVER=smtp
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=

View File

@ -11,3 +11,7 @@ DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
EMAIL_SMTP=
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=ABC

View File

@ -11,12 +11,9 @@ addons:
repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
install:
- rm composer.lock
- composer install
- composer update
- php artisan env
- mv -v .env.testing .env
- touch tests/database/db.sqlite
- php artisan migrate --seed
script:
- phpunit --debug

View File

@ -1,11 +1,12 @@
Firefly III (v3.3.5)
Firefly III (v3.3.6)
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Test Coverage](https://codeclimate.com/github/JC5/firefly-iii/badges/coverage.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.svg?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=master)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.svg?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=develop)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
@ -25,9 +26,14 @@ To install and use Firefly III, please read [the installation guide](https://git
## Current features
- [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- [A double-entry bookkeeping system](https://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_;
- You can manage different types of accounts
- Asset accounts
- Shared asset accounts (household accounts)
- Saving accounts
- Credit cards
- It's possible to create, change and manage money using _[budgets](https://en.wikipedia.org/wiki/Envelope_system)_;
- Organize transactions using categories;
- Save towards a goal using piggy banks;
- Predict and anticipate bills;
@ -48,9 +54,7 @@ Firefly III will feature, but does not feature yet:
- More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts
- Credit cards
- More test-coverage;
- Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control.
- Firefly will be able to join transactions.
@ -73,7 +77,4 @@ Some stuff has been removed:
## Current state
I have the basics up and running. Test coverage is currently coming, slowly.
Although I have not checked extensively, some forms and views have CSRF vulnerabilities. This is because not all
views escape all characters by default. Will be fixed.
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!

View File

@ -1,7 +1,5 @@
<?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
@ -10,24 +8,25 @@ use Illuminate\Queue\SerializesModels;
*
* @package FireflyIII\Events
*/
class JournalCreated extends Event {
class JournalCreated extends Event
{
use SerializesModels;
use SerializesModels;
public $journal;
public $piggyBankId;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(TransactionJournal $journal, $piggyBankId)
{
//
$this->journal = $journal;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(TransactionJournal $journal, $piggyBankId)
{
//
$this->journal = $journal;
$this->piggyBankId = $piggyBankId;
}
}
}

View File

@ -1,25 +1,24 @@
<?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
class JournalSaved extends Event {
class JournalSaved extends Event
{
use SerializesModels;
use SerializesModels;
public $journal;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(TransactionJournal $journal)
{
//
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(TransactionJournal $journal)
{
//
$this->journal = $journal;
}
}
}

View File

@ -38,7 +38,7 @@ class ConnectJournalToPiggyBank
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
if(intval($piggyBankId) < 1) {
if (intval($piggyBankId) < 1) {
return;
}
@ -66,7 +66,8 @@ class ConnectJournalToPiggyBank
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
Log::debug('Found no repetition for piggy bank for date '.$journal->date->format('Y M d'));
Log::debug('Found no repetition for piggy bank for date ' . $journal->date->format('Y M d'));
return;
}

View File

@ -1,8 +1,8 @@
<?php namespace FireflyIII\Handlers\Events;
use App;
use FireflyIII\Events\JournalSaved;
use Log;
use App;
/**
* Class RescanJournal

View File

@ -35,8 +35,8 @@ class UpdateJournalConnection
// get the event connected to this journal:
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
if(is_null($event)) {
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
if (is_null($event)) {
return;
}
$piggyBank = $event->piggyBank()->first();

View File

@ -2,16 +2,17 @@
namespace FireflyIII\Helpers\Reminders;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
/**
* Interface ReminderHelperInterface
*
* @package FireflyIII\Helpers\Reminders
*/
interface ReminderHelperInterface {
interface ReminderHelperInterface
{
/**
* Takes a reminder, finds the piggy bank and tells you what to do now.
* Aka how much money to put in.

View File

@ -2,6 +2,7 @@
namespace FireflyIII\Helpers\Report;
use App;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
@ -40,17 +41,20 @@ class ReportHelper implements ReportHelperInterface
* This method gets some kind of list for a monthly overview.
*
* @param Carbon $date
* @param bool $showSharedReports
*
* @return Collection
*/
public function getBudgetsForMonth(Carbon $date)
public function getBudgetsForMonth(Carbon $date, $showSharedReports = false)
{
/** @var \FireflyIII\Helpers\Report\ReportQueryInterface $query */
$query = App::make('FireflyIII\Helpers\Report\ReportQueryInterface');
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
// all budgets
$set = Auth::user()->budgets()
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
@ -58,22 +62,24 @@ class ReportHelper implements ReportHelperInterface
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
$budgets = $this->_helper->makeArray($set);
$amountSet = $this->_queries->journalsByBudget($start, $end);
$amounts = $this->_helper->makeArray($amountSet);
$combined = $this->_helper->mergeArrays($budgets, $amounts);
$combined[0]['spent'] = isset($combined[0]['spent']) ? $combined[0]['spent'] : 0.0;
$combined[0]['amount'] = isset($combined[0]['amount']) ? $combined[0]['amount'] : 0.0;
$combined[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $this->_queries->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$combined[0]['spent'] += floatval($transfer->amount) * -1;
// find transactions to shared asset accounts, which are without a budget by default:
// which is only relevant when shared asset accounts are hidden.
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
return $combined;
return $budgets;
}
/**
@ -113,6 +119,9 @@ class ReportHelper implements ReportHelperInterface
$years[] = $start->format('Y');
$start->addYear();
}
$years[] = Carbon::now()->format('Y');
// force the current year.
$years = array_unique($years);
return $years;
}

View File

@ -4,10 +4,12 @@ namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
@ -87,9 +89,9 @@ class ReportQuery implements ReportQueryInterface
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->get(
[
@ -174,26 +176,9 @@ class ReportQuery implements ReportQueryInterface
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC')
->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `amount`')]);
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $set;
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `amount`')]);
}
@ -209,26 +194,9 @@ class ReportQuery implements ReportQueryInterface
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->whereNull('budgets.id')
->orderBy('transaction_journals.date', 'ASC')
->get(['budgets.name', 'transactions.amount', 'transaction_journals.*']);
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $set;
return $query->get(['budgets.name', 'transactions.amount', 'transaction_journals.*']);
}
/**
@ -244,30 +212,7 @@ class ReportQuery implements ReportQueryInterface
*/
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// only get deposits not to a shared account
// and transfers to a shared account.
@ -291,11 +236,10 @@ class ReportQuery implements ReportQueryInterface
// any deposit is fine.
$query->where('transaction_types.type', 'Deposit');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date');
$query->groupBy('t_from.account_id')->orderBy('transaction_journals.date');
return $query->get(
// get everything, decrypt and return
$data = $query->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
@ -303,8 +247,19 @@ class ReportQuery implements ReportQueryInterface
DB::Raw('SUM(`t_to`.`amount`) as `amount`'),
'transaction_journals.date',
't_from.account_id as account_id',
'ac_from.name as name']
'ac_from.name as name',
'ac_from.encrypted as account_encrypted'
]
);
$data->each(
function (Model $object) {
// $object->description = intval($object->encrypted);
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
@ -382,7 +337,15 @@ class ReportQuery implements ReportQueryInterface
->groupBy('categories.id')
->orderBy('amount');
return $query->get(['categories.id', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]);
$data = $query->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]);
// decrypt data:
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
@ -400,29 +363,7 @@ class ReportQuery implements ReportQueryInterface
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = TransactionJournal::leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts
// and all transfers to a shared account
@ -446,13 +387,21 @@ class ReportQuery implements ReportQueryInterface
// any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal');
}
$query->before($end)
->after($start)
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('amount', 'DESC');
return $query->get(['t_to.account_id as id', 'ac_to.name as name', DB::Raw('SUM(t_to.amount) as `amount`')]);
$data = $query->get(['t_to.account_id as id', 'ac_to.name as name', 'ac_to.encrypted', DB::Raw('SUM(t_to.amount) as `amount`')]);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
@ -466,30 +415,7 @@ class ReportQuery implements ReportQueryInterface
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account
@ -514,11 +440,20 @@ class ReportQuery implements ReportQueryInterface
// any deposit goes:
$query->where('transaction_types.type', 'Deposit');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount');
return $query->get(['t_from.account_id as account_id', 'ac_from.name as name', DB::Raw('SUM(t_from.amount) as `amount`')]);
$query->groupBy('t_from.account_id')->orderBy('amount');
$data = $query->get(
['t_from.account_id as account_id', 'ac_from.name as name', 'ac_from.encrypted as encrypted', DB::Raw('SUM(t_from.amount) as `amount`')]
);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
@ -604,4 +539,74 @@ class ReportQuery implements ReportQueryInterface
);
}
/**
*
* This query will get all transaction journals and budget information for a specified account
* in a certain date range, where the transaction journal does not have a budget.
* There is no get() specified, this is up to the method itself.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Builder
*/
protected function queryJournalsNoBudget(Account $account, Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Builder
*/
protected function queryJournalsWithTransactions(Carbon $start, Carbon $end)
{
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
$query->before($end)->after($start)->where('transaction_journals.user_id', Auth::user()->id);
return $query;
}
}

View File

@ -84,6 +84,7 @@ class AccountController extends Controller
*/
public function edit(Account $account, AccountRepositoryInterface $repository)
{
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
@ -93,15 +94,19 @@ class AccountController extends Controller
// the opening balance is tricky:
$openingBalanceAmount = null;
if ($openingBalance) {
$transaction = $openingBalance->transactions()->where('account_id', $account->id)->first();
$transaction = $repository->getFirstTransaction($openingBalance, $account);
$openingBalanceAmount = $transaction->amount;
}
$preFilled = [
'accountRole' => $account->getMeta('accountRole'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount
'accountRole' => $account->getMeta('accountRole'),
'ccType' => $account->getMeta('ccType'),
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => floatval($account->virtual_balance)
];
Session::flash('preFilled', $preFilled);
@ -132,7 +137,7 @@ class AccountController extends Controller
$total = Auth::user()->accounts()->accountTypeIn($types)->count();
// last activity:
$start = clone Session::get('start');
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$start->subDay();
$set->each(
function (Account $account) use ($start) {
@ -169,7 +174,8 @@ class AccountController extends Controller
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$journals->setPath('accounts/show/'.$account->id);
$journals->setPath('accounts/show/' . $account->id);
return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle'));
}
@ -184,6 +190,7 @@ class AccountController extends Controller
$accountData = [
'name' => $request->input('name'),
'accountType' => $request->input('what'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'active' => true,
'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'),
@ -219,9 +226,12 @@ class AccountController extends Controller
'active' => $request->input('active'),
'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
];
$repository->update($account, $accountData);

View File

@ -64,7 +64,7 @@ class AuthController extends Controller
);
}
$data =$request->all();
$data = $request->all();
$data['password'] = bcrypt($data['password']);
$this->auth->login($this->registrar->create($data));
@ -81,6 +81,8 @@ class AuthController extends Controller
// set flash message
Session::flash('success', 'You have registered successfully!');
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'new-registration');
return redirect($this->redirectPath());

View File

@ -1,9 +1,10 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
@ -22,12 +23,59 @@ use View;
class BillController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Bills');
View::share('mainTitleIcon', 'fa-calendar-o');
}
/**
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function add(Bill $bill)
{
$matches = explode(',', $bill->match);
$description = [];
$expense = null;
// get users expense accounts:
$type = AccountType::where('type', 'Expense account')->first();
$accounts = Auth::user()->accounts()->where('account_type_id', $type->id)->get();
foreach ($matches as $match) {
$match = strtolower($match);
// find expense account for each word if not found already:
if (is_null($expense)) {
/** @var Account $account */
foreach ($accounts as $account) {
$name = strtolower($account->name);
if (!(strpos($name, $match) === false)) {
$expense = $account;
break;
}
}
}
if (is_null($expense)) {
$description[] = $match;
}
}
$parameters = [
'description' => ucfirst(join(' ', $description)),
'expense_account' => is_null($expense) ? '' : $expense->name,
'amount' => round(($bill->amount_min + $bill->amount_max), 2),
];
Session::put('preFilled', $parameters);
return Redirect::to(route('transactions.create', 'withdrawal'));
}
/**
* @return $this
*/
@ -139,11 +187,10 @@ class BillController extends Controller
public function show(Bill $bill, BillRepositoryInterface $repository)
{
$journals = $bill->transactionjournals()->withRelevantData()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get();
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get();
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$hideBill = true;
@ -156,22 +203,8 @@ class BillController extends Controller
*/
public function store(BillFormRequest $request, BillRepositoryInterface $repository)
{
$billData = [
'name' => $request->get('name'),
'match' => $request->get('match'),
'amount_min' => floatval($request->get('amount_min')),
'amount_currency_id' => floatval($request->get('amount_currency_id')),
'amount_max' => floatval($request->get('amount_max')),
'date' => new Carbon($request->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $request->get('repeat_freq'),
'skip' => intval($request->get('skip')),
'automatch' => intval($request->get('automatch')) === 1,
'active' => intval($request->get('active')) === 1,
];
$bill = $repository->store($billData);
$billData = $request->getBillData();
$bill = $repository->store($billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
if (intval(Input::get('create_another')) === 1) {
@ -189,21 +222,8 @@ class BillController extends Controller
*/
public function update(Bill $bill, BillFormRequest $request, BillRepositoryInterface $repository)
{
$billData = [
'name' => $request->get('name'),
'match' => $request->get('match'),
'amount_min' => floatval($request->get('amount_min')),
'amount_currency_id' => floatval($request->get('amount_currency_id')),
'amount_max' => floatval($request->get('amount_max')),
'date' => new Carbon($request->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $request->get('repeat_freq'),
'skip' => intval($request->get('skip')),
'automatch' => intval($request->get('automatch')) === 1,
'active' => intval($request->get('active')) === 1,
];
$bill = $repository->update($bill, $billData);
$billData = $request->getBillData();
$bill = $repository->update($bill, $billData);
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('bills.edit', $bill->id);

View File

@ -5,6 +5,7 @@ use Carbon\Carbon;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Input;
@ -13,7 +14,7 @@ use Redirect;
use Response;
use Session;
use View;
use URL;
/**
* Class BudgetController
*
@ -22,6 +23,9 @@ use View;
class BudgetController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Budgets');
@ -32,7 +36,6 @@ class BudgetController extends Controller
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function amount(Budget $budget, BudgetRepositoryInterface $repository)
{
@ -49,6 +52,12 @@ class BudgetController extends Controller
*/
public function create()
{
// put previous url in session if not redirect from store (not "create another").
if (Session::get('budgets.create.fromStore') !== true) {
Session::put('budgets.create.url', URL::previous());
}
Session::forget('budgets.create.fromStore');
return view('budgets.create')->with('subTitle', 'Create a new budget');
}
@ -61,6 +70,9 @@ class BudgetController extends Controller
{
$subTitle = 'Delete budget' . e($budget->name) . '"';
// put previous url in session
Session::put('budgets.delete.url', URL::previous());
return view('budgets.delete', compact('budget', 'subTitle'));
}
@ -75,9 +87,11 @@ class BudgetController extends Controller
$name = $budget->name;
$repository->destroy($budget);
Session::flash('success', 'The budget "' . e($name) . '" was deleted.');
return Redirect::route('budgets.index');
return Redirect::to(Session::get('budgets.delete.url'));
}
/**
@ -89,6 +103,12 @@ class BudgetController extends Controller
{
$subTitle = 'Edit budget "' . e($budget->name) . '"';
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('budgets.edit.fromUpdate') !== true) {
Session::put('budgets.edit.url', URL::previous());
}
Session::forget('budgets.edit.fromUpdate');
return view('budgets.edit', compact('budget', 'subTitle'));
}
@ -98,7 +118,15 @@ class BudgetController extends Controller
*/
public function index(BudgetRepositoryInterface $repository)
{
$budgets = Auth::user()->budgets()->get();
$budgets = Auth::user()->budgets()->where('active', 1)->get();
$inactive = Auth::user()->budgets()->where('active', 0)->get();
/**
* Do some cleanup:
*/
$repository->cleanupBudgets();
// loop the budgets:
$budgets->each(
@ -117,7 +145,7 @@ class BudgetController extends Controller
$budgetMax = Preferences::get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data;
return view('budgets.index', compact('budgetMaximum', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount'));
return view('budgets.index', compact('budgetMaximum', 'inactive', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount'));
}
/**
@ -133,9 +161,9 @@ class BudgetController extends Controller
->whereNull('budget_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a budget in ' . $start->format('F Y');
@ -171,10 +199,13 @@ class BudgetController extends Controller
Session::flash('success', 'New budget "' . $budget->name . '" stored!');
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('budgets.create.fromStore', true);
return Redirect::route('budgets.create')->withInput();
}
return Redirect::route('budgets.index');
// redirect to previous URL.
return Redirect::to(Session::get('budgets.create.url'));
}
@ -209,7 +240,8 @@ class BudgetController extends Controller
public function update(Budget $budget, BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
];
$repository->update($budget, $budgetData);
@ -217,10 +249,13 @@ class BudgetController extends Controller
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('budgets.edit', $budget->id);
// set value so edit routine will not overwrite URL:
Session::put('budgets.edit.fromUpdate', true);
return Redirect::route('budgets.edit', $budget->id)->withInput(['return_to_edit' => 1]);
}
return Redirect::route('budgets.index');
// redirect to previous URL.
return Redirect::to(Session::get('budgets.edit.url'));
}

View File

@ -11,7 +11,7 @@ use Input;
use Redirect;
use Session;
use View;
use URL;
/**
* Class CategoryController
@ -35,6 +35,12 @@ class CategoryController extends Controller
*/
public function create()
{
// put previous url in session if not redirect from store (not "create another").
if (Session::get('categories.create.fromStore') !== true) {
Session::put('categories.create.url', URL::previous());
}
Session::forget('categories.create.fromStore');
return view('categories.create')->with('subTitle', 'Create a new category');
}
@ -47,6 +53,9 @@ class CategoryController extends Controller
{
$subTitle = 'Delete category' . e($category->name) . '"';
// put previous url in session
Session::put('categories.delete.url', URL::previous());
return view('categories.delete', compact('category', 'subTitle'));
}
@ -63,7 +72,7 @@ class CategoryController extends Controller
Session::flash('success', 'The category "' . e($name) . '" was deleted.');
return Redirect::route('categories.index');
return Redirect::to(Session::get('categories.delete.url'));
}
/**
@ -75,6 +84,12 @@ class CategoryController extends Controller
{
$subTitle = 'Edit category "' . e($category->name) . '"';
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('categories.edit.fromUpdate') !== true) {
Session::put('categories.edit.url', URL::previous());
}
Session::forget('categories.edit.fromUpdate');
return view('categories.edit', compact('category', 'subTitle'));
}
@ -89,10 +104,10 @@ class CategoryController extends Controller
$categories->each(
function (Category $category) {
$latest = $category->transactionjournals()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->first();
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->first();
if ($latest) {
$category->lastActivity = $latest->date;
}
@ -115,9 +130,9 @@ class CategoryController extends Controller
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
@ -136,14 +151,12 @@ class CategoryController extends Controller
$page = intval(Input::get('page'));
$offset = $page > 0 ? $page * 50 : 0;
$set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get(
['transaction_journals.*']
);
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(
['transaction_journals.*']
);
$count = $category->transactionJournals()->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page);
@ -166,16 +179,22 @@ class CategoryController extends Controller
$category = $repository->store($categoryData);
Session::flash('success', 'New category "' . $category->name . '" stored!');
if (intval(Input::get('create_another')) === 1) {
Session::put('categories.create.fromStore', true);
return Redirect::route('categories.create')->withInput();
}
<<<<<<< HEAD
if (intval(Input::get('create_another')) === 1) {
return Redirect::route('categories.create');
}
return Redirect::route('categories.index');
=======
// redirect to previous URL.
return Redirect::to(Session::get('categories.create.url'));
>>>>>>> release/3.3.6
}
@ -198,10 +217,12 @@ class CategoryController extends Controller
Session::flash('success', 'Category "' . $category->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
Session::put('categories.edit.fromUpdate', true);
return Redirect::route('categories.edit', $category->id);
}
return Redirect::route('categories.index');
// redirect to previous URL.
return Redirect::to(Session::get('categories.edit.url'));
}

View File

@ -10,7 +10,7 @@ use Preferences;
use Redirect;
use Session;
use View;
use URL;
/**
* Class CurrencyController
@ -39,6 +39,12 @@ class CurrencyController extends Controller
$subTitleIcon = 'fa-plus';
$subTitle = 'Create a new currency';
// put previous url in session if not redirect from store (not "create another").
if (Session::get('currency.create.fromStore') !== true) {
Session::put('currency.create.url', URL::previous());
}
Session::forget('currency.create.fromStore');
return view('currency.create', compact('subTitleIcon', 'subTitle'));
}
@ -72,6 +78,9 @@ class CurrencyController extends Controller
if ($currency->transactionJournals()->count() > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.');
// put previous url in session
Session::put('currency.delete.url', URL::previous());
return Redirect::route('currency.index');
}
@ -96,7 +105,7 @@ class CurrencyController extends Controller
$currency->delete();
return Redirect::route('currency.index');
return Redirect::to(Session::get('currency.delete.url'));
}
/**
@ -110,6 +119,12 @@ class CurrencyController extends Controller
$subTitle = 'Edit currency "' . e($currency->name) . '"';
$currency->symbol = htmlentities($currency->symbol);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('currency.edit.fromUpdate') !== true) {
Session::put('currency.edit.url', URL::previous());
}
Session::forget('currency.edit.fromUpdate');
return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
}
@ -148,10 +163,12 @@ class CurrencyController extends Controller
Session::flash('success', 'Currency "' . $currency->name . '" created');
if (intval(Input::get('create_another')) === 1) {
Session::put('currency.create.fromStore', true);
return Redirect::route('currency.create')->withInput();
}
return Redirect::route('currency.index');
// redirect to previous URL.
return Redirect::to(Session::get('categories.create.url'));
}
@ -173,10 +190,12 @@ class CurrencyController extends Controller
if (intval(Input::get('return_to_edit')) === 1) {
Session::put('currency.edit.fromUpdate', true);
return Redirect::route('currency.edit', $currency->id);
}
return Redirect::route('currency.index');
// redirect to previous URL.
return Redirect::to(Session::get('currency.edit.url'));
}

View File

@ -1,8 +1,10 @@
<?php namespace FireflyIII\Http\Controllers;
use Amount;
use App;
use Auth;
use Carbon\Carbon;
use Crypt;
use DB;
use Exception;
use FireflyIII\Helpers\Report\ReportQueryInterface;
@ -51,7 +53,7 @@ class GoogleChartController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
@ -237,29 +239,31 @@ class GoogleChartController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = TransactionJournal::
where('transaction_journals.user_id',Auth::user()->id)
->leftJoin(
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
where('transaction_journals.user_id', Auth::user()->id)
->leftJoin(
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end)
->where('categories.user_id',Auth::user()->id)
->where('categories.user_id', Auth::user()->id)
->after($start)
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('sum', 'DESC')
->get(['categories.id', 'categories.name', \DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
->get(['categories.id', 'categories.encrypted', 'categories.name', \DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
foreach ($set as $entry) {
$entry->name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$chart->addRow($entry->name, floatval($entry->sum));
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
}
$chart->generate();
@ -279,7 +283,7 @@ class GoogleChartController extends Controller
$chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number');
$chart->addColumn('Current entry', 'number');
$chart->addColumn('Recorded bill entry', 'number');
// get first transaction or today for start:
$first = $bill->transactionjournals()->orderBy('date', 'ASC')->first();
@ -288,22 +292,18 @@ class GoogleChartController extends Controller
} else {
$start = new Carbon;
}
$end = new Carbon;
while ($start <= $end) {
$result = $bill->transactionjournals()->before($end)->after($start)->first();
if ($result) {
/** @var Transaction $tr */
foreach ($result->transactions()->get() as $tr) {
if (floatval($tr->amount) > 0) {
$amount = floatval($tr->amount);
}
$results = $bill->transactionjournals()->after($start)->get();
/** @var TransactionJournal $result */
foreach ($results as $result) {
$amount = 0;
/** @var Transaction $tr */
foreach ($result->transactions()->get() as $tr) {
if (floatval($tr->amount) > 0) {
$amount = floatval($tr->amount);
}
} else {
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $bill->amount_max, $bill->amount_min, $amount);
$start = Navigation::addPeriod($start, $bill->repeat_freq, 0);
$chart->addRow(clone $result->date, $bill->amount_max, $bill->amount_min, $amount);
}
$chart->generate();
@ -356,6 +356,49 @@ class GoogleChartController extends Controller
}
}
/**
* Find credit card accounts and possibly unpaid credit card bills.
*/
$creditCards = Auth::user()->accounts()
->hasMetaValue('accountRole', 'ccAsset')
->hasMetaValue('ccType', 'monthlyFull')
->get(
[
'accounts.*',
'ccType.data as ccType',
'accountRole.data as accountRole'
]
);
// if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid!
$unpaid['amount'] += $balance * -1;
$unpaid['items'][] = $creditCard->name . ' (expected ' . Amount::format(($balance * -1), false) . ') on the ' . $date->format('jS') . ')';
}
if ($balance == 0) {
// find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$transactions = $creditCard->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->before($end)->after($start)->get();
if ($transactions->count() > 0) {
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$journal = $transaction->transactionJournal;
if ($journal->transactionType->type == 'Transfer') {
$paid['amount'] += floatval($transaction->amount);
$paid['items'][] = $creditCard->name .
' (paid ' . Amount::format((floatval($transaction->amount)), false) .
' on the ' . $journal->date->format('jS') . ')';
}
}
}
}
}
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();

View File

@ -1,11 +1,12 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Preferences;
use Session;
use Redirect;
/**
* Class HomeController
*
@ -29,6 +30,11 @@ class HomeController extends Controller
Session::put('end', $end);
}
public function flush() {
Session::clear();
return Redirect::route('index');
}
/**
* @return \Illuminate\View\View
*/
@ -46,6 +52,16 @@ class HomeController extends Controller
$accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts();
// check if all books are correct.
$sum = floatval(Auth::user()->transactions()->sum('amount'));
if ($sum != 0) {
Session::flash(
'error', 'Your transactions are unbalanced. This means a'
. ' withdrawal, deposit or transfer was not stored properly. '
. 'Please check your accounts and transactions for errors.'
);
}
foreach ($accounts as $account) {
$set = $repository->getFrontpageTransactions($account, $start, $end);
if (count($set) > 0) {

View File

@ -2,14 +2,18 @@
use Amount;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class JsonController
@ -70,11 +74,34 @@ class JsonController extends Controller
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count == 0) {
$amount += floatval($bill->amount_max + $bill->amount_min / 2);
}
}
}
/**
* Find credit card accounts and possibly unpaid credit card bills.
*/
$creditCards = Auth::user()->accounts()
->hasMetaValue('accountRole', 'ccAsset')
->hasMetaValue('ccType', 'monthlyFull')
->get(
[
'accounts.*',
'ccType.data as ccType',
'accountRole.data as accountRole'
]
);
// if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
if ($balance < 0) {
// unpaid!
$amount += $balance * -1;
}
}
break;
case 'bills-paid':
$box = 'bills-paid';
@ -89,8 +116,8 @@ class JsonController extends Controller
// paid a bill in this range?
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count != 0) {
$journal = $bill->transactionjournals()->with('transactions')->before($range['end'])->after($range['start'])->first();
$currentAmount = 0;
$journal = $bill->transactionjournals()->with('transactions')->before($range['end'])->after($range['start'])->first();
$currentAmount = 0;
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0) {
$currentAmount = floatval($t->amount);
@ -101,6 +128,41 @@ class JsonController extends Controller
}
}
/**
* Find credit card accounts and possibly unpaid credit card bills.
*/
$creditCards = Auth::user()->accounts()
->hasMetaValue('accountRole', 'ccAsset')
->hasMetaValue('ccType', 'monthlyFull')
->get(
[
'accounts.*',
'ccType.data as ccType',
'accountRole.data as accountRole'
]
);
// if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
if ($balance == 0) {
// find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$transactions = $creditCard->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->before($end)->after($start)->get();
if ($transactions->count() > 0) {
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$journal = $transaction->transactionJournal;
if ($journal->transactionType->type == 'Transfer') {
$amount += floatval($transaction->amount);
}
}
}
}
}
}
return Response::json(['box' => $box, 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);

View File

@ -17,6 +17,7 @@ use Redirect;
use Session;
use Steam;
use View;
use URL;
/**
* Class PiggyBankController
@ -49,9 +50,6 @@ class PiggyBankController extends Controller
$leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave);
\Log::debug('Now going to view for piggy bank #' . $piggyBank->id . ' (' . $piggyBank->name . ')');
return view('piggy-banks.add', compact('piggyBank', 'maxAmount'));
}
@ -68,6 +66,12 @@ class PiggyBankController extends Controller
$subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus';
// put previous url in session if not redirect from store (not "create another").
if (Session::get('piggy-banks.create.fromStore') !== true) {
Session::put('piggy-banks.create.url', URL::previous());
}
Session::forget('piggy-banks.create.fromStore');
return view('piggy-banks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon'));
}
@ -80,6 +84,9 @@ class PiggyBankController extends Controller
{
$subTitle = 'Delete "' . e($piggyBank->name) . '"';
// put previous url in session
Session::put('piggy-banks.delete.url', URL::previous());
return view('piggy-banks.delete', compact('piggyBank', 'subTitle'));
}
@ -94,7 +101,7 @@ class PiggyBankController extends Controller
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$piggyBank->delete();
return Redirect::route('piggy-banks.index');
return Redirect::to(Session::get('piggy-banks.delete.url'));
}
/**
@ -132,6 +139,12 @@ class PiggyBankController extends Controller
];
Session::flash('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('piggy-banks.edit.fromUpdate') !== true) {
Session::put('piggy-banks.edit.url', URL::previous());
}
Session::forget('piggy-banks.edit.fromUpdate');
return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled'));
}
@ -141,7 +154,7 @@ class PiggyBankController extends Controller
public function index(AccountRepositoryInterface $repository)
{
/** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->orderBy('order', 'ASC')->get();
$piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get();
$accounts = [];
/** @var PiggyBank $piggyBank */
@ -157,7 +170,7 @@ class PiggyBankController extends Controller
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [
'name' => $account->name,
'balance' => Steam::balance($account),
'balance' => Steam::balance($account,null,true),
'leftForPiggyBanks' => $repository->leftOnAccount($account),
'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount),
@ -298,7 +311,6 @@ class PiggyBankController extends Controller
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
@ -313,11 +325,13 @@ class PiggyBankController extends Controller
Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('create_another')) === 1) {
Session::put('piggy-banks.create.fromStore', true);
return Redirect::route('piggy-banks.create')->withInput();
}
return Redirect::route('piggy-banks.index');
// redirect to previous URL.
return Redirect::to(Session::get('piggy-banks.create.url'));
}
/**
@ -330,7 +344,6 @@ class PiggyBankController extends Controller
public function update(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request)
{
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')),
@ -346,11 +359,13 @@ class PiggyBankController extends Controller
Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('return_to_edit')) === 1) {
Session::put('piggy-banks.edit.fromUpdate', true);
return Redirect::route('piggy-banks.edit', $piggyBank->id);
}
return Redirect::route('piggy-banks.index');
// redirect to previous URL.
return Redirect::to(Session::get('piggy-banks.edit.url'));
}

View File

@ -1,18 +1,17 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use Exception;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Preferences;
use Session;
use Steam;
use View;
use FireflyIII\Models\Preference;
use Crypt;
/**
* Class ReportController
@ -22,11 +21,20 @@ use FireflyIII\Models\Preference;
class ReportController extends Controller
{
/** @var ReportHelperInterface */
protected $helper;
/** @var ReportQueryInterface */
protected $query;
/**
*
* @param ReportHelperInterface $helper
* @param ReportQueryInterface $query
*/
public function __construct()
public function __construct(ReportHelperInterface $helper, ReportQueryInterface $query)
{
$this->query = $query;
$this->helper = $helper;
View::share('title', 'Reports');
View::share('mainTitleIcon', 'fa-line-chart');
@ -38,7 +46,7 @@ class ReportController extends Controller
*
* @return \Illuminate\View\View
*/
public function budget($year = '2014', $month = '1', ReportQueryInterface $query)
public function budget($year = '2014', $month = '1')
{
try {
new Carbon($year . '-' . $month . '-01');
@ -52,7 +60,7 @@ class ReportController extends Controller
$end->endOfMonth();
$start->subDay();
// shared accounts preference:
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
@ -61,13 +69,13 @@ class ReportController extends Controller
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$dayEarly = $dayEarly->subDay();
$accounts = $query->getAllAccounts($start, $end, $showSharedReports);
$accounts = $this->query->getAllAccounts($start, $end, $showSharedReports);
$start->addDay();
$accounts->each(
function (Account $account) use ($start, $end, $query) {
$budgets = $query->getBudgetSummary($account, $start, $end);
$balancedAmount = $query->balancedTransactionsSum($account, $start, $end);
function (Account $account) use ($start, $end) {
$budgets = $this->query->getBudgetSummary($account, $start, $end);
$balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$array = [];
$hide = true;
foreach ($budgets as $budget) {
@ -85,35 +93,10 @@ class ReportController extends Controller
}
);
$start = clone $date;
$start->startOfMonth();
/**
* Start getBudgetsForMonth DONE
*/
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared asset accounts, which are without a budget by default:
// which is only relevant when shared asset accounts are hidden.
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
@ -128,11 +111,11 @@ class ReportController extends Controller
*
* @return View
*/
public function index(ReportHelperInterface $helper)
public function index()
{
$start = Session::get('first');
$months = $helper->listOfMonths($start);
$years = $helper->listOfYears($start);
$months = $this->helper->listOfMonths($start);
$years = $this->helper->listOfYears($start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
@ -146,7 +129,7 @@ class ReportController extends Controller
*
* @return \Illuminate\View\View
*/
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query)
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1')
{
try {
@ -158,7 +141,7 @@ class ReportController extends Controller
$end = clone $start;
$end->endOfMonth();
$journals = $query->balancedTransactionsList($account, $start, $end);
$journals = $this->query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
@ -173,7 +156,7 @@ class ReportController extends Controller
*
* @return View
*/
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query)
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1')
{
try {
new Carbon($year . '-' . $month . '-01');
@ -183,7 +166,7 @@ class ReportController extends Controller
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$set = $query->getTransactionsWithoutBudget($account, $start, $end);
$set = $this->query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter(
function (TransactionJournal $journal) {
@ -204,7 +187,7 @@ class ReportController extends Controller
*
* @return \Illuminate\View\View
*/
public function modalNoBudget(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query)
public function modalNoBudget(Account $account, $year = '2014', $month = '1')
{
try {
new Carbon($year . '-' . $month . '-01');
@ -214,7 +197,7 @@ class ReportController extends Controller
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $this->query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
@ -226,7 +209,7 @@ class ReportController extends Controller
*
* @return \Illuminate\View\View
*/
public function month($year = '2014', $month = '1', ReportQueryInterface $query)
public function month($year = '2014', $month = '1')
{
try {
new Carbon($year . '-' . $month . '-01');
@ -237,7 +220,7 @@ class ReportController extends Controller
$subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
@ -256,14 +239,15 @@ class ReportController extends Controller
/**
* Start getIncomeForMonth DONE
*/
$income = $query->incomeByPeriod($start, $end, $showSharedReports);
$income = $this->query->incomeByPeriod($start, $end, $showSharedReports);
/**
* End getIncomeForMonth DONE
*/
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $query->journalsByExpenseAccount($start, $end, $showSharedReports);
$set = $this->query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
@ -273,28 +257,7 @@ class ReportController extends Controller
/**
* Start getBudgetsForMonth DONE
*/
$set = Auth::user()->budgets()
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
@ -303,18 +266,20 @@ class ReportController extends Controller
* Start getCategoriesForMonth DONE
*/
// all categories.
$result = $query->journalsByCategory($start, $end);
$result = $this->query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result);
// all transfers
if ($showSharedReports === false) {
$result = $query->sharedExpensesByCategory($start, $end);
$result = $this->query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
} else {
$merged = $categories;
}
// sort.
$sorted = Steam::sortNegativeArray($merged);
@ -326,7 +291,7 @@ class ReportController extends Controller
/**
* Start getAccountsForMonth
*/
$list = $query->accountList($showSharedReports);
$list = $this->query->accountList($showSharedReports);
$accounts = [];
/** @var Account $account */
foreach ($list as $account) {
@ -360,7 +325,7 @@ class ReportController extends Controller
*
* @return $this
*/
public function year($year, ReportHelperInterface $helper, ReportQueryInterface $query)
public function year($year)
{
try {
new Carbon('01-01-' . $year);
@ -377,9 +342,9 @@ class ReportController extends Controller
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end, $showSharedReports);
$balances = $this->helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports);
return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')

View File

@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\JournalSaved;
@ -13,9 +12,10 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Input;
use Redirect;
use Session;
use View;
use Response;
use Session;
use URL;
use View;
/**
* Class TransactionController
@ -61,6 +61,12 @@ class TransactionController extends Controller
}
Session::put('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "create another").
if (Session::get('transactions.create.fromStore') !== true) {
Session::put('transactions.create.url', URL::previous());
}
Session::forget('transactions.create.fromStore');
asort($piggies);
@ -79,6 +85,9 @@ class TransactionController extends Controller
$type = strtolower($journal->transactionType->type);
$subTitle = 'Delete ' . e($type) . ' "' . e($journal->description) . '"';
// put previous url in session
Session::put('transactions.delete.url', URL::previous());
return View::make('transactions.delete', compact('journal', 'subTitle'));
@ -91,23 +100,12 @@ class TransactionController extends Controller
*/
public function destroy(TransactionJournal $transactionJournal)
{
$type = $transactionJournal->transactionType->type;
$return = 'withdrawal';
Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.');
$transactionJournal->delete();
switch ($type) {
case 'Deposit':
$return = 'deposit';
break;
case 'Transfer':
$return = 'transfers';
break;
}
return Redirect::route('transactions.index', $return);
// redirect to previous URL:
return Redirect::to(Session::get('transactions.delete.url'));
}
/**
@ -164,6 +162,12 @@ class TransactionController extends Controller
$preFilled['account_from_id'] = $transactions[1]->account->id;
$preFilled['account_to_id'] = $transactions[0]->account->id;
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('transactions.edit.fromUpdate') !== true) {
Session::put('transactions.edit.url', URL::previous());
}
Session::forget('transactions.edit.fromUpdate');
return View::make('transactions.edit', compact('journal', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled);
}
@ -199,16 +203,13 @@ class TransactionController extends Controller
$page = intval(\Input::get('page'));
$offset = $page > 0 ? ($page - 1) * 50 : 0;
$set = Auth::user()->
transactionJournals()->
transactionTypes($types)->
withRelevantData()->take(50)->offset($offset)
->orderBy('date', 'DESC')
->orderBy('order','ASC')
->orderBy('id','DESC')
->get(
['transaction_journals.*']
);
$set = Auth::user()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)
->orderBy('date', 'DESC')
->orderBy('order', 'ASC')
->orderBy('id', 'DESC')
->get(
['transaction_journals.*']
);
$count = Auth::user()->transactionJournals()->transactionTypes($types)->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('transactions/' . $what);
@ -227,13 +228,14 @@ class TransactionController extends Controller
$order = 0;
foreach ($ids as $id) {
$journal = Auth::user()->transactionjournals()->where('id', $id)->where('date', Input::get('date'))->first();
if($journal) {
if ($journal) {
$journal->order = $order;
$order++;
$journal->save();
}
}
}
return Response::json(true);
}
@ -251,10 +253,10 @@ class TransactionController extends Controller
$t->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)
->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
->where('transaction_journals.order','>=',$journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->sum('transactions.amount')
->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
->where('transaction_journals.order', '>=', $journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->sum('transactions.amount')
);
$t->after = $t->before + $t->amount;
}
@ -274,25 +276,13 @@ class TransactionController extends Controller
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
'account_id' => intval($request->get('account_id')),
'account_from_id' => intval($request->get('account_from_id')),
'account_to_id' => intval($request->get('account_to_id')),
'expense_account' => $request->get('expense_account'),
'revenue_account' => $request->get('revenue_account'),
'amount' => floatval($request->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($request->get('amount_currency_id')),
'date' => new Carbon($request->get('date')),
'budget_id' => intval($request->get('budget_id')),
'category' => $request->get('category'),
];
$journal = $repository->store($journalData);
$journalData = $request->getJournalData();
$journal = $repository->store($journalData);
// rescan journal, UpdateJournalConnection
event(new JournalSaved($journal));
// ConnectJournalToPiggyBank
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
if (intval($request->get('reminder_id')) > 0) {
@ -304,10 +294,14 @@ class TransactionController extends Controller
Session::flash('success', 'New transaction "' . $journal->description . '" stored!');
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('transactions.create.fromStore', true);
return Redirect::route('transactions.create', $request->input('what'))->withInput();
}
return Redirect::route('transactions.index', $request->input('what'));
// redirect to previous URL.
return Redirect::to(Session::get('transactions.create.url'));
}
@ -321,23 +315,7 @@ class TransactionController extends Controller
public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
'account_id' => intval($request->get('account_id')),
'account_from_id' => intval($request->get('account_from_id')),
'account_to_id' => intval($request->get('account_to_id')),
'expense_account' => $request->get('expense_account'),
'revenue_account' => $request->get('revenue_account'),
'amount' => floatval($request->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($request->get('amount_currency_id')),
'date' => new Carbon($request->get('date')),
'budget_id' => intval($request->get('budget_id')),
'category' => $request->get('category'),
];
$journalData = $request->getJournalData();
$repository->update($journal, $journalData);
event(new JournalSaved($journal));
@ -346,11 +324,14 @@ class TransactionController extends Controller
Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('transactions.edit', $journal->id);
// set value so edit routine will not overwrite URL:
Session::put('transactions.edit.fromUpdate', true);
return Redirect::route('transactions.edit', $journal->id)->withInput(['return_to_edit' => 1]);
}
return Redirect::route('transactions.index', $journalData['what']);
// redirect to previous URL.
return Redirect::to(Session::get('transactions.edit.url'));
}

View File

@ -13,7 +13,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Navigation;
use Session;
use App;
/**
* Class PiggyBanks
@ -50,12 +50,11 @@ class PiggyBanks
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->check() && !$request->isXmlHttpRequest()) {
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') {
// get piggy banks without a repetition:
/** @var Collection $set */
$set = $this->auth->user()->piggybanks()
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->where('piggy_banks.repeats', 0)
->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) {
@ -70,68 +69,6 @@ class PiggyBanks
}
}
unset($partialPiggy, $set, $repetition);
// get repeating piggy banks without a repetition for current time frame.
/** @var Collection $set */
$set = $this->auth->user()->piggybanks()->leftJoin(
'piggy_bank_repetitions', function (JoinClause $join) {
$join->on('piggy_bank_repetitions.piggy_bank_id', '=', 'piggy_banks.id')
->where('piggy_bank_repetitions.targetdate', '>=', Session::get('start')->format('Y-m-d'))
->where('piggy_bank_repetitions.startdate', '<=', Session::get('end')->format('Y-m-d'));
}
)
->where('repeats', 1)
->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.*']);
// these piggy banks are missing a repetition. start looping and create them!
if ($set->count() > 0) {
/** @var PiggyBank $piggyBank */
foreach ($set as $piggyBank) {
$start = clone $piggyBank->startdate;
$end = clone $piggyBank->targetdate;
$max = clone $piggyBank->targetdate;
// first loop: start date to target date.
// then, continue looping until end is > today
while ($start <= $max) {
// first loop fixes this date. or should fix it.
$max = new Carbon;
echo '[#'.$piggyBank->id.', from: '.$start->format('Y-m-d.').' to '.$end->format('Y-m-d.').']';
// create stuff. Or at least, try:
$repetition = $piggyBank->piggyBankRepetitions()->onDates($start, $end)->first();
if(!$repetition) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($piggyBank);
$repetition->startdate = $start;
$repetition->targetdate = $end;
$repetition->currentamount = 0;
// it might exist, catch:
$repetition->save();
}
// start where end 'ended':
$start = clone $end;
// move end.
$end = Navigation::addPeriod($end, $piggyBank->rep_length, 0);
}
// first repetition: from original start to original target.
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($piggyBank);
$repetition->startdate = is_null($piggyBank->startdate) ? null : $piggyBank->startdate;
$repetition->targetdate = is_null($piggyBank->targetdate) ? null : $piggyBank->targetdate;
$repetition->currentamount = 0;
// it might exist, catch:
// then, loop from original target up to now.
}
}
}
return $next($request);

View File

@ -3,6 +3,7 @@
namespace FireflyIII\Http\Middleware;
use App;
use Carbon\Carbon;
use Closure;
use Illuminate\Contracts\Auth\Guard;
@ -47,14 +48,14 @@ class Range
*/
public function handle(Request $request, Closure $theNext)
{
if ($this->auth->check()) {
if ($this->auth->check() && App::environment() != 'testing') {
// ignore preference. set the range to be the current month:
if (!Session::has('start') && !Session::has('end')) {
/** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M');
$start = Session::has('start') ? Session::get('start') : new Carbon;
$start = new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start);
@ -62,11 +63,16 @@ class Range
Session::put('end', $end);
}
if (!Session::has('first')) {
$journal = $this->auth->user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
/**
* Get helper thing.
*/
/** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface');
$journal = $repository->first();
if ($journal) {
Session::put('first', $journal->date);
} else {
Session::put('first', Carbon::now());
Session::put('first', Carbon::now()->startOfYear());
}
}

View File

@ -46,7 +46,7 @@ class Reminders
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->check() && !$request->isXmlHttpRequest()) {
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') {
// do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon;

View File

@ -46,7 +46,7 @@ class ReplaceTestVars
$input = $request->all();
$input['_token'] = $request->session()->token();
// we need to update _token value to make sure we get the POST / PUT tests passed.
Log::debug('Input token replaced ('.$input['_token'].').');
Log::debug('Input token replaced (' . $input['_token'] . ').');
$request->replace($input);
}

View File

@ -28,22 +28,29 @@ class AccountFormRequest extends Request
*/
public function rules()
{
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier')));
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = join(',', array_keys(Config::get('firefly.ccTypes')));
$nameRule = 'required|between:1,100|uniqueAccountForUser';
$idRule = '';
if (Account::find(Input::get('id'))) {
$nameRule = 'required|between:1,100|belongsToUser:accounts|uniqueForUser:'.Input::get('id');
$idRule = 'belongsToUser:accounts';
$nameRule = 'required|between:1,100|uniqueAccountForUser:' . Input::get('id');
}
return [
'name' => $nameRule,
'openingBalance' => 'numeric',
'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean',
'balance_currency_id' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types
'id' => $idRule,
'name' => $nameRule,
'openingBalance' => 'numeric',
'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean',
'ccType' => 'in:' . $ccPaymentTypes,
'ccMonthlyPaymentDate' => 'date',
'balance_currency_id' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types
];
}
}

View File

@ -3,6 +3,7 @@
namespace FireflyIII\Http\Requests;
use Auth;
use Carbon\Carbon;
use Input;
/**
@ -21,19 +22,41 @@ class BillFormRequest extends Request
return Auth::check();
}
/**
* @return array
*/
public function getBillData()
{
return [
'name' => $this->get('name'),
'match' => $this->get('match'),
'amount_min' => floatval($this->get('amount_min')),
'amount_currency_id' => floatval($this->get('amount_currency_id')),
'amount_max' => floatval($this->get('amount_max')),
'date' => new Carbon($this->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),
'automatch' => intval($this->get('automatch')) === 1,
'active' => intval($this->get('active')) === 1,
];
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,255|uniqueForUser:bills,name';
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name,name_encrypted';
$matchRule = 'required|between:1,255|uniqueObjectForUser:bills,match,match_encrypted';
if (intval(Input::get('id')) > 0) {
$nameRule = 'required|between:1,255';
$nameRule .= ',' . intval(Input::get('id'));
$matchRule .= ',' . intval(Input::get('id'));
}
$rules = [
'name' => $nameRule,
'match' => 'required|between:1,255',
'match' => $matchRule,
'amount_min' => 'required|numeric|min:0.01',
'amount_max' => 'required|numeric|min:0.01',
'amount_currency_id' => 'required|exists:transaction_currencies,id',

View File

@ -28,13 +28,14 @@ class BudgetFormRequest extends Request
public function rules()
{
$nameRule = 'required|between:1,100|uniqueForUser:budgets,name';
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,encrypted';
if (Budget::find(Input::get('id'))) {
$nameRule = 'required|between:1,100';
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,encrypted,'.intval(Input::get('id'));
}
return [
'name' => $nameRule,
'name' => $nameRule,
'active' => 'numeric|between:0,1'
];
}
}

View File

@ -28,9 +28,9 @@ class CategoryFormRequest extends Request
public function rules()
{
$nameRule = 'required|between:1,100|uniqueForUser:categories,name';
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,encrypted';
if (Category::find(Input::get('id'))) {
$nameRule = 'required|between:1,100';
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,encrypted,'.intval(Input::get('id'));
}
return [

View File

@ -3,8 +3,10 @@
namespace FireflyIII\Http\Requests;
use Auth;
use Input;
use Carbon\Carbon;
use Exception;
use Input;
/**
* Class JournalFormRequest
*
@ -21,6 +23,28 @@ class JournalFormRequest extends Request
return Auth::check();
}
/**
* @return array
*/
public function getJournalData()
{
return [
'what' => $this->get('what'),
'description' => $this->get('description'),
'account_id' => intval($this->get('account_id')),
'account_from_id' => intval($this->get('account_from_id')),
'account_to_id' => intval($this->get('account_to_id')),
'expense_account' => $this->get('expense_account'),
'revenue_account' => $this->get('revenue_account'),
'amount' => floatval($this->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($this->get('amount_currency_id')),
'date' => new Carbon($this->get('date')),
'budget_id' => intval($this->get('budget_id')),
'category' => $this->get('category'),
];
}
/**
* @return array
* @throws Exception

View File

@ -29,33 +29,20 @@ class PiggyBankFormRequest extends Request
public function rules()
{
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:piggy_banks,name';
$nameRule = 'required|between:1,255|uniquePiggyBankForUser';
$targetDateRule = 'date';
if (intval(Input::get('id'))) {
$nameRule = 'required|between:1,255';
}
if (intval(Input::get('repeats')) == 1) {
$targetDateRule = 'required|date|after:' . date('Y-m-d');
// switch on rep_every, make sure it's not too far away.
if (!is_null(Input::get('rep_length'))) {
$end = Navigation::addPeriod(new Carbon, Input::get('rep_length'), 0);
$targetDateRule .= '|before:' . $end->format('Y-m-d');
}
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:'.intval(Input::get('id'));
}
$rules = [
'repeats' => 'required|boolean',
'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01',
'amount_currency_id' => 'exists:transaction_currencies,id',
'startdate' => 'date',
'targetdate' => $targetDateRule,
'rep_length' => 'in:day,week,quarter,month,year',
'rep_every' => 'integer|min:0|max:31',
'rep_times' => 'integer|min:0|max:99',
'reminder' => 'in:day,week,quarter,month,year',
'reminder_skip' => 'integer|min:0|max:99',
'remind_me' => 'boolean|piggyBankReminder',

View File

@ -26,7 +26,7 @@ class ProfileFormRequest extends Request
public function rules()
{
return [
'current_password' => 'required',
'current_password' => 'required',
'new_password' => 'required|confirmed',
'new_password_confirmation' => 'required',
];

View File

@ -3,13 +3,13 @@ use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\TransactionJournal;
/*
* Back home.

View File

@ -107,7 +107,7 @@ Route::bind(
where('piggy_banks.id', $value)
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', Auth::user()->id)
->where('repeats', 0)->first(['piggy_banks.*']);
->first(['piggy_banks.*']);
}
return null;
@ -132,7 +132,7 @@ Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'r
Route::controllers(
[
'auth' => 'Auth\AuthController',
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]
);
@ -142,7 +142,7 @@ Route::controllers(
* Home Controller
*/
Route::group(
['middleware' => ['auth', 'range', 'reminders','piggybanks']], function () {
['middleware' => ['auth', 'range', 'reminders', 'piggybanks']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
@ -167,6 +167,7 @@ Route::group(
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']);
Route::get('/bills/add/{bill}', ['uses' => 'BillController@add', 'as' => 'bills.add']);
Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);

View File

@ -1,10 +1,13 @@
<?php namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\JoinClause;
use Watson\Validating\ValidatingTrait;
use Crypt;
/**
* Class Account
*
@ -14,47 +17,44 @@ class Account extends Model
{
use SoftDeletes, ValidatingTrait;
protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance'];
protected $rules
= [
= [
'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id',
'name' => 'required|between:1,1024|uniqueAccountForUser',
'active' => 'required|boolean'
];
protected $fillable = ['user_id', 'account_type_id', 'name', 'active'];
/**
* @param $fieldName
* @param array $fields
*
* @return string|null
* @return Account|null
*/
public function getMeta($fieldName)
public static function firstOrCreateEncrypted(array $fields)
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
return $meta->data;
// everything but the name:
$query = Account::orderBy('id');
foreach ($fields as $name => $value) {
if ($name != 'name') {
$query->where($name, $value);
}
}
$set = $query->get(['accounts.*']);
/** @var Account $account */
foreach ($set as $account) {
if ($account->name == $fields['name']) {
return $account;
}
}
// create it!
$account = Account::create($fields);
if (is_null($account->id)) {
// could not create account:
App::abort(500, 'Could not create new account with data: ' . json_encode($fields));
return null;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if ($this->encrypted) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
@ -81,6 +81,48 @@ class Account extends Model
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @param $fieldName
*
* @return string|null
*/
public function getMeta($fieldName)
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
return $meta->data;
}
}
return null;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBanks()
{
return $this->hasMany('FireflyIII\Models\PiggyBank');
}
/**
* @param EloquentBuilder $query
* @param array $types
@ -94,6 +136,31 @@ class Account extends Model
$query->whereIn('account_types.type', $types);
}
/**
* @param EloquentBuilder $query
* @param string $name
* @param string $value
*/
public function scopeHasMetaValue(EloquentBuilder $query, $name, $value)
{
$joinName = str_replace('.', '_', $name);
$query->leftJoin(
'account_meta as ' . $joinName, function (JoinClause $join) use ($joinName, $name) {
$join->on($joinName . '.account_id', '=', 'accounts.id')->where($joinName . '.name', '=', $name);
}
);
$query->where($joinName . '.data', json_encode($value));
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
@ -110,12 +177,4 @@ class Account extends Model
return $this->belongsTo('FireflyIII\User');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBanks()
{
return $this->hasMany('FireflyIII\Models\PiggyBank');
}
}

View File

@ -14,12 +14,12 @@ class AccountMeta extends Model
use ValidatingTrait;
protected $fillable = ['account_id', 'name', 'data'];
protected $rules
= [
= [
'account_id' => 'required|exists:accounts,id',
'name' => 'required|between:1,100',
'data' => 'required'
];
protected $table = 'account_meta';
protected $table = 'account_meta';
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model;
/**
@ -10,7 +11,8 @@ use Illuminate\Database\Eloquent\Model;
class Bill extends Model
{
protected $fillable = ['name', 'match', 'amount_min','user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',];
protected $fillable
= ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',];
/**
* @return array
@ -20,6 +22,58 @@ class Bill extends Model
return ['created_at', 'updated_at', 'date'];
}
/**
* @param $value
*
* @return string
*/
public function getMatchAttribute($value)
{
if (intval($this->match_encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->name_encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @param $value
*/
public function setMatchAttribute($value)
{
$this->attributes['match'] = Crypt::encrypt($value);
$this->attributes['match_encrypted'] = true;
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['name_encrypted'] = true;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -31,6 +32,23 @@ class Budget extends Model
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
@ -39,6 +57,15 @@ class Budget extends Model
return $this->hasManyThrough('FireflyIII\Models\LimitRepetition', 'FireflyIII\Models\BudgetLimit', 'budget_id');
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/

View File

@ -2,7 +2,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Crypt;
/**
* Class Category
*
@ -38,4 +38,29 @@ class Category extends Model
return $this->belongsTo('FireflyIII\User');
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
}

View File

@ -12,7 +12,7 @@ class Component extends Model
{
use SoftDeletes;
protected $fillable = ['user_id', 'name','class'];
protected $fillable = ['user_id', 'name', 'class'];
/**
* @return array

View File

@ -1,11 +1,8 @@
<?php namespace FireflyIII\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App;
use Log;
use Crypt;
/**
* Class PiggyBank
*
@ -16,8 +13,7 @@ class PiggyBank extends Model
use SoftDeletes;
protected $fillable
= ['repeats', 'name', 'account_id', 'rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me',
'rep_length'];
= ['name', 'account_id', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
@ -38,14 +34,10 @@ class PiggyBank extends Model
return $this->currentRep;
}
// repeating piggy banks are no longer supported.
if (intval($this->repeats) === 0) {
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
$this->currentRep = $rep;
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
$this->currentRep = $rep;
return $rep;
} else {
Log::error('Tried to work with a piggy bank with a repeats=1 value! (id is '.$this->id.')');
}
return $rep;
}
@ -91,4 +83,29 @@ class PiggyBank extends Model
{
return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable');
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
}

View File

@ -3,7 +3,7 @@
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Crypt;
/**
* Class Reminder
*
@ -40,6 +40,9 @@ class Reminder extends Model
*/
public function getMetadataAttribute($value)
{
if (intval($this->encrypted) == 1) {
return json_decode(Crypt::decrypt($value));
}
return json_decode($value);
}
@ -86,7 +89,8 @@ class Reminder extends Model
*/
public function setMetadataAttribute($value)
{
$this->attributes['metadata'] = json_encode($value);
$this->attributes['encrypted'] = true;
$this->attributes['metadata'] = Crypt::encrypt(json_encode($value));
}
/**

View File

@ -3,7 +3,8 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Carbon\Carbon;
/**
* Class Transaction
*
@ -30,6 +31,28 @@ class Transaction extends Model
return $this->belongsTo('FireflyIII\Models\Account');
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeAfter(EloquentBuilder $query, Carbon $date)
{
return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00'));
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeBefore(EloquentBuilder $query, Carbon $date)
{
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
}
/**
* @return array
*/

View File

@ -12,7 +12,7 @@ use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Log;
use Reminder;
/**
* Class EventServiceProvider
*
@ -28,7 +28,7 @@ class EventServiceProvider extends ServiceProvider
*/
protected $listen
= [
'FireflyIII\Events\JournalSaved' => [
'FireflyIII\Events\JournalSaved' => [
'FireflyIII\Handlers\Events\RescanJournal',
'FireflyIII\Handlers\Events\UpdateJournalConnection',
@ -60,6 +60,14 @@ class EventServiceProvider extends ServiceProvider
);
PiggyBank::deleting(function(PiggyBank $piggyBank) {
$reminders = $piggyBank->reminders()->get();
/** @var Reminder $reminder */
foreach($reminders as $reminder) {
$reminder->delete();
}
});
Account::deleted(
function (Account $account) {

View File

@ -48,6 +48,18 @@ class AccountRepository implements AccountRepositoryInterface
return true;
}
/**
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
*/
public function getFirstTransaction(TransactionJournal $journal, Account $account)
{
return $journal->transactions()->where('account_id', $account->id)->first();
}
/**
* @param Preference $preference
*
@ -106,9 +118,9 @@ class AccountRepository implements AccountRepositoryInterface
->withRelevantData()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC');
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
$query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
@ -119,7 +131,6 @@ class AccountRepository implements AccountRepositoryInterface
return $paginator;
}
/**
@ -134,8 +145,8 @@ class AccountRepository implements AccountRepositoryInterface
->where('account_meta.name', 'accountRole')
->where('account_meta.data', '"savingAsset"')
->get(['accounts.*']);
$start = clone Session::get('start');
$end = clone Session::get('end');
$start = clone Session::get('start', new Carbon);
$end = clone Session::get('end', new Carbon);
$accounts->each(
function (Account $account) use ($start, $end) {
@ -171,7 +182,7 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function leftOnAccount(Account $account)
{
$balance = \Steam::balance($account);
$balance = \Steam::balance($account, null, true);
/** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;
@ -209,10 +220,11 @@ class AccountRepository implements AccountRepositoryInterface
if ($data['openingBalance'] != 0) {
$type = $data['openingBalance'] < 0 ? 'expense' : 'revenue';
$opposingData = [
'user' => $data['user'],
'accountType' => $type,
'name' => $data['name'] . ' initial balance',
'active' => false,
'user' => $data['user'],
'accountType' => $type,
'virtual_balance' => $data['virtualBalance'],
'name' => $data['name'] . ' initial balance',
'active' => false,
];
$opposing = $this->storeAccount($opposingData);
$this->storeInitialBalance($newAccount, $opposing, $data);
@ -230,8 +242,9 @@ class AccountRepository implements AccountRepositoryInterface
public function update(Account $account, array $data)
{
// update the account:
$account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false;
$account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false;
$account->virtual_balance = $data['virtualBalance'];
$account->save();
// update meta data:
@ -308,17 +321,21 @@ class AccountRepository implements AccountRepositoryInterface
*/
protected function storeMetadata(Account $account, array $data)
{
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => 'accountRole',
'data' => $data['accountRole']
]
);
if (!$metaData->isValid()) {
App::abort(500);
$validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType'];
foreach ($validFields as $field) {
if (isset($data[$field])) {
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => $field,
'data' => $data[$field]
]
);
$metaData->save();
}
}
$metaData->save();
}
/**
@ -399,30 +416,28 @@ class AccountRepository implements AccountRepositoryInterface
*/
protected function updateMetadata(Account $account, array $data)
{
$metaEntries = $account->accountMeta()->get();
$validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType'];
$updated = false;
/** @var AccountMeta $entry */
foreach ($metaEntries as $entry) {
if ($entry->name == 'accountRole') {
$entry->data = $data['accountRole'];
$updated = true;
foreach ($validFields as $field) {
$entry = $account->accountMeta()->where('name', $field)->first();
// update if new data is present:
if ($entry && isset($data[$field])) {
$entry->data = $data[$field];
$entry->save();
}
}
if ($updated === false) {
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => 'accountRole',
'data' => $data['accountRole']
]
);
if (!$metaData->isValid()) {
App::abort(500);
// no entry but data present?
if (!$entry && isset($data[$field])) {
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => $field,
'data' => $data[$field]
]
);
$metaData->save();
}
$metaData->save();
}
}

View File

@ -2,11 +2,13 @@
namespace FireflyIII\Repositories\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Preference;
use Illuminate\Support\Collection;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Interface AccountRepositoryInterface
*
@ -85,4 +87,13 @@ interface AccountRepositoryInterface
* @return Collection
*/
public function getSavingsAccounts();
/**
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
*/
public function getFirstTransaction(TransactionJournal $journal, Account $account);
}

View File

@ -11,7 +11,8 @@ use FireflyIII\Models\TransactionJournal;
*
* @package FireflyIII\Repositories\Bill
*/
interface BillRepositoryInterface {
interface BillRepositoryInterface
{
/**
* @param Bill $bill
@ -25,7 +26,7 @@ interface BillRepositoryInterface {
* and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and
* you give 1st and the 31st of that month as argument, you'll get one response, matching the range of your bill.
*
* @param Bill $bill
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*

View File

@ -16,6 +16,31 @@ use Illuminate\Pagination\LengthAwarePaginator;
class BudgetRepository implements BudgetRepositoryInterface
{
/**
* @return void
*/
public function cleanupBudgets()
{
$limits = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')->get(['budget_limits.*']);
// loop budget limits:
$found = [];
/** @var BudgetLimit $limit */
foreach ($limits as $limit) {
$key = $limit->budget_id . '-' . $limit->startdate;
if (isset($found[$key])) {
$limit->delete();
} else {
$found[$key] = true;
}
unset($key);
}
// delete limits with amount 0:
BudgetLimit::where('amount',0)->delete();
}
/**
* @param Budget $budget
*
@ -43,9 +68,9 @@ class BudgetRepository implements BudgetRepositoryInterface
$setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC');
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
$countQuery = $budget->transactionJournals();
@ -57,6 +82,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$set = $setQuery->get(['transaction_journals.*']);
$count = $countQuery->count();
return new LengthAwarePaginator($set, $count, $take, $offset);
}
@ -103,7 +129,8 @@ class BudgetRepository implements BudgetRepositoryInterface
public function update(Budget $budget, array $data)
{
// update the account:
$budget->name = $data['name'];
$budget->name = $data['name'];
$budget->active = $data['active'];
$budget->save();
return $budget;
@ -118,10 +145,12 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
public function updateLimitAmount(Budget $budget, Carbon $date, $amount)
{
// there should be a budget limit for this startdate:
/** @var BudgetLimit $limit */
$limit = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
$limit = $budget->budgetlimits()->where('budget_limits.startdate', $date)->first(['budget_limits.*']);
if (!$limit) {
// create one!
// if not, create one!
$limit = new BudgetLimit;
$limit->budget()->associate($budget);
$limit->startdate = $date;
@ -129,6 +158,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$limit->repeat_freq = 'monthly';
$limit->repeats = 0;
$limit->save();
// likewise, there should be a limit repetition to match the end date
// (which is always the end of the month) but that is caught by an event.
} else {
if ($amount > 0) {
$limit->amount = $amount;

View File

@ -20,6 +20,11 @@ interface BudgetRepositoryInterface
*/
public function destroy(Budget $budget);
/**
* @return void
*/
public function cleanupBudgets();
/**
* @param Budget $budget
* @param Carbon $date

View File

@ -2,6 +2,7 @@
namespace FireflyIII\Repositories\Journal;
use App;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
@ -11,6 +12,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log;
/**
* Class JournalRepository
@ -20,6 +22,16 @@ use Illuminate\Support\Collection;
class JournalRepository implements JournalRepositoryInterface
{
/**
* Get users first transaction journal
*
* @return TransactionJournal
*/
public function first()
{
return Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
}
/**
*
* Get the account_id, which is the asset account that paid for the transaction.
@ -139,41 +151,7 @@ class JournalRepository implements JournalRepositoryInterface
}
// store accounts (depends on type)
switch ($transactionType->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
list($from, $to) = $this->storeAccounts($transactionType, $data);
// store accompanying transactions.
Transaction::create( // first transaction.
@ -198,7 +176,6 @@ class JournalRepository implements JournalRepositoryInterface
}
/**
* @param TransactionJournal $journal
* @param array $data
@ -228,41 +205,7 @@ class JournalRepository implements JournalRepositoryInterface
}
// store accounts (depends on type)
switch ($journal->transactionType->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
list($from, $to) = $this->storeAccounts($journal->transactionType, $data);
// update the from and to transaction.
/** @var Transaction $transaction */
@ -286,4 +229,65 @@ class JournalRepository implements JournalRepositoryInterface
return $journal;
}
/**
* @param TransactionType $type
* @param array $data
*
* @return array
*/
protected function storeAccounts(TransactionType $type, array $data)
{
$from = null;
$to = null;
switch ($type->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
if (is_null($to->id)) {
Log::error('"to"-account is null, so we cannot continue!');
App::abort(500, '"to"-account is null, so we cannot continue!');
}
if (is_null($from->id)) {
Log::error('"from"-account is null, so we cannot continue!');
App::abort(500, '"from"-account is null, so we cannot continue!');
}
return [$from, $to];
}
}

View File

@ -3,6 +3,7 @@
namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
/**
@ -44,4 +45,10 @@ interface JournalRepositoryInterface
* @return mixed
*/
public function update(TransactionJournal $journal, array $data);
/**
* Get users first transaction journal
* @return TransactionJournal
*/
public function first();
}

View File

@ -111,7 +111,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function setOrder($id, $order)
{
$piggyBank = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', Auth::user()->id)
->where('piggy_banks.id',$id)->first(['piggy_banks.*']);
->where('piggy_banks.id', $id)->first(['piggy_banks.*']);
if ($piggyBank) {
$piggyBank->order = $order;
$piggyBank->save();
@ -153,9 +153,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
$piggyBank->targetdate = $data['targetdate'];
$piggyBank->reminder = $data['reminder'];
$piggyBank->startdate = $data['startdate'];
$piggyBank->rep_length = isset($data['rep_length']) ? $data['rep_length'] : null;
$piggyBank->rep_every = isset($data['rep_every']) ? $data['rep_every'] : null;
$piggyBank->rep_times = isset($data['rep_times']) ? $data['rep_times'] : null;
$piggyBank->remind_me = isset($data['remind_me']) && $data['remind_me'] == '1' ? 1 : 0;
$piggyBank->save();

View File

@ -2,10 +2,9 @@
namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\PiggyBankRepetition;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
/**
* Class PiggyBankPart

View File

@ -6,6 +6,7 @@ use Cache;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Preferences as Prefs;
/**
@ -134,6 +135,14 @@ class Amount
return $this->formatWithSymbol($symbol, $amount, $coloured);
}
/**
* @return Collection
*/
public function getAllCurrencies()
{
return TransactionCurrency::orderBy('code', 'ASC')->get();
}
/**
* @return string
*/

View File

@ -34,7 +34,7 @@ class ExpandedForm
$options['step'] = 'any';
$options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = TransactionCurrency::orderBy('code', 'ASC')->get();
$currencies = Amt::getAllCurrencies();
$html = View::make('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
@ -53,18 +53,22 @@ class ExpandedForm
return $options['label'];
}
$labels = [
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'piggy_bank_id' => 'Piggy bank'];
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'virtualBalance' => 'Virtual balance',
'targetamount' => 'Target amount',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank'];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
@ -143,7 +147,7 @@ class ExpandedForm
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = TransactionCurrency::orderBy('code', 'ASC')->get();
$currencies = Amt::getAllCurrencies();
$html = View::make('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
@ -190,6 +194,24 @@ class ExpandedForm
return $html;
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function month($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$html = View::make('form.month', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
* @param $name
* @param null $value

View File

@ -344,6 +344,48 @@ class Navigation
throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq);
}
/**
* @param Carbon $theDate
* @param $repeatFreq
* @param int $subtract
*
* @return Carbon
* @throws FireflyException
*/
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
$functionMap = [
'daily' => 'subDays',
'week' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function($subtract);
return $date;
}
if (isset($modifierMap[$repeatFreq])) {
$subtract = $subtract * $modifierMap[$repeatFreq];
$date->subMonths($subtract);
return $date;
}
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
}
/**
* @param $range
* @param Carbon $start
@ -414,47 +456,5 @@ class Navigation
throw new FireflyException('updateStartDate cannot handle $range ' . $range);
}
/**
* @param Carbon $theDate
* @param $repeatFreq
* @param int $subtract
*
* @return Carbon
* @throws FireflyException
*/
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
$functionMap = [
'daily' => 'subDays',
'week' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function($subtract);
return $date;
}
if (isset($modifierMap[$repeatFreq])) {
$subtract = $subtract * $modifierMap[$repeatFreq];
$date->subMonths($subtract);
return $date;
}
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
}
}

View File

@ -16,7 +16,7 @@ class Preferences
* @param $name
* @param null $default
*
* @return null|\Preference
* @return null|Preference
*/
public function get($name, $default = null)
{
@ -37,7 +37,7 @@ class Preferences
* @param $name
* @param $value
*
* @return \Preference
* @return Preference
*/
public function set($name, $value)
{

View File

@ -112,8 +112,8 @@ class Search implements SearchInterface
)->get();
// encrypted
$all = Auth::user()->transactionjournals()->withRelevantData()->where('encrypted', 1)->get();
$set = $all->filter(
$all = Auth::user()->transactionjournals()->withRelevantData()->where('encrypted', 1)->get();
$set = $all->filter(
function (TransactionJournal $journal) use ($words) {
foreach ($words as $word) {
$haystack = strtolower($journal->description);

View File

@ -9,7 +9,8 @@ use Illuminate\Support\Collection;
*
* @package FireflyIII\Support\Search
*/
interface SearchInterface {
interface SearchInterface
{
/**
* @param array $words
*

View File

@ -19,10 +19,11 @@ class Steam
*
* @param Account $account
* @param Carbon $date
* @param bool $ignoreVirtualBalance
*
* @return float
*/
public function balance(Account $account, Carbon $date = null)
public function balance(Account $account, Carbon $date = null, $ignoreVirtualBalance = false)
{
$date = is_null($date) ? Carbon::now() : $date;
@ -47,6 +48,9 @@ class Steam
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
);
if (!$ignoreVirtualBalance) {
$balance += floatval($account->virtual_balance);
}
return $balance;
}
@ -100,11 +104,13 @@ class Steam
if (isset($array[$id])) {
$array[$id]['amount'] += floatval($entry->amount);
$array[$id]['spent'] += floatval($entry->spent);
$array[$id]['encrypted'] = intval($entry->encrypted);
} else {
$array[$id] = [
'amount' => floatval($entry->amount),
'spent' => floatval($entry->spent),
'name' => $entry->name
'amount' => floatval($entry->amount),
'spent' => floatval($entry->spent),
'encrypted' => intval($entry->encrypted),
'name' => $entry->name
];
}
}

View File

@ -75,6 +75,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->hasManyThrough('FireflyIII\Models\PiggyBank', 'FireflyIII\Models\Account');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function transactions()
{
return $this->hasManyThrough('FireflyIII\Models\Transaction', 'FireflyIII\Models\TransactionJournal');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@ -5,10 +5,13 @@ namespace FireflyIII\Validation;
use Auth;
use Carbon\Carbon;
use Config;
use Crypt;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Validation\Validator;
use Input;
use Log;
use Navigation;
/**
@ -28,6 +31,7 @@ class FireflyValidator extends Validator
*/
public function validateBelongsToUser($attribute, $value, $parameters)
{
$count = DB::table($parameters[0])->where('user_id', Auth::user()->id)->where('id', $value)->count();
if ($count == 1) {
return true;
@ -79,44 +83,62 @@ class FireflyValidator extends Validator
*/
public function validateUniqueAccountForUser($attribute, $value, $parameters)
{
// get account type from data, we must have this:
$validTypes = array_keys(Config::get('firefly.subTitlesByIdentifier'));
$type = null;
$type = isset($this->data['what']) && in_array($this->data['what'], $validTypes) ? $this->data['what'] : null;
// some fallback:
if (is_null($type)) {
$type = in_array(Input::get('what'), $validTypes) ? Input::get('what') : null;
}
// still null?
if (is_null($type)) {
// find by other field:
$type = isset($this->data['account_type_id']) ? $this->data['account_type_id'] : 0;
$dbType = AccountType::find($type);
} else {
$longType = Config::get('firefly.accountTypeByIdentifier.' . $type);
$dbType = AccountType::whereType($longType)->first();
/**
* Switch on different cases on which this method can respond:
*/
$hasWhat = isset($this->data['what']);
$hasAccountTypeId = isset($this->data['account_type_id']) && isset($this->data['name']);
$hasAccountId = isset($this->data['id']);
$ignoreId = 0;
if ($hasWhat) {
$search = Config::get('firefly.accountTypeByIdentifier.' . $this->data['what']);
$type = AccountType::whereType($search)->first();
// this field can be used to find the exact type, and continue.
}
if (is_null($dbType)) {
if ($hasAccountTypeId) {
$type = AccountType::find($this->data['account_type_id']);
}
if ($hasAccountId) {
/** @var Account $account */
$account = Account::find($this->data['id']);
$ignoreId = intval($this->data['id']);
$type = AccountType::find($account->account_type_id);
unset($account);
}
/**
* Try to decrypt data just in case:
*/
try {
$value = Crypt::decrypt($value);
} catch (DecryptException $e) {
}
if (is_null($type)) {
Log::error('Could not determine type of account to validate.');
return false;
}
// user id?
$userId = Auth::check() ? Auth::user()->id : $this->data['user_id'];
$query = DB::table('accounts')->where('name', $value)->where('account_type_id', $dbType->id)->where('user_id', $userId);
if (isset($parameters[0])) {
$query->where('id', '!=', $parameters[0]);
}
$count = $query->count();
if ($count == 0) {
return true;
// get all accounts with this type, and find the name.
$userId = Auth::check() ? Auth::user()->id : 0;
$set = Account::where('account_type_id', $type->id)->where('id', '!=', $ignoreId)->where('user_id', $userId)->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $value) {
return false;
}
}
return false;
return true;
}
@ -143,6 +165,46 @@ class FireflyValidator extends Validator
}
/**
* Validate an object and its unicity. Checks for encryption / encrypted values as well.
*
* parameter 0: the table
* parameter 1: the field
* parameter 2: the encrypted / not encrypted boolean. Defaults to "encrypted".
* parameter 3: an id to ignore (when editing)
*
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validateUniqueObjectForUser($attribute, $value, $parameters)
{
$table = $parameters[0];
$field = $parameters[1];
$encrypted = isset($parameters[2]) ? $parameters[2] : 'encrypted';
$exclude = isset($parameters[3]) ? $parameters[3] : null;
$query = DB::table($table)->where('user_id', Auth::user()->id);
if (!is_null($exclude)) {
$query->where('id', '!=', $exclude);
}
$set = $query->get();
foreach ($set as $entry) {
$isEncrypted = intval($entry->$encrypted) == 1 ? true : false;
$checkValue = $isEncrypted ? Crypt::decrypt($entry->$field) : $entry->$field;
if ($checkValue == $value) {
return false;
}
}
return true;
}
/**
* @param $attribute
* @param $value
@ -152,18 +214,24 @@ class FireflyValidator extends Validator
*/
public function validateUniquePiggyBankForUser($attribute, $value, $parameters)
{
$query = DB::table($parameters[0])->where('piggy_banks.'.$parameters[1], $value);
$exclude = isset($parameters[0]) ? $parameters[0] : null;
$query = DB::table('piggy_banks');
$query->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id');
$query->where('accounts.user_id', Auth::user()->id);
if (isset($paramers[2])) {
$query->where('piggy_banks.id', '!=', $parameters[2]);
if (!is_null($exclude)) {
$query->where('piggy_banks.id', '!=', $exclude);
}
$count = $query->count();
if ($count == 0) {
return true;
$set = $query->get(['piggy_banks.*']);
foreach($set as $entry) {
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$checkValue = $isEncrypted ? Crypt::decrypt($entry->name) : $entry->name;
if($checkValue == $value) {
return false;
}
}
return false;
return true;
}
}

View File

@ -33,7 +33,11 @@
"barryvdh/laravel-ide-helper": "~2.0",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"satooshi/php-coveralls": "0.6.1"
"satooshi/php-coveralls": "0.6.1",
"mockery/mockery": "0.9.*",
"league/factory-muffin": "~2.1"
},
"autoload": {
"classmap": [

477
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "b77b9f717b25e1e193bdc6edb18ad492",
"hash": "0d43c4c85607c5cdc901cde2d18b75d5",
"packages": [
{
"name": "classpreloader/classpreloader",
@ -412,16 +412,16 @@
},
{
"name": "doctrine/common",
"version": "v2.4.2",
"version": "v2.5.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/common.git",
"reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b"
"reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/common/zipball/5db6ab40e4c531f14dad4ca96a394dfce5d4255b",
"reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b",
"url": "https://api.github.com/repos/doctrine/common/zipball/cd8daf2501e10c63dced7b8b9b905844316ae9d3",
"reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3",
"shasum": ""
},
"require": {
@ -438,7 +438,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4.x-dev"
"dev-master": "2.6.x-dev"
}
},
"autoload": {
@ -451,17 +451,6 @@
"MIT"
],
"authors": [
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com",
"homepage": "http://www.jwage.com/",
"role": "Creator"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com",
"homepage": "http://www.instaclick.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
@ -470,11 +459,17 @@
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh",
"role": "Developer of wrapped JMSSerializerBundle"
"email": "schmittjoh@gmail.com"
}
],
"description": "Common Library for Doctrine projects",
@ -486,7 +481,7 @@
"persistence",
"spl"
],
"time": "2014-05-21 19:28:51"
"time": "2015-04-02 19:55:44"
},
{
"name": "doctrine/dbal",
@ -950,16 +945,16 @@
},
{
"name": "laravel/framework",
"version": "v5.0.23",
"version": "v5.0.26",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "59219f7afb60be05d74ce01fcb5d2440f7a1b13d"
"reference": "8e53c33e144f94032cc6ecbfee0be2a96ed63be0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/59219f7afb60be05d74ce01fcb5d2440f7a1b13d",
"reference": "59219f7afb60be05d74ce01fcb5d2440f7a1b13d",
"url": "https://api.github.com/repos/laravel/framework/zipball/8e53c33e144f94032cc6ecbfee0be2a96ed63be0",
"reference": "8e53c33e144f94032cc6ecbfee0be2a96ed63be0",
"shasum": ""
},
"require": {
@ -1072,7 +1067,7 @@
"framework",
"laravel"
],
"time": "2015-03-28 16:56:59"
"time": "2015-04-03 02:58:05"
},
{
"name": "league/commonmark",
@ -1135,16 +1130,16 @@
},
{
"name": "league/flysystem",
"version": "1.0.2",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "51cd7cd7ee0defbaafc6ec0d3620110a5d71e11a"
"reference": "3c2400a99ccc3be6884d40361890010449c6b447"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/51cd7cd7ee0defbaafc6ec0d3620110a5d71e11a",
"reference": "51cd7cd7ee0defbaafc6ec0d3620110a5d71e11a",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3c2400a99ccc3be6884d40361890010449c6b447",
"reference": "3c2400a99ccc3be6884d40361890010449c6b447",
"shasum": ""
},
"require": {
@ -1154,7 +1149,7 @@
"ext-fileinfo": "*",
"league/phpunit-coverage-listener": "~1.1",
"mockery/mockery": "~0.9",
"phpspec/phpspec": "~2.0.0",
"phpspec/phpspec": "~2.0",
"phpspec/prophecy-phpunit": "~1.0",
"phpunit/phpunit": "~4.1",
"predis/predis": "~1.0",
@ -1214,7 +1209,7 @@
"sftp",
"storage"
],
"time": "2015-03-10 11:04:14"
"time": "2015-03-29 14:01:43"
},
{
"name": "monolog/monolog",
@ -1588,17 +1583,17 @@
},
{
"name": "symfony/console",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "53f86497ccd01677e22435cfb7262599450a90d1"
"reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/53f86497ccd01677e22435cfb7262599450a90d1",
"reference": "53f86497ccd01677e22435cfb7262599450a90d1",
"url": "https://api.github.com/repos/symfony/Console/zipball/5b91dc4ed5eb08553f57f6df04c4730a73992667",
"reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667",
"shasum": ""
},
"require": {
@ -1642,21 +1637,21 @@
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/debug",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Debug",
"source": {
"type": "git",
"url": "https://github.com/symfony/Debug.git",
"reference": "5c1570dea188ade0c6c5e874c2f0a6570587aa1c"
"reference": "d49a46a20a8f0544aedac54466750ad787d3d3e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Debug/zipball/5c1570dea188ade0c6c5e874c2f0a6570587aa1c",
"reference": "5c1570dea188ade0c6c5e874c2f0a6570587aa1c",
"url": "https://api.github.com/repos/symfony/Debug/zipball/d49a46a20a8f0544aedac54466750ad787d3d3e3",
"reference": "d49a46a20a8f0544aedac54466750ad787d3d3e3",
"shasum": ""
},
"require": {
@ -1703,11 +1698,11 @@
],
"description": "Symfony Debug Component",
"homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22"
"time": "2015-03-22 16:55:57"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
@ -1766,17 +1761,17 @@
},
{
"name": "symfony/filesystem",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "fdc5f151bc2db066b51870d5bea3773d915ced0b"
"reference": "4983964b3693e4f13449cb3800c64a9112c301b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/fdc5f151bc2db066b51870d5bea3773d915ced0b",
"reference": "fdc5f151bc2db066b51870d5bea3773d915ced0b",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/4983964b3693e4f13449cb3800c64a9112c301b4",
"reference": "4983964b3693e4f13449cb3800c64a9112c301b4",
"shasum": ""
},
"require": {
@ -1812,21 +1807,21 @@
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44"
"time": "2015-03-22 16:55:57"
},
{
"name": "symfony/finder",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Finder",
"source": {
"type": "git",
"url": "https://github.com/symfony/Finder.git",
"reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e"
"reference": "5dbe2e73a580618f5b4880fda93406eed25de251"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/bebc7479c566fa4f14b9bcef9e32e719eabec74e",
"reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e",
"url": "https://api.github.com/repos/symfony/Finder/zipball/5dbe2e73a580618f5b4880fda93406eed25de251",
"reference": "5dbe2e73a580618f5b4880fda93406eed25de251",
"shasum": ""
},
"require": {
@ -1862,21 +1857,21 @@
],
"description": "Symfony Finder Component",
"homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/http-foundation",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/HttpFoundation",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpFoundation.git",
"reference": "d527885e37b55ec0e3dc6f4b70566d0f9b2f2388"
"reference": "8a6337233f08f7520de97f4ffd6f00e947d892f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/d527885e37b55ec0e3dc6f4b70566d0f9b2f2388",
"reference": "d527885e37b55ec0e3dc6f4b70566d0f9b2f2388",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/8a6337233f08f7520de97f4ffd6f00e947d892f9",
"reference": "8a6337233f08f7520de97f4ffd6f00e947d892f9",
"shasum": ""
},
"require": {
@ -1916,21 +1911,21 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22"
"time": "2015-04-01 16:50:12"
},
{
"name": "symfony/http-kernel",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/HttpKernel",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpKernel.git",
"reference": "6f7b2d3ba8bf02cf77edb399696e85ef24a888a4"
"reference": "3829cacfe21eaf3f73604a62d79183d1f6e792c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/6f7b2d3ba8bf02cf77edb399696e85ef24a888a4",
"reference": "6f7b2d3ba8bf02cf77edb399696e85ef24a888a4",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/3829cacfe21eaf3f73604a62d79183d1f6e792c4",
"reference": "3829cacfe21eaf3f73604a62d79183d1f6e792c4",
"shasum": ""
},
"require": {
@ -1994,21 +1989,21 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "http://symfony.com",
"time": "2015-03-17 14:58:46"
"time": "2015-04-01 16:55:26"
},
{
"name": "symfony/process",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc"
"reference": "a8bebaec1a9dc6cde53e0250e32917579b0be552"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc",
"reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc",
"url": "https://api.github.com/repos/symfony/Process/zipball/a8bebaec1a9dc6cde53e0250e32917579b0be552",
"reference": "a8bebaec1a9dc6cde53e0250e32917579b0be552",
"shasum": ""
},
"require": {
@ -2044,21 +2039,21 @@
],
"description": "Symfony Process Component",
"homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/routing",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
"url": "https://github.com/symfony/Routing.git",
"reference": "a7f3eb540e5c553c3c95993c6fc2e7edb2f3b9d2"
"reference": "4e173a645b63ff60a124f3741b4f15feebd908fa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/a7f3eb540e5c553c3c95993c6fc2e7edb2f3b9d2",
"reference": "a7f3eb540e5c553c3c95993c6fc2e7edb2f3b9d2",
"url": "https://api.github.com/repos/symfony/Routing/zipball/4e173a645b63ff60a124f3741b4f15feebd908fa",
"reference": "4e173a645b63ff60a124f3741b4f15feebd908fa",
"shasum": ""
},
"require": {
@ -2113,21 +2108,21 @@
"uri",
"url"
],
"time": "2015-03-13 17:37:22"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/security-core",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Security/Core",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
"reference": "889290a5c00d3f174cc73ce13a11a0a6406939e9"
"reference": "d25c17db741f58c0f615e52006a47f6fb23cd9b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/security-core/zipball/889290a5c00d3f174cc73ce13a11a0a6406939e9",
"reference": "889290a5c00d3f174cc73ce13a11a0a6406939e9",
"url": "https://api.github.com/repos/symfony/security-core/zipball/d25c17db741f58c0f615e52006a47f6fb23cd9b3",
"reference": "d25c17db741f58c0f615e52006a47f6fb23cd9b3",
"shasum": ""
},
"require": {
@ -2177,21 +2172,21 @@
],
"description": "Symfony Security Component - Core Library",
"homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/translation",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Translation",
"source": {
"type": "git",
"url": "https://github.com/symfony/Translation.git",
"reference": "043db5f1eef9598d1bc1d75b93304984c003d7d9"
"reference": "bd939f05cdaca128f4ddbae1b447d6f0203b60af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Translation/zipball/043db5f1eef9598d1bc1d75b93304984c003d7d9",
"reference": "043db5f1eef9598d1bc1d75b93304984c003d7d9",
"url": "https://api.github.com/repos/symfony/Translation/zipball/bd939f05cdaca128f4ddbae1b447d6f0203b60af",
"reference": "bd939f05cdaca128f4ddbae1b447d6f0203b60af",
"shasum": ""
},
"require": {
@ -2236,21 +2231,21 @@
],
"description": "Symfony Translation Component",
"homepage": "http://symfony.com",
"time": "2015-03-14 11:42:25"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/var-dumper",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/VarDumper",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "61ee6c848fd2c623e13f59df48833f8b8bad7fda"
"reference": "aafae00236e147568832de3c65ccb94cfc836278"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/61ee6c848fd2c623e13f59df48833f8b8bad7fda",
"reference": "61ee6c848fd2c623e13f59df48833f8b8bad7fda",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/aafae00236e147568832de3c65ccb94cfc836278",
"reference": "aafae00236e147568832de3c65ccb94cfc836278",
"shasum": ""
},
"require": {
@ -2296,7 +2291,7 @@
"debug",
"dump"
],
"time": "2015-03-06 16:45:31"
"time": "2015-03-31 08:12:29"
},
{
"name": "vlucas/phpdotenv",
@ -2577,6 +2572,54 @@
],
"time": "2014-10-13 12:58:55"
},
{
"name": "fzaninotto/faker",
"version": "v1.4.0",
"source": {
"type": "git",
"url": "https://github.com/fzaninotto/Faker.git",
"reference": "010c7efedd88bf31141a02719f51fb44c732d5a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fzaninotto/Faker/zipball/010c7efedd88bf31141a02719f51fb44c732d5a0",
"reference": "010c7efedd88bf31141a02719f51fb44c732d5a0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "~1.5"
},
"type": "library",
"extra": {
"branch-alias": []
},
"autoload": {
"psr-0": {
"Faker": "src/",
"Faker\\PHPUnit": "test/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "François Zaninotto"
}
],
"description": "Faker is a PHP library that generates fake data for you.",
"keywords": [
"data",
"faker",
"fixtures"
],
"time": "2014-06-04 14:43:02"
},
{
"name": "guzzle/guzzle",
"version": "v3.9.3",
@ -2672,6 +2715,112 @@
],
"time": "2015-03-18 18:23:50"
},
{
"name": "hamcrest/hamcrest-php",
"version": "v1.2.1",
"source": {
"type": "git",
"url": "https://github.com/hamcrest/hamcrest-php.git",
"reference": "ac50c470531243944f977b8de75be0b684a9cb51"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/ac50c470531243944f977b8de75be0b684a9cb51",
"reference": "ac50c470531243944f977b8de75be0b684a9cb51",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"replace": {
"cordoval/hamcrest-php": "*",
"davedevelopment/hamcrest-php": "*",
"kodova/hamcrest-php": "*"
},
"require-dev": {
"phpunit/php-file-iterator": "1.3.3",
"satooshi/php-coveralls": "dev-master"
},
"type": "library",
"autoload": {
"classmap": [
"hamcrest"
],
"files": [
"hamcrest/Hamcrest.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"description": "This is the PHP port of Hamcrest Matchers",
"keywords": [
"test"
],
"time": "2015-01-20 19:34:09"
},
{
"name": "league/factory-muffin",
"version": "v2.1.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/factory-muffin.git",
"reference": "91f0adcdac6b5f7bf2277ac2c90f94352afe65de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/factory-muffin/zipball/91f0adcdac6b5f7bf2277ac2c90f94352afe65de",
"reference": "91f0adcdac6b5f7bf2277ac2c90f94352afe65de",
"shasum": ""
},
"require": {
"fzaninotto/faker": "1.4.*",
"php": ">=5.3.3"
},
"replace": {
"zizaco/factory-muff": "self.version"
},
"require-dev": {
"illuminate/database": "~4.1",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"illuminate/database": "Factory Muffin works well with eloquent models."
},
"type": "library",
"autoload": {
"psr-4": {
"League\\FactoryMuffin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "graham@mineuk.com"
},
{
"name": "Zizaco Zizuini",
"email": "zizaco@gmail.com"
},
{
"name": "Scott Robertson",
"email": "scottymeuk@gmail.com"
}
],
"description": "The goal of this package is to enable the rapid creation of objects for the purpose of testing.",
"homepage": "http://factory-muffin.thephpleague.com/",
"keywords": [
"factory",
"laravel",
"testing"
],
"time": "2014-09-18 18:29:06"
},
{
"name": "maximebf/debugbar",
"version": "v1.10.4",
@ -2728,6 +2877,71 @@
],
"time": "2015-02-05 07:51:20"
},
{
"name": "mockery/mockery",
"version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/padraic/mockery.git",
"reference": "70bba85e4aabc9449626651f48b9018ede04f86b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b",
"reference": "70bba85e4aabc9449626651f48b9018ede04f86b",
"shasum": ""
},
"require": {
"hamcrest/hamcrest-php": "~1.1",
"lib-pcre": ">=7.0",
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.9.x-dev"
}
},
"autoload": {
"psr-0": {
"Mockery": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Pádraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "http://blog.astrumfutura.com"
},
{
"name": "Dave Marshall",
"email": "dave.marshall@atstsolutions.co.uk",
"homepage": "http://davedevelopment.co.uk"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
"homepage": "http://github.com/padraic/mockery",
"keywords": [
"BDD",
"TDD",
"library",
"mock",
"mock objects",
"mockery",
"stub",
"test",
"test double",
"testing"
],
"time": "2015-04-02 19:54:00"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "2.0.4",
@ -2889,21 +3103,22 @@
},
{
"name": "phpspec/prophecy",
"version": "v1.3.1",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9"
"reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/9ca52329bcdd1500de24427542577ebf3fc2f1c9",
"reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5",
"reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5",
"shasum": ""
},
"require": {
"doctrine/instantiator": "~1.0,>=1.0.2",
"phpdocumentor/reflection-docblock": "~2.0"
"doctrine/instantiator": "^1.0.2",
"phpdocumentor/reflection-docblock": "~2.0",
"sebastian/comparator": "~1.1"
},
"require-dev": {
"phpspec/phpspec": "~2.0"
@ -2911,7 +3126,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
"dev-master": "1.4.x-dev"
}
},
"autoload": {
@ -2935,7 +3150,7 @@
}
],
"description": "Highly opinionated mocking framework for PHP 5.3+",
"homepage": "http://phpspec.org",
"homepage": "https://github.com/phpspec/prophecy",
"keywords": [
"Double",
"Dummy",
@ -2944,7 +3159,7 @@
"spy",
"stub"
],
"time": "2014-11-17 16:23:49"
"time": "2015-03-27 19:31:25"
},
{
"name": "phpunit/php-code-coverage",
@ -3192,16 +3407,16 @@
},
{
"name": "phpunit/phpunit",
"version": "4.5.0",
"version": "4.5.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5"
"reference": "d6429b0995b24a2d9dfe5587ee3a7071c1161af4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5b578d3865a9128b9c209b011fda6539ec06e7a5",
"reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d6429b0995b24a2d9dfe5587ee3a7071c1161af4",
"reference": "d6429b0995b24a2d9dfe5587ee3a7071c1161af4",
"shasum": ""
},
"require": {
@ -3211,8 +3426,8 @@
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpspec/prophecy": "~1.3.1",
"phpunit/php-code-coverage": "~2.0",
"phpspec/prophecy": "~1.3,>=1.3.1",
"phpunit/php-code-coverage": "~2.0,>=2.0.11",
"phpunit/php-file-iterator": "~1.3.2",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "~1.0.2",
@ -3260,29 +3475,29 @@
"testing",
"xunit"
],
"time": "2015-02-05 15:51:19"
"time": "2015-03-29 09:24:05"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "2.3.0",
"version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "c63d2367247365f688544f0d500af90a11a44c65"
"reference": "74ffb87f527f24616f72460e54b595f508dccb5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65",
"reference": "c63d2367247365f688544f0d500af90a11a44c65",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/74ffb87f527f24616f72460e54b595f508dccb5c",
"reference": "74ffb87f527f24616f72460e54b595f508dccb5c",
"shasum": ""
},
"require": {
"doctrine/instantiator": "~1.0,>=1.0.1",
"doctrine/instantiator": "~1.0,>=1.0.2",
"php": ">=5.3.3",
"phpunit/php-text-template": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.3"
"phpunit/phpunit": "~4.4"
},
"suggest": {
"ext-soap": "*"
@ -3315,7 +3530,7 @@
"mock",
"xunit"
],
"time": "2014-10-03 05:12:11"
"time": "2015-04-02 05:36:41"
},
{
"name": "satooshi/php-coveralls",
@ -3758,17 +3973,17 @@
},
{
"name": "symfony/class-loader",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/ClassLoader",
"source": {
"type": "git",
"url": "https://github.com/symfony/ClassLoader.git",
"reference": "56bf6fe551ca013471541d866f73a6cc70ece9c5"
"reference": "861765b3e5f32979de5bd19ad2577cbb830a29d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/ClassLoader/zipball/56bf6fe551ca013471541d866f73a6cc70ece9c5",
"reference": "56bf6fe551ca013471541d866f73a6cc70ece9c5",
"url": "https://api.github.com/repos/symfony/ClassLoader/zipball/861765b3e5f32979de5bd19ad2577cbb830a29d5",
"reference": "861765b3e5f32979de5bd19ad2577cbb830a29d5",
"shasum": ""
},
"require": {
@ -3805,21 +4020,21 @@
],
"description": "Symfony ClassLoader Component",
"homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22"
"time": "2015-03-27 10:19:51"
},
{
"name": "symfony/config",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "7a47189c7667ca69bcaafd19ef8a8941db449a2c"
"reference": "d91be01336605db8da21b79bc771e46a7276d1bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/7a47189c7667ca69bcaafd19ef8a8941db449a2c",
"reference": "7a47189c7667ca69bcaafd19ef8a8941db449a2c",
"url": "https://api.github.com/repos/symfony/Config/zipball/d91be01336605db8da21b79bc771e46a7276d1bc",
"reference": "d91be01336605db8da21b79bc771e46a7276d1bc",
"shasum": ""
},
"require": {
@ -3856,21 +4071,21 @@
],
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44"
"time": "2015-03-30 15:54:10"
},
{
"name": "symfony/stopwatch",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Stopwatch",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
"reference": "ba4e774f71e2ce3e3f65cabac4031b9029972af5"
"reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/ba4e774f71e2ce3e3f65cabac4031b9029972af5",
"reference": "ba4e774f71e2ce3e3f65cabac4031b9029972af5",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/5f196e84b5640424a166d2ce9cca161ce1e9d912",
"reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912",
"shasum": ""
},
"require": {
@ -3906,21 +4121,21 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "http://symfony.com",
"time": "2015-02-24 11:52:21"
"time": "2015-03-22 16:55:57"
},
{
"name": "symfony/yaml",
"version": "v2.6.5",
"version": "v2.6.6",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d"
"reference": "174f009ed36379a801109955fc5a71a49fe62dd4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/0cd8e72071e46e15fc072270ae39ea1b66b10a9d",
"reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/174f009ed36379a801109955fc5a71a49fe62dd4",
"reference": "174f009ed36379a801109955fc5a71a49fe62dd4",
"shasum": ""
},
"require": {
@ -3956,7 +4171,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44"
"time": "2015-03-30 15:54:10"
}
],
"aliases": [],

View File

@ -48,7 +48,7 @@ return [
'sqlite' => [
'driver' => 'sqlite',
'database' => realpath(__DIR__ . '/../tests/database/db.sqlite'),
'database' => ':memory:',
'prefix' => '',
],

View File

@ -20,7 +20,8 @@ return [
'accountRoles' => [
'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account',
'savingAsset' => 'Savings account'
'savingAsset' => 'Savings account',
'ccAsset' => 'Credit card',
],
'range_to_text' => [
@ -31,6 +32,9 @@ return [
'6M' => 'half year',
'custom' => '(custom)'
],
'ccTypes' => [
'monthlyFull' => 'Full payment every month'
],
'range_to_name' => [
'1D' => 'one day',
'1W' => 'one week',

View File

@ -1,13 +1,12 @@
<?php
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\Component;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* @SuppressWarnings(PHPMD.ShortMethodName) // method names are mandated by laravel.
* @SuppressWarnings("TooManyMethods") // I'm fine with this
@ -427,28 +426,28 @@ class ChangesForV321 extends Migration
public function moveComponentIdToBudgetId()
{
\Log::debug('Now in moveComponentIdToBudgetId()');
//Log::debug('Now in moveComponentIdToBudgetId()');
BudgetLimit::get()->each(
function (BudgetLimit $bl) {
\Log::debug('Now at budgetLimit #' . $bl->id . ' with component_id: ' . $bl->component_id);
Log::debug('Now at budgetLimit #' . $bl->id . ' with component_id: ' . $bl->component_id);
$component = Component::find($bl->component_id);
if ($component) {
\Log::debug('Found component with id #' . $component->id . ' and name ' . $component->name);
Log::debug('Found component with id #' . $component->id . ' and name ' . $component->name);
$budget = Budget::whereName($component->name)->whereUserId($component->user_id)->first();
if ($budget) {
\Log::debug('Found a budget with ID #' . $budget->id . ' and name ' . $budget->name);
Log::debug('Found a budget with ID #' . $budget->id . ' and name ' . $budget->name);
$bl->budget_id = $budget->id;
$bl->save();
\Log::debug('Connected budgetLimit #' . $bl->id . ' to budget_id' . $budget->id);
Log::debug('Connected budgetLimit #' . $bl->id . ' to budget_id' . $budget->id);
} else {
\Log::debug('Could not find a matching budget with name ' . $component->name);
Log::debug('Could not find a matching budget with name ' . $component->name);
}
} else {
\Log::debug('Could not find a component with id ' . $bl->component_id);
Log::debug('Could not find a component with id ' . $bl->component_id);
}
}
);
\Log::debug('Done with moveComponentIdToBudgetId()');
//Log::debug('Done with moveComponentIdToBudgetId()');
}

View File

@ -1,20 +1,31 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV332
*/
class ChangesForV332 extends Migration {
class ChangesForV332 extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table(
'accounts', function (Blueprint $table) {
@ -33,14 +44,4 @@ class ChangesForV332 extends Migration {
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -1,33 +1,34 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class ChangesForV333 extends Migration {
class ChangesForV333 extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table(
'transaction_journals', function (Blueprint $table) {
$table->smallInteger('order',false,true)->default(0);
$table->smallInteger('order', false, true)->default(0);
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}
}

View File

@ -0,0 +1,239 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV336
*/
class ChangesForV336 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
/**
* ACCOUNTS
*/
// unchange field to be encryptable.
Schema::table(
'accounts', function (Blueprint $table) {
// drop foreign key:
$table->dropForeign('account_user_id');
}
);
Schema::table(
'accounts', function (Blueprint $table) {
$table->string('name', 255)->change();
$table->dropColumn('virtual_balance');
// recreate foreign key
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
// recreate unique:
$table->unique(['user_id', 'account_type_id', 'name']);
}
);
/**
* BILLS
*/
// change field to be cryptable.
Schema::table(
'bills', function (Blueprint $table) {
// drop foreign key:
$table->dropForeign('bill_user_id');
// drop unique:
$table->dropUnique('bill_user_id');
}
);
//
Schema::table(
'bills', function (Blueprint $table) {
// raw query:
DB::insert('ALTER TABLE `bills` CHANGE `name` `name` varchar(255) NOT NULL');
DB::insert('ALTER TABLE `bills` CHANGE `match` `match` varchar(255) NOT NULL');
$table->foreign('user_id', 'bills_uid_for')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'name'], 'uid_name_unique');
}
);
// remove a long forgotten index:
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropUnique('unique_limit');
}
);
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
/**
* ACCOUNTS
*/
// change field to be cryptable.
Schema::table(
'accounts', function (Blueprint $table) {
// drop foreign key:
$table->dropForeign('accounts_user_id_foreign');
// drop unique:
$table->dropUnique('accounts_user_id_account_type_id_name_unique');
}
);
Schema::table(
'accounts', function (Blueprint $table) {
$table->text('name')->change();
$table->decimal('virtual_balance', 10, 2)->default(0);
$table->foreign('user_id', 'account_user_id')->references('id')->on('users')->onDelete('cascade');
}
);
/**
* BUDGETS
*/
// add active/inactive and encrypt.
Schema::table(
'budgets', function (Blueprint $table) {
$table->smallInteger('active', false, true)->default(1);
$table->smallInteger('encrypted', false, true)->default(0);
// drop foreign key:
$table->dropForeign('budgets_user_id_foreign');
// drop unique:
$table->dropUnique('budgets_user_id_name_unique');
}
);
Schema::table(
'budgets', function (Blueprint $table) {
$table->text('name')->change();
$table->foreign('user_id', 'budget_user_id')->references('id')->on('users')->onDelete('cascade');
}
);
// reinstate a long forgotten index:
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->unique(['budget_id', 'startdate'],'unique_limit');
}
);
/**
* BILLS
*/
// change field to be cryptable.
Schema::table(
'bills', function (Blueprint $table) {
// drop foreign key:
$table->dropForeign('bills_uid_for');
// drop unique:
$table->dropUnique('uid_name_unique');
}
);
Schema::table(
'bills', function (Blueprint $table) {
// raw query:
try {
DB::insert('ALTER TABLE `bills` CHANGE `name` `name` TEXT NOT NULL');
} catch (PDOException $e) {
// don't care.
}
try {
DB::insert('ALTER TABLE `bills` CHANGE `match` `match` TEXT NOT NULL');
} catch (PDOException $e) {
// don't care.
}
$table->smallInteger('name_encrypted', false, true)->default(0);
$table->smallInteger('match_encrypted', false, true)->default(0);
$table->foreign('user_id', 'bill_user_id')->references('id')->on('users')->onDelete('cascade');
}
);
/**
* CATEGORIES
*/
Schema::table(
'categories', function (Blueprint $table) {
$table->smallInteger('encrypted', false, true)->default(0);
// drop foreign key:
$table->dropForeign('categories_user_id_foreign');
// drop unique:
$table->dropUnique('categories_user_id_name_unique');
}
);
Schema::table(
'categories', function (Blueprint $table) {
$table->text('name')->change();
$table->foreign('user_id', 'category_user_id')->references('id')->on('users')->onDelete('cascade');
}
);
/**
* PIGGY BANKS
*/
Schema::table(
'piggy_banks', function (Blueprint $table) {
$table->smallInteger('encrypted', false, true)->default(0);
// drop foreign:
$table->dropForeign('piggybanks_account_id_foreign');
// drop unique:
$table->dropUnique('piggybanks_account_id_name_unique');
}
);
Schema::table(
'piggy_banks', function (Blueprint $table) {
try {
DB::insert('ALTER TABLE `piggy_banks` CHANGE `name` `name` TEXT NOT NULL');
} catch (PDOException $e) {
// don't care.
}
$table->dropColumn(['repeats', 'rep_length', 'rep_every', 'rep_times']);
// create index again:
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
}
);
/**
* REMINDERS
*/
Schema::table(
'reminders', function (Blueprint $table) {
$table->smallInteger('encrypted', false, true)->default(0);
}
);
}
}

View File

@ -21,7 +21,7 @@ class DatabaseSeeder extends Seeder
$this->call('TransactionCurrencySeeder');
$this->call('TransactionTypeSeeder');
if (App::environment() == 'testing' || App::environment() == 'homestead') {
if (App::environment() == 'testing' || App::environment() == 'homestead' || gethostname() == 'vagrant-firefly-iii') {
$this->call('TestDataSeeder');
}
}

View File

@ -113,7 +113,9 @@ class TestDataSeeder extends Seeder
*/
public function createUsers()
{
User::create(['email' => 'reset@example.com', 'password' => bcrypt('functional'), 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]);
User::create(
['email' => 'reset@example.com', 'password' => bcrypt('functional'), 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]
);
User::create(['email' => 'functional@example.com', 'password' => bcrypt('functional'), 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]);
}
@ -238,7 +240,8 @@ class TestDataSeeder extends Seeder
public function createPiggyBanks()
{
// account
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$savings = $this->findAccount('Savings account');
// some dates
$endDate = clone $this->_startOfMonth;
@ -247,7 +250,7 @@ class TestDataSeeder extends Seeder
$endDate->addMonths(4);
$nextYear->addYear()->subDay();
$end = $endDate->format('Y-m-d');
$end = $endDate->format('Y-m-d');
// piggy bank
$newCamera = PiggyBank::create(
@ -257,10 +260,6 @@ class TestDataSeeder extends Seeder
'targetamount' => 2000,
'startdate' => $this->som,
'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null,
'reminder_skip' => 0,
'remind_me' => 0,
@ -278,10 +277,6 @@ class TestDataSeeder extends Seeder
'targetamount' => 2000,
'startdate' => $this->som,
'targetdate' => $end,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null,
'reminder_skip' => 0,
'remind_me' => 0,
@ -295,22 +290,18 @@ class TestDataSeeder extends Seeder
* New: create no less than eight piggy banks that
* create all sorts of reminders
*/
$list = ['week','quarter','month','year'];
$list = ['week', 'quarter', 'month', 'year'];
$nextYear = clone $this->_startOfMonth;
$nextYear->addYear();
foreach($list as $entry) {
foreach ($list as $entry) {
PiggyBank::create(
[
'account_id' => $savings->id,
'name' => $entry.' piggy bank with target date.',
'name' => $entry . ' piggy bank with target date.',
'targetamount' => 1000,
'startdate' => $this->som,
'targetdate' => $nextYear,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => $entry,
'reminder_skip' => 0,
'remind_me' => 1,
@ -320,14 +311,10 @@ class TestDataSeeder extends Seeder
PiggyBank::create(
[
'account_id' => $savings->id,
'name' => $entry.' piggy bank without target date.',
'name' => $entry . ' piggy bank without target date.',
'targetamount' => 1000,
'startdate' => $this->som,
'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => $entry,
'reminder_skip' => 0,
'remind_me' => 1,
@ -337,6 +324,26 @@ class TestDataSeeder extends Seeder
}
}
/**
* @param $name
*
* @return Account|null
*/
protected function findAccount($name)
{
// account
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
/** @var Account $account */
foreach (Account::get() as $account) {
if ($account->name == $name && $user->id == $account->user_id) {
return $account;
break;
}
}
return null;
}
/**
*
*/
@ -431,20 +438,20 @@ class TestDataSeeder extends Seeder
public function createMonthlyExpenses(Carbon $date)
{
// get some objects from the database:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$landLord = Account::whereName('Land lord')->orderBy('id', 'DESC')->first();
$utilities = Account::whereName('Utilities company')->orderBy('id', 'DESC')->first();
$television = Account::whereName('TV company')->orderBy('id', 'DESC')->first();
$phone = Account::whereName('Phone agency')->orderBy('id', 'DESC')->first();
$employer = Account::whereName('Employer')->orderBy('id', 'DESC')->first();
$bills = Budget::whereName('Bills')->orderBy('id', 'DESC')->first();
$house = Category::whereName('House')->orderBy('id', 'DESC')->first();
$checking = $this->findAccount('Checking account');
$savings = $this->findAccount('Savings account');
$landLord = $this->findAccount('Land lord');
$utilities = $this->findAccount('Utilities company');
$television = $this->findAccount('TV company');
$phone = $this->findAccount('Phone agency');
$employer = $this->findAccount('Employer');
$bills = $this->findBudget('Bills');
$house = $this->findCategory('House');
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$deposit = TransactionType::whereType('Deposit')->first();
$transfer = TransactionType::whereType('Transfer')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$rentBill = Bill::where('name', 'Rent')->first();
$rentBill = $this->findBill('Rent');
$cur = $date->format('Y-m-d');
$formatted = $date->format('F Y');
@ -487,21 +494,102 @@ class TestDataSeeder extends Seeder
}
/**
* @param $name
*
* @return Budget|null
*/
protected function findBudget($name)
{
// account
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
/** @var Budget $budget */
foreach (Budget::get() as $budget) {
if ($budget->name == $name && $user->id == $budget->user_id) {
return $budget;
break;
}
}
return null;
}
/**
* @param $name
*
* @return PiggyBank|null
*/
protected function findPiggyBank($name)
{
// account
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
/** @var Budget $budget */
foreach (PiggyBank::get() as $piggyBank) {
$account = $piggyBank->account()->first();
if ($piggyBank->name == $name && $user->id == $account->user_id) {
return $piggyBank;
break;
}
}
return null;
}
/**
* @param $name
*
* @return Category|null
*/
protected function findCategory($name)
{
// account
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
/** @var Category $category */
foreach (Category::get() as $category) {
if ($category->name == $name && $user->id == $category->user_id) {
return $category;
break;
}
}
return null;
}
/**
* @param $name
*
* @return Bill|null
*/
protected function findBill($name)
{
// account
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
/** @var Bill $bill */
foreach (Bill::get() as $bill) {
if ($bill->name == $name && $user->id == $bill->user_id) {
return $bill;
break;
}
}
return null;
}
/**
* @param Carbon $date
*/
public function createGroceries(Carbon $date)
{
// variables we need:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$shopOne = Account::whereName('Groceries House')->orderBy('id', 'DESC')->first();
$shopTwo = Account::whereName('Super savers')->orderBy('id', 'DESC')->first();
$lunchHouse = Account::whereName('Lunch House')->orderBy('id', 'DESC')->first();
$lunch = Category::whereName('Lunch')->orderBy('id', 'DESC')->first();
$daily = Category::whereName('DailyGroceries')->orderBy('id', 'DESC')->first();
$checking = $this->findAccount('Checking account');
$shopOne = $this->findAccount('Groceries House');
$shopTwo = $this->findAccount('Super savers');
$lunchHouse = $this->findAccount('Lunch House');
$lunch = $this->findCategory('Lunch');
$daily = $this->findCategory('DailyGroceries');
$euro = TransactionCurrency::whereCode('EUR')->first();
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first();
$groceries = $this->findBudget('Groceries');
$shops = [$shopOne, $shopTwo];
@ -534,9 +622,9 @@ class TestDataSeeder extends Seeder
{
$date->addDays(12);
$dollar = TransactionCurrency::whereCode('USD')->first();
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$buyMore = Account::whereName('Buy More')->orderBy('id', 'DESC')->first();
$checking = $this->findAccount('Checking account');
$savings = $this->findAccount('Savings account');
$buyMore = $this->findAccount('Buy More');
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
@ -571,13 +659,13 @@ class TestDataSeeder extends Seeder
// piggy bank event
// add money to this piggy bank
// create a piggy bank event to match:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$checking = $this->findAccount('Checking account');
$savings = $this->findAccount('Savings account');
$transfer = TransactionType::whereType('Transfer')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first();
$house = Category::whereName('House')->orderBy('id', 'DESC')->first();
$piggyBank = PiggyBank::whereName('New camera')->orderBy('id', 'DESC')->first();
$groceries = $this->findBudget('Groceries');
$house = $this->findCategory('House');
$piggyBank = $this->findPiggyBank('New camera');
$intoPiggy = $this->createJournal(
['from' => $checking, 'to' => $savings, 'amount' => 100, 'transactionType' => $transfer, 'description' => 'Money for piggy',
'date' => $this->yaeom, 'transactionCurrency' => $euro, 'category' => $house, 'budget' => $groceries]

13
pu.sh
View File

@ -1,10 +1,13 @@
#!/bin/bash
# create DB if not exists
# backup .env file.
cp .env .env.backup
if [ ! -f tests/database/db.sqlite ]; then
touch tests/database/db.sqlite
php artisan migrate --seed
fi
# set testing environment
cp .env.testing .env
# test!
phpunit --verbose
# restore .env file
mv .env.backup .env

View File

@ -299,7 +299,7 @@ table.dataTable thead .sorting:after {
font-size: 40px;
}
.large {
font-size: 30px;
font-size: 20px;
}
.panel-green {

View File

@ -36,7 +36,8 @@
{!! ExpandedForm::balance('openingBalance') !!}
{!! ExpandedForm::date('openingBalanceDate', date('Y-m-d')) !!}
{!! ExpandedForm::select('accountRole',Config::get('firefly.accountRoles')) !!}
{!! ExpandedForm::select('accountRole',Config::get('firefly.accountRoles'),null,['helpText' => 'Any extra options resulting from your choice can be set later.']) !!}
{!! ExpandedForm::balance('virtualBalance') !!}
</div>
</div>

View File

@ -10,16 +10,19 @@
</div>
<div class="panel-body">
<p>
Are you sure?
Are you sure that you want to delete the {{strtolower($account->accountType->type)}} "{{$account->name}}"?
</p>
@if($account->transactions()->count() > 0)
<p class="text-info">
Account "{{{$account->name}}}" still has {{$account->transactions()->count()}} transaction(s) associated to it.
These will be deleted as well.
<p class="text-danger">
{{ucfirst($account->accountType->type)}} "{{{$account->name}}}" still has {{$account->transactions()->count()}} transaction(s) associated to it. These will be deleted as well.
</p>
@endif
@if($account->piggyBanks()->count() > 0)
<p class="text-danger">
{{ucfirst($account->accountType->type)}} "{{{$account->name}}}" still has {{$account->piggyBanks()->count()}} piggy bank(s) associated to it. These will be deleted as well.
</p>
@endif
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >

View File

@ -2,6 +2,9 @@
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) !!}
{!! Form::model($account, ['class' => 'form-horizontal','id' => 'update','url' => route('accounts.update',$account->id)]) !!}
<input type="hidden" name="id" value="{{$account->id}}" />
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary">
@ -28,12 +31,26 @@
{!! ExpandedForm::balance('openingBalance',null, ['currency' => $openingBalance ? $openingBalance->transactionCurrency : null]) !!}
{!! ExpandedForm::date('openingBalanceDate') !!}
{!! ExpandedForm::select('accountRole',Config::get('firefly.accountRoles')) !!}
{!! ExpandedForm::balance('virtualBalance',null) !!}
{!! Form::hidden('id',$account->id) !!}
@endif
{!! ExpandedForm::checkbox('active','1') !!}
</div>
</div>
<!-- panel for credit card options -->
@if(Session::get('preFilled')['accountRole'] == 'ccAsset')
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-credit-card"></i> Credit card options
</div>
<div class="panel-body">
{!! ExpandedForm::select('ccType',Config::get('firefly.ccTypes')) !!}
{!! ExpandedForm::date('ccMonthlyPaymentDate',null,['helpText' => 'Select any year and any month, it will be ignored anway. Only the day of the month is relevant.']) !!}
</div>
</div>
@endif
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">

View File

@ -10,9 +10,16 @@
</div>
<div class="panel-body">
<p>
Are you sure?
Are you sure that you want to delete bill "{{{$bill->name}}}"?
</p>
@if($bill->transactionjournals()->count() > 0)
<p class="text-info">
Bill "{{{$bill->name}}}" still has {{$bill->transactionjournals()->count()}} transactions connected
to it. These will <strong>not</strong> be removed but will lose their connection to this bill.
</p>
@endif
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >

View File

@ -10,9 +10,16 @@
</div>
<div class="panel-body">
<p>
Are you sure?
Are you sure that you want to delete budget "{{{$budget->name}}}"?
</p>
@if($budget->transactionjournals()->count() > 0)
<p class="text-info">
Budget "{{{$budget->name}}}" still has {{$budget->transactionjournals()->count()}} transactions connected
to it. These will <strong>not</strong> be removed but will lose their connection to this budget.
</p>
@endif
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >

View File

@ -16,6 +16,7 @@
<i class="fa fa-fw fa-exclamation"></i> Mandatory fields
</div>
<div class="panel-body">
{!! ExpandedForm::checkbox('active') !!}
{!! ExpandedForm::text('name') !!}
</div>
</div>

View File

@ -64,6 +64,7 @@
</div>
</div>
</div>
</div>
<div class="row">
@ -142,7 +143,27 @@
<div class="panel-body">
<a href="{{route('budgets.create')}}" class="btn btn-success"><i class="fa fa-fw fa-plus"></i> Create new budget</a>
</div>
</div>
</div>
@if($inactive->count() > 0)
<div class="col-lg-3 col-sm-4 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-minus-circle"></i>
Inactive budgets
</div>
<div class="panel-body">
@foreach($inactive as $index => $budget)
@if($index != count($inactive)-1)
<a href="{{route('budgets.show',$budget->id)}}">{{$budget->name}}</a>,
@else
<a href="{{route('budgets.show',$budget->id)}}">{{$budget->name}}</a>
@endif
@endforeach
</div>
</div>
</div>
@endif
</div>
<!-- DIALOG -->

View File

@ -6,6 +6,21 @@
<div class="panel panel-default">
<div class="panel-heading">
Overview
<!-- ACTIONS MENU -->
<div class="pull-right">
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('budgets.edit',$budget->id)}}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li>
<li><a href="{{route('budgets.delete',$budget->id)}}"><i class="fa fa-trash fa-fw"></i> Delete</a></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<div id="budgetOverview"></div>
@ -46,7 +61,8 @@
?>
@if($overspent)
<?php
$pct = $rep->amount / $rep->spentInRepetition()*100;
$spent = floatval($rep->spentInRepetition());
$pct = $spent != 0 ? ($rep->amount / $spent)*100 : 0;
?>
<div class="progress progress-striped">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{ceil($pct)}}" aria-valuemin="0" aria-valuemax="100" style="width: {{ceil($pct)}}%;"></div>
@ -54,7 +70,8 @@
</div>
@else
<?php
$pct = $rep->spentInRepetition() / $rep->amount*100;
$amount = floatval($rep->amount);
$pct = $amount != 0 ? ($rep->spentInRepetition() / $amount)*100 : 0;
?>
<div class="progress progress-striped">
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{ceil($pct)}}" aria-valuemin="0" aria-valuemax="100" style="width: {{ceil($pct)}}%;">

View File

@ -10,9 +10,16 @@
</div>
<div class="panel-body">
<p>
Are you sure?
Are you sure that you want to delete category "{{$category->name}}"?
</p>
@if($category->transactionjournals()->count() > 0)
<p class="text-info">
Category "{{{$category->name}}}" still has {{$category->transactionjournals()->count()}} transactions connected
to it. These will <strong>not</strong> be removed but will lose their connection to this category.
</p>
@endif
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >

View File

@ -10,7 +10,7 @@
</div>
<div class="panel-body">
<p>
Are you sure?
Are you sure that you want to delete currency "{{{$currency->name}}}"?
</p>
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>

View File

@ -1,6 +1,6 @@
<html>
<head>
<link href='http://fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>
<style>
body {

View File

@ -2,6 +2,7 @@
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
{!! Form::input('date', $name, $value, $options) !!}
@include('form.help')
@include('form.feedback')
</div>
</div>

View File

@ -0,0 +1,3 @@
@if(isset($options['helpText']))
<p class="help-block">{{$options['helpText']}}</p>
@endif

View File

@ -0,0 +1,8 @@
<div class="{{{$classes}}}">
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
{!! Form::input('month', $name, $value, $options) !!}
@include('form.help')
@include('form.feedback')
</div>
</div>

View File

@ -64,7 +64,7 @@
</label>
<div class="col-sm-8">
<div class="radio"><label>
{!! Form::checkbox('return_to_edit', '1') !!}
{!! Form::checkbox('return_to_edit', '1', intval(Input::old('return_to_edit')) == 1) !!}
After updating, return here.
</label>
</div>

View File

@ -2,6 +2,7 @@
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
{!! Form::select($name, $list, $selected , $options ) !!}
@include('form.help')
@include('form.feedback')
</div>

View File

@ -1,6 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<?php $r = Route::getCurrentRoute()->getName();?>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -122,19 +121,36 @@
<script type="text/javascript" src="js/daterangepicker.js"></script>
<script type="text/javascript">
var start = "{{Session::get('start')->format('d-m-Y')}}";
var end = "{{Session::get('end')->format('d-m-Y')}}";
var titleString = "{{Session::get('start')->format('j M Y')}} - {{Session::get('end')->format('j M Y')}}";
var start = "{{Session::get('start', new Carbon\Carbon)->format('d-m-Y')}}";
var end = "{{Session::get('end', new Carbon\Carbon)->format('d-m-Y')}}";
var titleString = "{{Session::get('start', new Carbon\Carbon)->format('j M Y')}} - {{Session::get('end', new Carbon\Carbon)->format('j M Y')}}";
var dateRangeURL = "{{route('daterange')}}";
var token = "{{csrf_token()}}";
var firstDate = moment("{{Session::get('first')->format('Y-m-d')}}");
var currentMonthName = "{{$currentMonthName}}";
var previousMonthName = "{{$previousMonthName}}";
var nextMonthName = "{{$nextMonthName}}";
var firstDate = moment("{{Session::get('first', new Carbon\Carbon)->format('Y-m-d')}}");
var currentMonthName = "{{isset($currentMonthName) ? $currentMonthName : 'Month'}}";
var previousMonthName = "{{isset($previousMonthName) ? $previousMonthName : 'Month'}}";
var nextMonthName = "{{isset($nextMonthName) ? $nextMonthName : 'Month'}}";
$('#daterange span').text(titleString);
</script>
<script type="text/javascript" src="js/firefly.js"></script>
@yield('scripts')
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '<?php echo env('ANALYTICS_ID', 'XXX-XX-X'); ?>', 'auto');
ga('send', 'pageview');
// send an event if relevant:
@if(Session::has('gaEventCategory') && Session::has('gaEventAction'))
ga('send','event','{{Session::get('gaEventCategory')}}','{{Session::get('gaEventAction')}}');
@endif
</script>
</body>
</html>

View File

@ -9,6 +9,7 @@
<th>Is active</th>
<th>Will be automatched</th>
<th>Repeats every</th>
<th>&nbsp;</th>
</tr>
@foreach($bills as $entry)
<tr>
@ -66,6 +67,11 @@
skips over {{$entry->skip}}
@endif
</td>
<td>
@if($entry->active)
<a href="{{route('bills.add',$entry->id)}}" class="btn btn-success btn-xs"><i class="fa fa-fw fa-plus-circle"></i></a>
@endif
</td>
</tr>
@endforeach

View File

@ -1,11 +1,11 @@
<!-- /.row -->
<div class="row">
<div class="col-lg-3 col-md-6">
<div class="hidden-xs col-lg-3 col-md-6">
<div class="panel panel-red">
<div class="panel-heading">
<div class="row">
<div class="col-xs-3">
<i class="fa fa-upload fa-5x"></i>
<i class="fa fa-upload fa-3x"></i>
</div>
<div class="col-xs-9 text-right">
<div id="box-out" class="large">{{Amount::format(0,false)}}</div>
@ -22,12 +22,12 @@
</a>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="hidden-xs col-lg-3 col-md-6">
<div class="panel panel-green">
<div class="panel-heading">
<div class="row">
<div class="col-xs-3">
<i class="fa fa-download fa-5x"></i>
<i class="fa fa-download fa-3x"></i>
</div>
<div class="col-xs-9 text-right">
<div id="box-in" class="large">{{Amount::format(0,false)}}</div>
@ -44,12 +44,12 @@
</a>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="hidden-xs col-lg-3 col-md-6">
<div class="panel panel-primary">
<div class="panel-heading">
<div class="row">
<div class="col-xs-3">
<i class="fa fa-calendar fa-5x"></i>
<i class="fa fa-calendar fa-3x"></i>
</div>
<div class="col-xs-9 text-right">
<div id="box-bills-unpaid" class="large">{{Amount::format(0,false)}}</div>
@ -66,12 +66,12 @@
</a>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="hidden-xs col-lg-3 col-md-6">
<div class="panel panel-green">
<div class="panel-heading">
<div class="row">
<div class="col-xs-3">
<i class="fa fa-line-chart fa-5x"></i>
<i class="fa fa-line-chart fa-3x"></i>
</div>
<div class="col-xs-9 text-right">
<div id="box-bills-paid" class="large">{{Amount::format(0,false)}}</div>

View File

@ -1,4 +1,6 @@
<!-- Navigation -->
<?php $r = Route::getCurrentRoute()->getName();?>
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
@ -16,7 +18,7 @@
<ul class="nav navbar-top-links navbar-right">
<!-- reminders -->
@if($reminders->count() > 0)
@if(isset($reminders) && $reminders->count() > 0)
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" aria-expanded="false">
<i class="fa fa-envelope fa-fw"></i> <i class="fa fa-caret-down"></i>
@ -147,10 +149,9 @@
</li>
<?php
$isMM = !(strpos($r,'piggy-banks') === false) || !(strpos($r,'bills') === false) | !(strpos($r,'repeated') === false);
$isMM = !(strpos($r,'piggy-banks') === false) || !(strpos($r,'bills') === false);
$isPiggy = !(strpos($r,'piggy-banks') === false);
$isBill = !(strpos($r,'bills') === false) && strpos($r,'bills.create') === false;
$isRep = !(strpos($r,'repeated') === false);
?>
<li @if($isMM)class="active"@endif>
<a href="#"><i class="fa fa-euro fa-fw"></i> Money management<span class="fa arrow"></span></a>

View File

@ -22,11 +22,7 @@
@foreach($income as $entry)
<tr>
<td>
@if($entry->encrypted === true)
<a href="{{route('transactions.show',$entry->id)}}" title="{{{Crypt::decrypt($entry->description)}}}">{{{Crypt::decrypt($entry->description)}}}</a>
@else
<a href="{{route('transactions.show',$entry->id)}}" title="{{{$entry->description}}}">{{{$entry->description}}}</a>
@endif
<a href="{{route('transactions.show',$entry->id)}}" title="{{{$entry->description}}}">{{{$entry->description}}}</a>
</td>
<td>
<?php $tableSum += floatval($entry->amount);?>
@ -67,12 +63,14 @@
<table class="table table-bordered">
<?php $sum = 0;?>
@foreach($expenses as $id => $expense)
<?php $sum += floatval($expense['amount']);?>
<?php
$sum += floatval($expense['amount']);
?>
<tr>
@if($id > 0)
<td><a href="{{route('accounts.show',$id)}}">{{{$expense['name']}}}</a></td>
<td><a href="{{route('accounts.show',$id)}}">{{{$expense['name']}}}</a></td>
@else
<td><em>{{{$expense['name']}}}</em></td>
<td><em>{{{$expense['name']}}}</em></td>
@endif
<td>{!! Amount::format($expense['amount']) !!}</td>
</tr>
@ -233,7 +231,7 @@
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-sort-amount-asc fa-fw"></i>
@ -242,15 +240,6 @@
<div class="panel-body">Body</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-rotate-left fa-fw"></i>
Repeated expenses
</div>
<div class="panel-body">Body</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">

View File

@ -43,6 +43,12 @@
Account balance
</div>
<table class="table table-bordered table-striped">
<tr>
<th>Name</th>
<th>Balance at start of year</th>
<th>Balance at end of year</th>
<th>Difference</th>
</tr>
<?php
$start = 0;
$end = 0;
@ -120,7 +126,9 @@
<table class="table">
<?php $sum = 0;?>
@foreach($groupedIncomes as $income)
<?php $sum += floatval($income->amount)*-1;?>
<?php
$sum += floatval($income->amount)*-1;
?>
<tr>
<td><a href="{{route('accounts.show',$income->account_id)}}">{{{$income->name}}}</a></td>
<td>{!! Amount::format(floatval($income->amount)*-1) !!}</td>

View File

@ -1,5 +1,7 @@
<?php
use League\FactoryMuffin\Facade as FactoryMuffin;
/**
* Class TestCase
*/
@ -20,4 +22,44 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
return $app;
}
/**
* This method is called before the first test of this test class is run.
*
* @since Method available since Release 3.4.0
*/
public static function setUpBeforeClass()
{
}
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
FactoryMuffin::loadFactories(__DIR__ . '/factories');
}
/**
* @param string $class
*
* @return mixed
*/
public function mock($class)
{
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
}

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