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
103 changed files with 2651 additions and 1163 deletions

View File

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

View File

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

View File

@@ -11,12 +11,9 @@ addons:
repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61 repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
install: install:
- rm composer.lock - composer update
- composer install
- php artisan env - php artisan env
- mv -v .env.testing .env - mv -v .env.testing .env
- touch tests/database/db.sqlite
- php artisan migrate --seed
script: script:
- phpunit --debug - 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) [![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) [![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) [![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) [![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) [![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) [![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 ## 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; - 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; - Organize transactions using categories;
- Save towards a goal using piggy banks; - Save towards a goal using piggy banks;
- Predict and anticipate bills; - 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 - More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts - Debts
- Credit cards
- More test-coverage; - More test-coverage;
- Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control. - Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control.
- Firefly will be able to join transactions. - Firefly will be able to join transactions.
@@ -73,7 +77,4 @@ Some stuff has been removed:
## Current state ## Current state
I have the basics up and running. Test coverage is currently coming, slowly. 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)! 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; <?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
@@ -10,7 +8,8 @@ use Illuminate\Queue\SerializesModels;
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class JournalCreated extends Event { class JournalCreated extends Event
{
use SerializesModels; use SerializesModels;

View File

@@ -1,11 +1,10 @@
<?php namespace FireflyIII\Events; <?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class JournalSaved extends Event { class JournalSaved extends Event
{
use SerializesModels; use SerializesModels;

View File

@@ -67,6 +67,7 @@ class ConnectJournalToPiggyBank
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) { 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; return;
} }

View File

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

View File

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

View File

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

View File

@@ -4,10 +4,12 @@ namespace FireflyIII\Helpers\Report;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Crypt;
use DB; use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Steam; use Steam;
@@ -174,26 +176,9 @@ class ReportQuery implements ReportQueryInterface
*/ */
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end) public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{ {
$set = TransactionJournal:: $query = $this->queryJournalsNoBudget($account, $start, $end);
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`')]);
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) public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{ {
$set = TransactionJournal:: $query = $this->queryJournalsNoBudget($account, $start, $end);
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.*']);
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) public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false)
{ {
$query = TransactionJournal:: $query = $this->queryJournalsWithTransactions($start, $end);
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');
if ($showSharedReports === false) { if ($showSharedReports === false) {
// only get deposits not to a shared account // only get deposits not to a shared account
// and transfers to a shared account. // and transfers to a shared account.
@@ -291,11 +236,10 @@ class ReportQuery implements ReportQueryInterface
// any deposit is fine. // any deposit is fine.
$query->where('transaction_types.type', 'Deposit'); $query->where('transaction_types.type', 'Deposit');
} }
$query->before($end)->after($start) $query->groupBy('t_from.account_id')->orderBy('transaction_journals.date');
->where('transaction_journals.user_id', Auth::user()->id)
->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.id',
'transaction_journals.description', 'transaction_journals.description',
'transaction_journals.encrypted', 'transaction_journals.encrypted',
@@ -303,8 +247,19 @@ class ReportQuery implements ReportQueryInterface
DB::Raw('SUM(`t_to`.`amount`) as `amount`'), DB::Raw('SUM(`t_to`.`amount`) as `amount`'),
'transaction_journals.date', 'transaction_journals.date',
't_from.account_id as account_id', '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') ->groupBy('categories.id')
->orderBy('amount'); ->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) public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{ {
$query = TransactionJournal::leftJoin( $query = $this->queryJournalsWithTransactions($start, $end);
'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');
if ($showSharedReports === false) { if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts // get all withdrawals not from a shared accounts
// and all transfers to a shared account // and all transfers to a shared account
@@ -446,13 +387,21 @@ class ReportQuery implements ReportQueryInterface
// any withdrawal goes: // any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal'); $query->where('transaction_types.type', 'Withdrawal');
} }
$query->before($end) $query->before($end)->after($start)
->after($start)
->where('transaction_journals.user_id', Auth::user()->id) ->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id') ->groupBy('t_to.account_id')
->orderBy('amount', 'DESC'); ->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) public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{ {
$query = TransactionJournal:: $query = $this->queryJournalsWithTransactions($start, $end);
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');
if ($showSharedReports === false) { if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account // 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: // any deposit goes:
$query->where('transaction_types.type', 'Deposit'); $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) public function edit(Account $account, AccountRepositoryInterface $repository)
{ {
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type]; $what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
@@ -93,15 +94,19 @@ class AccountController extends Controller
// the opening balance is tricky: // the opening balance is tricky:
$openingBalanceAmount = null; $openingBalanceAmount = null;
if ($openingBalance) { if ($openingBalance) {
$transaction = $openingBalance->transactions()->where('account_id', $account->id)->first(); $transaction = $repository->getFirstTransaction($openingBalance, $account);
$openingBalanceAmount = $transaction->amount; $openingBalanceAmount = $transaction->amount;
} }
$preFilled = [ $preFilled = [
'accountRole' => $account->getMeta('accountRole'), 'accountRole' => $account->getMeta('accountRole'),
'ccType' => $account->getMeta('ccType'),
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null, 'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount 'openingBalance' => $openingBalanceAmount,
'virtualBalance' => floatval($account->virtual_balance)
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
@@ -132,7 +137,7 @@ class AccountController extends Controller
$total = Auth::user()->accounts()->accountTypeIn($types)->count(); $total = Auth::user()->accounts()->accountTypeIn($types)->count();
// last activity: // last activity:
$start = clone Session::get('start'); $start = clone Session::get('start', Carbon::now()->startOfMonth());
$start->subDay(); $start->subDay();
$set->each( $set->each(
function (Account $account) use ($start) { function (Account $account) use ($start) {
@@ -170,6 +175,7 @@ class AccountController extends Controller
$journals = $repository->getJournals($account, $page); $journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $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')); return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle'));
} }
@@ -184,6 +190,7 @@ class AccountController extends Controller
$accountData = [ $accountData = [
'name' => $request->input('name'), 'name' => $request->input('name'),
'accountType' => $request->input('what'), 'accountType' => $request->input('what'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'active' => true, 'active' => true,
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'), 'accountRole' => $request->input('accountRole'),
@@ -219,9 +226,12 @@ class AccountController extends Controller
'active' => $request->input('active'), 'active' => $request->input('active'),
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'), 'accountRole' => $request->input('accountRole'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'openingBalance' => floatval($request->input('openingBalance')), 'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')), 'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')), 'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
]; ];
$repository->update($account, $accountData); $repository->update($account, $accountData);

View File

@@ -81,6 +81,8 @@ class AuthController extends Controller
// set flash message // set flash message
Session::flash('success', 'You have registered successfully!'); Session::flash('success', 'You have registered successfully!');
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'new-registration');
return redirect($this->redirectPath()); return redirect($this->redirectPath());

View File

@@ -1,9 +1,10 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BillFormRequest; use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -22,12 +23,59 @@ use View;
class BillController extends Controller class BillController extends Controller
{ {
/**
*
*/
public function __construct() public function __construct()
{ {
View::share('title', 'Bills'); View::share('title', 'Bills');
View::share('mainTitleIcon', 'fa-calendar-o'); 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 * @return $this
*/ */
@@ -142,7 +190,6 @@ class BillController extends Controller
->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.id', 'DESC')
->get(); ->get();
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$hideBill = true; $hideBill = true;
@@ -156,21 +203,7 @@ class BillController extends Controller
*/ */
public function store(BillFormRequest $request, BillRepositoryInterface $repository) public function store(BillFormRequest $request, BillRepositoryInterface $repository)
{ {
$billData = $request->getBillData();
$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); $bill = $repository->store($billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.'); Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
@@ -189,20 +222,7 @@ class BillController extends Controller
*/ */
public function update(Bill $bill, BillFormRequest $request, BillRepositoryInterface $repository) public function update(Bill $bill, BillFormRequest $request, BillRepositoryInterface $repository)
{ {
$billData = [ $billData = $request->getBillData();
'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); $bill = $repository->update($bill, $billData);
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {

View File

@@ -5,6 +5,7 @@ use Carbon\Carbon;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BudgetFormRequest; use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Input; use Input;
@@ -13,7 +14,7 @@ use Redirect;
use Response; use Response;
use Session; use Session;
use View; use View;
use URL;
/** /**
* Class BudgetController * Class BudgetController
* *
@@ -22,6 +23,9 @@ use View;
class BudgetController extends Controller class BudgetController extends Controller
{ {
/**
*
*/
public function __construct() public function __construct()
{ {
View::share('title', 'Budgets'); View::share('title', 'Budgets');
@@ -32,7 +36,6 @@ class BudgetController extends Controller
* @param Budget $budget * @param Budget $budget
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* @throws Exception
*/ */
public function amount(Budget $budget, BudgetRepositoryInterface $repository) public function amount(Budget $budget, BudgetRepositoryInterface $repository)
{ {
@@ -49,6 +52,12 @@ class BudgetController extends Controller
*/ */
public function create() 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'); return view('budgets.create')->with('subTitle', 'Create a new budget');
} }
@@ -61,6 +70,9 @@ class BudgetController extends Controller
{ {
$subTitle = 'Delete budget' . e($budget->name) . '"'; $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')); return view('budgets.delete', compact('budget', 'subTitle'));
} }
@@ -75,9 +87,11 @@ class BudgetController extends Controller
$name = $budget->name; $name = $budget->name;
$repository->destroy($budget); $repository->destroy($budget);
Session::flash('success', 'The budget "' . e($name) . '" was deleted.'); 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) . '"'; $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')); return view('budgets.edit', compact('budget', 'subTitle'));
} }
@@ -98,7 +118,15 @@ class BudgetController extends Controller
*/ */
public function index(BudgetRepositoryInterface $repository) 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: // loop the budgets:
$budgets->each( $budgets->each(
@@ -117,7 +145,7 @@ class BudgetController extends Controller
$budgetMax = Preferences::get('budgetMaximum', 1000); $budgetMax = Preferences::get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data; $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'));
} }
/** /**
@@ -171,10 +199,13 @@ class BudgetController extends Controller
Session::flash('success', 'New budget "' . $budget->name . '" stored!'); Session::flash('success', 'New budget "' . $budget->name . '" stored!');
if (intval(Input::get('create_another')) === 1) { 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.create')->withInput();
} }
return Redirect::route('budgets.index'); // redirect to previous URL.
return Redirect::to(Session::get('budgets.create.url'));
} }
@@ -210,6 +241,7 @@ class BudgetController extends Controller
{ {
$budgetData = [ $budgetData = [
'name' => $request->input('name'), 'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
]; ];
$repository->update($budget, $budgetData); $repository->update($budget, $budgetData);
@@ -217,10 +249,13 @@ class BudgetController extends Controller
Session::flash('success', 'Budget "' . $budget->name . '" updated.'); Session::flash('success', 'Budget "' . $budget->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) { 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 Redirect;
use Session; use Session;
use View; use View;
use URL;
/** /**
* Class CategoryController * Class CategoryController
@@ -35,6 +35,12 @@ class CategoryController extends Controller
*/ */
public function create() 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'); return view('categories.create')->with('subTitle', 'Create a new category');
} }
@@ -47,6 +53,9 @@ class CategoryController extends Controller
{ {
$subTitle = 'Delete category' . e($category->name) . '"'; $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')); return view('categories.delete', compact('category', 'subTitle'));
} }
@@ -63,7 +72,7 @@ class CategoryController extends Controller
Session::flash('success', 'The category "' . e($name) . '" was deleted.'); 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) . '"'; $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')); return view('categories.edit', compact('category', 'subTitle'));
} }
@@ -136,11 +151,9 @@ class CategoryController extends Controller
$page = intval(Input::get('page')); $page = intval(Input::get('page'));
$offset = $page > 0 ? $page * 50 : 0; $offset = $page > 0 ? $page * 50 : 0;
$set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset) $set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)
->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.id', 'DESC')
->get( ->get(
['transaction_journals.*'] ['transaction_journals.*']
); );
@@ -168,14 +181,20 @@ class CategoryController extends Controller
Session::flash('success', 'New category "' . $category->name . '" stored!'); Session::flash('success', 'New category "' . $category->name . '" stored!');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
Session::put('categories.create.fromStore', true);
return Redirect::route('categories.create')->withInput(); return Redirect::route('categories.create')->withInput();
} }
<<<<<<< HEAD
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
return Redirect::route('categories.create'); return Redirect::route('categories.create');
} }
return Redirect::route('categories.index'); 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.'); Session::flash('success', 'Category "' . $category->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) { 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.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 Redirect;
use Session; use Session;
use View; use View;
use URL;
/** /**
* Class CurrencyController * Class CurrencyController
@@ -39,6 +39,12 @@ class CurrencyController extends Controller
$subTitleIcon = 'fa-plus'; $subTitleIcon = 'fa-plus';
$subTitle = 'Create a new currency'; $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')); return view('currency.create', compact('subTitleIcon', 'subTitle'));
} }
@@ -72,6 +78,9 @@ class CurrencyController extends Controller
if ($currency->transactionJournals()->count() > 0) { if ($currency->transactionJournals()->count() > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.'); 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'); return Redirect::route('currency.index');
} }
@@ -96,7 +105,7 @@ class CurrencyController extends Controller
$currency->delete(); $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) . '"'; $subTitle = 'Edit currency "' . e($currency->name) . '"';
$currency->symbol = htmlentities($currency->symbol); $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')); return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
} }
@@ -148,10 +163,12 @@ class CurrencyController extends Controller
Session::flash('success', 'Currency "' . $currency->name . '" created'); Session::flash('success', 'Currency "' . $currency->name . '" created');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
Session::put('currency.create.fromStore', true);
return Redirect::route('currency.create')->withInput(); 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) { 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.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; <?php namespace FireflyIII\Http\Controllers;
use Amount;
use App; use App;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Crypt;
use DB; use DB;
use Exception; use Exception;
use FireflyIII\Helpers\Report\ReportQueryInterface; use FireflyIII\Helpers\Report\ReportQueryInterface;
@@ -255,11 +257,13 @@ class GoogleChartController extends Controller
->where('transaction_types.type', 'Withdrawal') ->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id') ->groupBy('categories.id')
->orderBy('sum', 'DESC') ->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) { foreach ($set as $entry) {
$entry->name = strlen($entry->name) == 0 ? '(no category)' : $entry->name; $isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$chart->addRow($entry->name, floatval($entry->sum)); $name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
} }
$chart->generate(); $chart->generate();
@@ -279,7 +283,7 @@ class GoogleChartController extends Controller
$chart->addColumn('Date', 'date'); $chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number'); $chart->addColumn('Max amount', 'number');
$chart->addColumn('Min 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: // get first transaction or today for start:
$first = $bill->transactionjournals()->orderBy('date', 'ASC')->first(); $first = $bill->transactionjournals()->orderBy('date', 'ASC')->first();
@@ -288,22 +292,18 @@ class GoogleChartController extends Controller
} else { } else {
$start = new Carbon; $start = new Carbon;
} }
$end = new Carbon;
while ($start <= $end) { $results = $bill->transactionjournals()->after($start)->get();
$result = $bill->transactionjournals()->before($end)->after($start)->first(); /** @var TransactionJournal $result */
if ($result) { foreach ($results as $result) {
$amount = 0;
/** @var Transaction $tr */ /** @var Transaction $tr */
foreach ($result->transactions()->get() as $tr) { foreach ($result->transactions()->get() as $tr) {
if (floatval($tr->amount) > 0) { if (floatval($tr->amount) > 0) {
$amount = floatval($tr->amount); $amount = floatval($tr->amount);
} }
} }
} else { $chart->addRow(clone $result->date, $bill->amount_max, $bill->amount_min, $amount);
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $bill->amount_max, $bill->amount_min, $amount);
$start = Navigation::addPeriod($start, $bill->repeat_freq, 0);
} }
$chart->generate(); $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('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']); $chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate(); $chart->generate();

View File

@@ -1,11 +1,12 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
use Session; use Session;
use Redirect;
/** /**
* Class HomeController * Class HomeController
* *
@@ -29,6 +30,11 @@ class HomeController extends Controller
Session::put('end', $end); Session::put('end', $end);
} }
public function flush() {
Session::clear();
return Redirect::route('index');
}
/** /**
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
@@ -46,6 +52,16 @@ class HomeController extends Controller
$accounts = $repository->getFrontpageAccounts($frontPage); $accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts(); $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) { foreach ($accounts as $account) {
$set = $repository->getFrontpageTransactions($account, $start, $end); $set = $repository->getFrontpageTransactions($account, $start, $end);
if (count($set) > 0) { if (count($set) > 0) {

View File

@@ -2,14 +2,18 @@
use Amount; use Amount;
use Auth; use Auth;
use Carbon\Carbon;
use DB; use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
use Response; use Response;
use Session; use Session;
use Steam;
/** /**
* Class JsonController * Class JsonController
@@ -70,11 +74,34 @@ class JsonController extends Controller
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count(); $count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count == 0) { if ($count == 0) {
$amount += floatval($bill->amount_max + $bill->amount_min / 2); $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; break;
case 'bills-paid': case 'bills-paid':
$box = 'bills-paid'; $box = 'bills-paid';
@@ -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]); return Response::json(['box' => $box, 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);

View File

@@ -17,6 +17,7 @@ use Redirect;
use Session; use Session;
use Steam; use Steam;
use View; use View;
use URL;
/** /**
* Class PiggyBankController * Class PiggyBankController
@@ -49,9 +50,6 @@ class PiggyBankController extends Controller
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave); $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')); return view('piggy-banks.add', compact('piggyBank', 'maxAmount'));
} }
@@ -68,6 +66,12 @@ class PiggyBankController extends Controller
$subTitle = 'Create new piggy bank'; $subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus'; $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')); return view('piggy-banks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon'));
} }
@@ -80,6 +84,9 @@ class PiggyBankController extends Controller
{ {
$subTitle = 'Delete "' . e($piggyBank->name) . '"'; $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')); 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.'); Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$piggyBank->delete(); $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); 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')); 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) public function index(AccountRepositoryInterface $repository)
{ {
/** @var Collection $piggyBanks */ /** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->orderBy('order', 'ASC')->get(); $piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get();
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
@@ -157,7 +170,7 @@ class PiggyBankController extends Controller
if (!isset($accounts[$account->id])) { if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [ $accounts[$account->id] = [
'name' => $account->name, 'name' => $account->name,
'balance' => Steam::balance($account), 'balance' => Steam::balance($account,null,true),
'leftForPiggyBanks' => $repository->leftOnAccount($account), 'leftForPiggyBanks' => $repository->leftOnAccount($account),
'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount), 'sumOfTargets' => floatval($piggyBank->targetamount),
@@ -298,7 +311,6 @@ class PiggyBankController extends Controller
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository) public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{ {
$piggyBankData = [ $piggyBankData = [
'repeats' => false,
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => new Carbon, 'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')), 'account_id' => intval($request->get('account_id')),
@@ -313,11 +325,13 @@ class PiggyBankController extends Controller
Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".'); Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('create_another')) === 1) { 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.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) public function update(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request)
{ {
$piggyBankData = [ $piggyBankData = [
'repeats' => false,
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate, 'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')), 'account_id' => intval($request->get('account_id')),
@@ -346,11 +359,13 @@ class PiggyBankController extends Controller
Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".'); Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('return_to_edit')) === 1) { 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.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; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface; use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Preferences; use Preferences;
use Session; use Session;
use Steam; use Steam;
use View; use View;
use FireflyIII\Models\Preference; use Crypt;
/** /**
* Class ReportController * Class ReportController
@@ -22,11 +21,20 @@ use FireflyIII\Models\Preference;
class ReportController extends Controller 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('title', 'Reports');
View::share('mainTitleIcon', 'fa-line-chart'); View::share('mainTitleIcon', 'fa-line-chart');
@@ -38,7 +46,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function budget($year = '2014', $month = '1', ReportQueryInterface $query) public function budget($year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -52,7 +60,7 @@ class ReportController extends Controller
$end->endOfMonth(); $end->endOfMonth();
$start->subDay(); $start->subDay();
// shared accounts preference: /** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false); $pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data; $showSharedReports = $pref->data;
@@ -61,13 +69,13 @@ class ReportController extends Controller
$subTitle = 'Budget report for ' . $date->format('F Y'); $subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar'; $subTitleIcon = 'fa-calendar';
$dayEarly = $dayEarly->subDay(); $dayEarly = $dayEarly->subDay();
$accounts = $query->getAllAccounts($start, $end, $showSharedReports); $accounts = $this->query->getAllAccounts($start, $end, $showSharedReports);
$start->addDay(); $start->addDay();
$accounts->each( $accounts->each(
function (Account $account) use ($start, $end, $query) { function (Account $account) use ($start, $end) {
$budgets = $query->getBudgetSummary($account, $start, $end); $budgets = $this->query->getBudgetSummary($account, $start, $end);
$balancedAmount = $query->balancedTransactionsSum($account, $start, $end); $balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$array = []; $array = [];
$hide = true; $hide = true;
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
@@ -85,35 +93,10 @@ class ReportController extends Controller
} }
); );
$start = clone $date;
$start->startOfMonth();
/** /**
* Start getBudgetsForMonth DONE * Start getBudgetsForMonth DONE
*/ */
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC') $budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
->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;
}
}
/** /**
* End getBudgetsForMonth DONE * End getBudgetsForMonth DONE
@@ -128,11 +111,11 @@ class ReportController extends Controller
* *
* @return View * @return View
*/ */
public function index(ReportHelperInterface $helper) public function index()
{ {
$start = Session::get('first'); $start = Session::get('first');
$months = $helper->listOfMonths($start); $months = $this->helper->listOfMonths($start);
$years = $helper->listOfYears($start); $years = $this->helper->listOfYears($start);
$title = 'Reports'; $title = 'Reports';
$mainTitleIcon = 'fa-line-chart'; $mainTitleIcon = 'fa-line-chart';
@@ -146,7 +129,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @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 { try {
@@ -158,7 +141,7 @@ class ReportController extends Controller
$end = clone $start; $end = clone $start;
$end->endOfMonth(); $end->endOfMonth();
$journals = $query->balancedTransactionsList($account, $start, $end); $journals = $this->query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals')); return view('reports.modal-journal-list', compact('journals'));
@@ -173,7 +156,7 @@ class ReportController extends Controller
* *
* @return View * @return View
*/ */
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query) public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -183,7 +166,7 @@ class ReportController extends Controller
$start = new Carbon($year . '-' . $month . '-01'); $start = new Carbon($year . '-' . $month . '-01');
$end = clone $start; $end = clone $start;
$end->endOfMonth(); $end->endOfMonth();
$set = $query->getTransactionsWithoutBudget($account, $start, $end); $set = $this->query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter( $journals = $set->filter(
function (TransactionJournal $journal) { function (TransactionJournal $journal) {
@@ -204,7 +187,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @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 { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -214,7 +197,7 @@ class ReportController extends Controller
$start = new Carbon($year . '-' . $month . '-01'); $start = new Carbon($year . '-' . $month . '-01');
$end = clone $start; $end = clone $start;
$end->endOfMonth(); $end->endOfMonth();
$journals = $query->getTransactionsWithoutBudget($account, $start, $end); $journals = $this->query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals')); return view('reports.modal-journal-list', compact('journals'));
@@ -226,7 +209,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function month($year = '2014', $month = '1', ReportQueryInterface $query) public function month($year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -237,7 +220,7 @@ class ReportController extends Controller
$subTitle = 'Report for ' . $date->format('F Y'); $subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar'; $subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report. $displaySum = true; // to show sums in report.
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false); $pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data; $showSharedReports = $pref->data;
@@ -256,14 +239,15 @@ class ReportController extends Controller
/** /**
* Start getIncomeForMonth DONE * Start getIncomeForMonth DONE
*/ */
$income = $query->incomeByPeriod($start, $end, $showSharedReports); $income = $this->query->incomeByPeriod($start, $end, $showSharedReports);
/** /**
* End getIncomeForMonth DONE * End getIncomeForMonth DONE
*/ */
/** /**
* Start getExpenseGroupedForMonth DONE * Start getExpenseGroupedForMonth DONE
*/ */
$set = $query->journalsByExpenseAccount($start, $end, $showSharedReports); $set = $this->query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set); $expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses); $expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10); $expenses = Steam::limitArray($expenses, 10);
@@ -273,28 +257,7 @@ class ReportController extends Controller
/** /**
* Start getBudgetsForMonth DONE * Start getBudgetsForMonth DONE
*/ */
$set = Auth::user()->budgets() $budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
->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;
}
}
/** /**
* End getBudgetsForMonth DONE * End getBudgetsForMonth DONE
@@ -303,18 +266,20 @@ class ReportController extends Controller
* Start getCategoriesForMonth DONE * Start getCategoriesForMonth DONE
*/ */
// all categories. // all categories.
$result = $query->journalsByCategory($start, $end); $result = $this->query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result); $categories = Steam::makeArray($result);
// all transfers // all transfers
if ($showSharedReports === false) { if ($showSharedReports === false) {
$result = $query->sharedExpensesByCategory($start, $end); $result = $this->query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result); $transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers); $merged = Steam::mergeArrays($categories, $transfers);
} else { } else {
$merged = $categories; $merged = $categories;
} }
// sort. // sort.
$sorted = Steam::sortNegativeArray($merged); $sorted = Steam::sortNegativeArray($merged);
@@ -326,7 +291,7 @@ class ReportController extends Controller
/** /**
* Start getAccountsForMonth * Start getAccountsForMonth
*/ */
$list = $query->accountList($showSharedReports); $list = $this->query->accountList($showSharedReports);
$accounts = []; $accounts = [];
/** @var Account $account */ /** @var Account $account */
foreach ($list as $account) { foreach ($list as $account) {
@@ -360,7 +325,7 @@ class ReportController extends Controller
* *
* @return $this * @return $this
*/ */
public function year($year, ReportHelperInterface $helper, ReportQueryInterface $query) public function year($year)
{ {
try { try {
new Carbon('01-01-' . $year); new Carbon('01-01-' . $year);
@@ -377,9 +342,9 @@ class ReportController extends Controller
$subTitle = $year; $subTitle = $year;
$subTitleIcon = 'fa-bar-chart'; $subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart'; $mainTitleIcon = 'fa-line-chart';
$balances = $helper->yearBalanceReport($date, $showSharedReports); $balances = $this->helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end, $showSharedReports); $groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end, $showSharedReports); $groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports);
return view( return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon') 'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use Auth;
use Carbon\Carbon;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Events\JournalCreated; use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
@@ -13,9 +12,10 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Input; use Input;
use Redirect; use Redirect;
use Session;
use View;
use Response; use Response;
use Session;
use URL;
use View;
/** /**
* Class TransactionController * Class TransactionController
@@ -61,6 +61,12 @@ class TransactionController extends Controller
} }
Session::put('preFilled', $preFilled); 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); asort($piggies);
@@ -79,6 +85,9 @@ class TransactionController extends Controller
$type = strtolower($journal->transactionType->type); $type = strtolower($journal->transactionType->type);
$subTitle = 'Delete ' . e($type) . ' "' . e($journal->description) . '"'; $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')); return View::make('transactions.delete', compact('journal', 'subTitle'));
@@ -91,23 +100,12 @@ class TransactionController extends Controller
*/ */
public function destroy(TransactionJournal $transactionJournal) public function destroy(TransactionJournal $transactionJournal)
{ {
$type = $transactionJournal->transactionType->type;
$return = 'withdrawal';
Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.'); Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.');
$transactionJournal->delete(); $transactionJournal->delete();
switch ($type) { // redirect to previous URL:
case 'Deposit': return Redirect::to(Session::get('transactions.delete.url'));
$return = 'deposit';
break;
case 'Transfer':
$return = 'transfers';
break;
}
return Redirect::route('transactions.index', $return);
} }
/** /**
@@ -164,6 +162,12 @@ class TransactionController extends Controller
$preFilled['account_from_id'] = $transactions[1]->account->id; $preFilled['account_from_id'] = $transactions[1]->account->id;
$preFilled['account_to_id'] = $transactions[0]->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); return View::make('transactions.edit', compact('journal', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled);
} }
@@ -199,10 +203,7 @@ class TransactionController extends Controller
$page = intval(\Input::get('page')); $page = intval(\Input::get('page'));
$offset = $page > 0 ? ($page - 1) * 50 : 0; $offset = $page > 0 ? ($page - 1) * 50 : 0;
$set = Auth::user()-> $set = Auth::user()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)
transactionJournals()->
transactionTypes($types)->
withRelevantData()->take(50)->offset($offset)
->orderBy('date', 'DESC') ->orderBy('date', 'DESC')
->orderBy('order', 'ASC') ->orderBy('order', 'ASC')
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
@@ -234,6 +235,7 @@ class TransactionController extends Controller
} }
} }
} }
return Response::json(true); return Response::json(true);
} }
@@ -274,25 +276,13 @@ class TransactionController extends Controller
*/ */
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) 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'),
];
$journalData = $request->getJournalData();
$journal = $repository->store($journalData); $journal = $repository->store($journalData);
// rescan journal, UpdateJournalConnection
event(new JournalSaved($journal)); event(new JournalSaved($journal));
// ConnectJournalToPiggyBank
event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
if (intval($request->get('reminder_id')) > 0) { if (intval($request->get('reminder_id')) > 0) {
@@ -304,10 +294,14 @@ class TransactionController extends Controller
Session::flash('success', 'New transaction "' . $journal->description . '" stored!'); Session::flash('success', 'New transaction "' . $journal->description . '" stored!');
if (intval(Input::get('create_another')) === 1) { 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.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) public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository)
{ {
$journalData = $request->getJournalData();
$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'),
];
$repository->update($journal, $journalData); $repository->update($journal, $journalData);
event(new JournalSaved($journal)); event(new JournalSaved($journal));
@@ -346,11 +324,14 @@ class TransactionController extends Controller
Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.'); Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) { 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]);
} }
// redirect to previous URL.
return Redirect::route('transactions.index', $journalData['what']); return Redirect::to(Session::get('transactions.edit.url'));
} }

View File

@@ -13,7 +13,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
use Session; use Session;
use App;
/** /**
* Class PiggyBanks * Class PiggyBanks
@@ -50,12 +50,11 @@ class PiggyBanks
*/ */
public function handle(Request $request, Closure $next) 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: // get piggy banks without a repetition:
/** @var Collection $set */ /** @var Collection $set */
$set = $this->auth->user()->piggybanks() $set = $this->auth->user()->piggybanks()
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id') ->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->where('piggy_banks.repeats', 0)
->whereNull('piggy_bank_repetitions.id') ->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']); ->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) { if ($set->count() > 0) {
@@ -70,68 +69,6 @@ class PiggyBanks
} }
} }
unset($partialPiggy, $set, $repetition); 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); return $next($request);

View File

@@ -3,6 +3,7 @@
namespace FireflyIII\Http\Middleware; namespace FireflyIII\Http\Middleware;
use App;
use Carbon\Carbon; use Carbon\Carbon;
use Closure; use Closure;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
@@ -47,14 +48,14 @@ class Range
*/ */
public function handle(Request $request, Closure $theNext) 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: // ignore preference. set the range to be the current month:
if (!Session::has('start') && !Session::has('end')) { if (!Session::has('start') && !Session::has('end')) {
/** @var \FireflyIII\Models\Preference $viewRange */ /** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M'); $viewRange = Preferences::get('viewRange', '1M');
$start = Session::has('start') ? Session::get('start') : new Carbon; $start = new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start); $start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start); $end = Navigation::updateEndDate($viewRange->data, $start);
@@ -62,11 +63,16 @@ class Range
Session::put('end', $end); Session::put('end', $end);
} }
if (!Session::has('first')) { 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) { if ($journal) {
Session::put('first', $journal->date); Session::put('first', $journal->date);
} else { } 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) 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. // do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get(); $piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon; $today = new Carbon;

View File

@@ -30,18 +30,25 @@ class AccountFormRequest extends Request
{ {
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles'))); $accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier'))); $types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = join(',', array_keys(Config::get('firefly.ccTypes')));
$nameRule = 'required|between:1,100|uniqueAccountForUser'; $nameRule = 'required|between:1,100|uniqueAccountForUser';
$idRule = '';
if (Account::find(Input::get('id'))) { 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 [ return [
'id' => $idRule,
'name' => $nameRule, 'name' => $nameRule,
'openingBalance' => 'numeric', 'openingBalance' => 'numeric',
'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date', 'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles, 'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean', 'active' => 'boolean',
'ccType' => 'in:' . $ccPaymentTypes,
'ccMonthlyPaymentDate' => 'date',
'balance_currency_id' => 'exists:transaction_currencies,id', 'balance_currency_id' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types 'what' => 'in:' . $types
]; ];

View File

@@ -3,6 +3,7 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use Carbon\Carbon;
use Input; use Input;
/** /**
@@ -21,19 +22,41 @@ class BillFormRequest extends Request
return Auth::check(); 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 * @return array
*/ */
public function rules() 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) { if (intval(Input::get('id')) > 0) {
$nameRule = 'required|between:1,255'; $nameRule .= ',' . intval(Input::get('id'));
$matchRule .= ',' . intval(Input::get('id'));
} }
$rules = [ $rules = [
'name' => $nameRule, 'name' => $nameRule,
'match' => 'required|between:1,255', 'match' => $matchRule,
'amount_min' => 'required|numeric|min:0.01', 'amount_min' => 'required|numeric|min:0.01',
'amount_max' => 'required|numeric|min:0.01', 'amount_max' => 'required|numeric|min:0.01',
'amount_currency_id' => 'required|exists:transaction_currencies,id', 'amount_currency_id' => 'required|exists:transaction_currencies,id',

View File

@@ -28,13 +28,14 @@ class BudgetFormRequest extends Request
public function rules() 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'))) { 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 [ return [
'name' => $nameRule, 'name' => $nameRule,
'active' => 'numeric|between:0,1'
]; ];
} }
} }

View File

@@ -28,9 +28,9 @@ class CategoryFormRequest extends Request
public function rules() 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'))) { 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 [ return [

View File

@@ -3,8 +3,10 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use Input; use Carbon\Carbon;
use Exception; use Exception;
use Input;
/** /**
* Class JournalFormRequest * Class JournalFormRequest
* *
@@ -21,6 +23,28 @@ class JournalFormRequest extends Request
return Auth::check(); 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 * @return array
* @throws Exception * @throws Exception

View File

@@ -29,33 +29,20 @@ class PiggyBankFormRequest extends Request
public function rules() public function rules()
{ {
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:piggy_banks,name'; $nameRule = 'required|between:1,255|uniquePiggyBankForUser';
$targetDateRule = 'date'; $targetDateRule = 'date';
if (intval(Input::get('id'))) { if (intval(Input::get('id'))) {
$nameRule = 'required|between:1,255'; $nameRule = 'required|between:1,255|uniquePiggyBankForUser:'.intval(Input::get('id'));
}
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');
}
} }
$rules = [ $rules = [
'repeats' => 'required|boolean',
'name' => $nameRule, 'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts', 'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01', 'targetamount' => 'required|min:0.01',
'amount_currency_id' => 'exists:transaction_currencies,id', 'amount_currency_id' => 'exists:transaction_currencies,id',
'startdate' => 'date', 'startdate' => 'date',
'targetdate' => $targetDateRule, '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' => 'in:day,week,quarter,month,year',
'reminder_skip' => 'integer|min:0|max:99', 'reminder_skip' => 'integer|min:0|max:99',
'remind_me' => 'boolean|piggyBankReminder', 'remind_me' => 'boolean|piggyBankReminder',

View File

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

View File

@@ -107,7 +107,7 @@ Route::bind(
where('piggy_banks.id', $value) where('piggy_banks.id', $value)
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', Auth::user()->id) ->where('accounts.user_id', Auth::user()->id)
->where('repeats', 0)->first(['piggy_banks.*']); ->first(['piggy_banks.*']);
} }
return null; return null;
@@ -167,6 +167,7 @@ Route::group(
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching. 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/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']); 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/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']); Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);

View File

@@ -1,10 +1,13 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\JoinClause;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
use Crypt;
/** /**
* Class Account * Class Account
* *
@@ -14,6 +17,7 @@ class Account extends Model
{ {
use SoftDeletes, ValidatingTrait; use SoftDeletes, ValidatingTrait;
protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance'];
protected $rules protected $rules
= [ = [
'user_id' => 'required|exists:users,id', 'user_id' => 'required|exists:users,id',
@@ -22,39 +26,35 @@ class Account extends Model
'active' => 'required|boolean' '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) { // everything but the name:
if ($meta->name == $fieldName) { $query = Account::orderBy('id');
return $meta->data; foreach ($fields as $name => $value) {
if ($name != 'name') {
$query->where($name, $value);
} }
} }
$set = $query->get(['accounts.*']);
return null; /** @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));
} }
/**
* @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']; 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 EloquentBuilder $query
* @param array $types * @param array $types
@@ -94,6 +136,31 @@ class Account extends Model
$query->whereIn('account_types.type', $types); $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 * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
@@ -110,12 +177,4 @@ class Account extends Model
return $this->belongsTo('FireflyIII\User'); return $this->belongsTo('FireflyIII\User');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBanks()
{
return $this->hasMany('FireflyIII\Models\PiggyBank');
}
} }

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/** /**
@@ -10,7 +11,8 @@ use Illuminate\Database\Eloquent\Model;
class Bill extends 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 * @return array
@@ -20,6 +22,58 @@ class Bill extends Model
return ['created_at', 'updated_at', 'date']; 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 * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
@@ -31,6 +32,23 @@ class Budget extends Model
return ['created_at', 'updated_at', 'deleted_at']; 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 * @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'); 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 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/ */

View File

@@ -2,7 +2,7 @@
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Crypt;
/** /**
* Class Category * Class Category
* *
@@ -38,4 +38,29 @@ class Category extends Model
return $this->belongsTo('FireflyIII\User'); 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

@@ -1,11 +1,8 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use App; use Crypt;
use Log;
/** /**
* Class PiggyBank * Class PiggyBank
* *
@@ -16,8 +13,7 @@ class PiggyBank extends Model
use SoftDeletes; use SoftDeletes;
protected $fillable protected $fillable
= ['repeats', 'name', 'account_id', 'rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me', = ['name', 'account_id', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me'];
'rep_length'];
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
@@ -38,14 +34,10 @@ class PiggyBank extends Model
return $this->currentRep; return $this->currentRep;
} }
// repeating piggy banks are no longer supported. // repeating piggy banks are no longer supported.
if (intval($this->repeats) === 0) {
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']); $rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
$this->currentRep = $rep; $this->currentRep = $rep;
return $rep; return $rep;
} else {
Log::error('Tried to work with a piggy bank with a repeats=1 value! (id is '.$this->id.')');
}
} }
@@ -91,4 +83,29 @@ class PiggyBank extends Model
{ {
return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable'); 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 Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Crypt;
/** /**
* Class Reminder * Class Reminder
* *
@@ -40,6 +40,9 @@ class Reminder extends Model
*/ */
public function getMetadataAttribute($value) public function getMetadataAttribute($value)
{ {
if (intval($this->encrypted) == 1) {
return json_decode(Crypt::decrypt($value));
}
return json_decode($value); return json_decode($value);
} }
@@ -86,7 +89,8 @@ class Reminder extends Model
*/ */
public function setMetadataAttribute($value) 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\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Carbon\Carbon;
/** /**
* Class Transaction * Class Transaction
* *
@@ -30,6 +31,28 @@ class Transaction extends Model
return $this->belongsTo('FireflyIII\Models\Account'); 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 * @return array
*/ */

View File

@@ -12,7 +12,7 @@ use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Log; use Log;
use Reminder;
/** /**
* Class EventServiceProvider * Class EventServiceProvider
* *
@@ -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( Account::deleted(
function (Account $account) { function (Account $account) {

View File

@@ -48,6 +48,18 @@ class AccountRepository implements AccountRepositoryInterface
return true; 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 * @param Preference $preference
* *
@@ -119,7 +131,6 @@ class AccountRepository implements AccountRepositoryInterface
return $paginator; return $paginator;
} }
/** /**
@@ -134,8 +145,8 @@ class AccountRepository implements AccountRepositoryInterface
->where('account_meta.name', 'accountRole') ->where('account_meta.name', 'accountRole')
->where('account_meta.data', '"savingAsset"') ->where('account_meta.data', '"savingAsset"')
->get(['accounts.*']); ->get(['accounts.*']);
$start = clone Session::get('start'); $start = clone Session::get('start', new Carbon);
$end = clone Session::get('end'); $end = clone Session::get('end', new Carbon);
$accounts->each( $accounts->each(
function (Account $account) use ($start, $end) { function (Account $account) use ($start, $end) {
@@ -171,7 +182,7 @@ class AccountRepository implements AccountRepositoryInterface
*/ */
public function leftOnAccount(Account $account) public function leftOnAccount(Account $account)
{ {
$balance = \Steam::balance($account); $balance = \Steam::balance($account, null, true);
/** @var PiggyBank $p */ /** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) { foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount; $balance -= $p->currentRelevantRep()->currentamount;
@@ -211,6 +222,7 @@ class AccountRepository implements AccountRepositoryInterface
$opposingData = [ $opposingData = [
'user' => $data['user'], 'user' => $data['user'],
'accountType' => $type, 'accountType' => $type,
'virtual_balance' => $data['virtualBalance'],
'name' => $data['name'] . ' initial balance', 'name' => $data['name'] . ' initial balance',
'active' => false, 'active' => false,
]; ];
@@ -232,6 +244,7 @@ class AccountRepository implements AccountRepositoryInterface
// update the account: // update the account:
$account->name = $data['name']; $account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false; $account->active = $data['active'] == '1' ? true : false;
$account->virtual_balance = $data['virtualBalance'];
$account->save(); $account->save();
// update meta data: // update meta data:
@@ -308,19 +321,23 @@ class AccountRepository implements AccountRepositoryInterface
*/ */
protected function storeMetadata(Account $account, array $data) protected function storeMetadata(Account $account, array $data)
{ {
$validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType'];
foreach ($validFields as $field) {
if (isset($data[$field])) {
$metaData = new AccountMeta( $metaData = new AccountMeta(
[ [
'account_id' => $account->id, 'account_id' => $account->id,
'name' => 'accountRole', 'name' => $field,
'data' => $data['accountRole'] 'data' => $data[$field]
] ]
); );
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save(); $metaData->save();
} }
}
}
/** /**
* @param Account $account * @param Account $account
* @param Account $opposing * @param Account $opposing
@@ -399,31 +416,29 @@ class AccountRepository implements AccountRepositoryInterface
*/ */
protected function updateMetadata(Account $account, array $data) protected function updateMetadata(Account $account, array $data)
{ {
$metaEntries = $account->accountMeta()->get(); $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType'];
$updated = false; $updated = false;
/** @var AccountMeta $entry */ foreach ($validFields as $field) {
foreach ($metaEntries as $entry) { $entry = $account->accountMeta()->where('name', $field)->first();
if ($entry->name == 'accountRole') {
$entry->data = $data['accountRole']; // update if new data is present:
$updated = true; if ($entry && isset($data[$field])) {
$entry->data = $data[$field];
$entry->save(); $entry->save();
} }
} // no entry but data present?
if (!$entry && isset($data[$field])) {
if ($updated === false) {
$metaData = new AccountMeta( $metaData = new AccountMeta(
[ [
'account_id' => $account->id, 'account_id' => $account->id,
'name' => 'accountRole', 'name' => $field,
'data' => $data['accountRole'] 'data' => $data[$field]
] ]
); );
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save(); $metaData->save();
} }
}
} }

View File

@@ -2,11 +2,13 @@
namespace FireflyIII\Repositories\Account; namespace FireflyIII\Repositories\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Preference;
use Illuminate\Support\Collection;
use Carbon\Carbon; 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 * Interface AccountRepositoryInterface
* *
@@ -85,4 +87,13 @@ interface AccountRepositoryInterface
* @return Collection * @return Collection
*/ */
public function getSavingsAccounts(); 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 * @package FireflyIII\Repositories\Bill
*/ */
interface BillRepositoryInterface { interface BillRepositoryInterface
{
/** /**
* @param Bill $bill * @param Bill $bill

View File

@@ -16,6 +16,31 @@ use Illuminate\Pagination\LengthAwarePaginator;
class BudgetRepository implements BudgetRepositoryInterface 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 * @param Budget $budget
* *
@@ -57,6 +82,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$set = $setQuery->get(['transaction_journals.*']); $set = $setQuery->get(['transaction_journals.*']);
$count = $countQuery->count(); $count = $countQuery->count();
return new LengthAwarePaginator($set, $count, $take, $offset); return new LengthAwarePaginator($set, $count, $take, $offset);
} }
@@ -104,6 +130,7 @@ class BudgetRepository implements BudgetRepositoryInterface
{ {
// update the account: // update the account:
$budget->name = $data['name']; $budget->name = $data['name'];
$budget->active = $data['active'];
$budget->save(); $budget->save();
return $budget; return $budget;
@@ -118,10 +145,12 @@ class BudgetRepository implements BudgetRepositoryInterface
*/ */
public function updateLimitAmount(Budget $budget, Carbon $date, $amount) public function updateLimitAmount(Budget $budget, Carbon $date, $amount)
{ {
// there should be a budget limit for this startdate:
/** @var BudgetLimit $limit */ /** @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) { if (!$limit) {
// create one! // if not, create one!
$limit = new BudgetLimit; $limit = new BudgetLimit;
$limit->budget()->associate($budget); $limit->budget()->associate($budget);
$limit->startdate = $date; $limit->startdate = $date;
@@ -129,6 +158,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$limit->repeat_freq = 'monthly'; $limit->repeat_freq = 'monthly';
$limit->repeats = 0; $limit->repeats = 0;
$limit->save(); $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 { } else {
if ($amount > 0) { if ($amount > 0) {
$limit->amount = $amount; $limit->amount = $amount;

View File

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

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
use App;
use Auth; use Auth;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
@@ -11,6 +12,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
/** /**
* Class JournalRepository * Class JournalRepository
@@ -20,6 +22,16 @@ use Illuminate\Support\Collection;
class JournalRepository implements JournalRepositoryInterface 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. * 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) // store accounts (depends on type)
switch ($transactionType->type) { list($from, $to) = $this->storeAccounts($transactionType, $data);
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;
}
// store accompanying transactions. // store accompanying transactions.
Transaction::create( // first transaction. Transaction::create( // first transaction.
@@ -198,7 +176,6 @@ class JournalRepository implements JournalRepositoryInterface
} }
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param array $data * @param array $data
@@ -228,41 +205,7 @@ class JournalRepository implements JournalRepositoryInterface
} }
// store accounts (depends on type) // store accounts (depends on type)
switch ($journal->transactionType->type) { list($from, $to) = $this->storeAccounts($journal->transactionType, $data);
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;
}
// update the from and to transaction. // update the from and to transaction.
/** @var Transaction $transaction */ /** @var Transaction $transaction */
@@ -286,4 +229,65 @@ class JournalRepository implements JournalRepositoryInterface
return $journal; 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; namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@@ -44,4 +45,10 @@ interface JournalRepositoryInterface
* @return mixed * @return mixed
*/ */
public function update(TransactionJournal $journal, array $data); public function update(TransactionJournal $journal, array $data);
/**
* Get users first transaction journal
* @return TransactionJournal
*/
public function first();
} }

View File

@@ -153,9 +153,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
$piggyBank->targetdate = $data['targetdate']; $piggyBank->targetdate = $data['targetdate'];
$piggyBank->reminder = $data['reminder']; $piggyBank->reminder = $data['reminder'];
$piggyBank->startdate = $data['startdate']; $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->remind_me = isset($data['remind_me']) && $data['remind_me'] == '1' ? 1 : 0;
$piggyBank->save(); $piggyBank->save();

View File

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

View File

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

View File

@@ -34,7 +34,7 @@ class ExpandedForm
$options['step'] = 'any'; $options['step'] = 'any';
$options['min'] = '0.01'; $options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency(); $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(); $html = View::make('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html; return $html;
@@ -62,8 +62,12 @@ class ExpandedForm
'account_id' => 'Asset account', 'account_id' => 'Asset account',
'budget_id' => 'Budget', 'budget_id' => 'Budget',
'openingBalance' => 'Opening balance', 'openingBalance' => 'Opening balance',
'virtualBalance' => 'Virtual balance',
'targetamount' => 'Target amount',
'accountRole' => 'Account role', 'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date', 'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank']; 'piggy_bank_id' => 'Piggy bank'];
@@ -143,7 +147,7 @@ class ExpandedForm
$value = $this->fillFieldValue($name, $value); $value = $this->fillFieldValue($name, $value);
$options['step'] = 'any'; $options['step'] = 'any';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency(); $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(); $html = View::make('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html; return $html;
@@ -190,6 +194,24 @@ class ExpandedForm
return $html; 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 $name
* @param null $value * @param null $value

View File

@@ -344,6 +344,48 @@ class Navigation
throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq); 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 $range
* @param Carbon $start * @param Carbon $start
@@ -414,47 +456,5 @@ class Navigation
throw new FireflyException('updateStartDate cannot handle $range ' . $range); 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 $name
* @param null $default * @param null $default
* *
* @return null|\Preference * @return null|Preference
*/ */
public function get($name, $default = null) public function get($name, $default = null)
{ {
@@ -37,7 +37,7 @@ class Preferences
* @param $name * @param $name
* @param $value * @param $value
* *
* @return \Preference * @return Preference
*/ */
public function set($name, $value) public function set($name, $value)
{ {

View File

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

View File

@@ -19,10 +19,11 @@ class Steam
* *
* @param Account $account * @param Account $account
* @param Carbon $date * @param Carbon $date
* @param bool $ignoreVirtualBalance
* *
* @return float * @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; $date = is_null($date) ? Carbon::now() : $date;
@@ -47,6 +48,9 @@ class Steam
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount') )->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
); );
if (!$ignoreVirtualBalance) {
$balance += floatval($account->virtual_balance);
}
return $balance; return $balance;
} }
@@ -100,10 +104,12 @@ class Steam
if (isset($array[$id])) { if (isset($array[$id])) {
$array[$id]['amount'] += floatval($entry->amount); $array[$id]['amount'] += floatval($entry->amount);
$array[$id]['spent'] += floatval($entry->spent); $array[$id]['spent'] += floatval($entry->spent);
$array[$id]['encrypted'] = intval($entry->encrypted);
} else { } else {
$array[$id] = [ $array[$id] = [
'amount' => floatval($entry->amount), 'amount' => floatval($entry->amount),
'spent' => floatval($entry->spent), 'spent' => floatval($entry->spent),
'encrypted' => intval($entry->encrypted),
'name' => $entry->name '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 $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 * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -5,10 +5,13 @@ namespace FireflyIII\Validation;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config; use Config;
use Crypt;
use DB; use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use Input; use Log;
use Navigation; use Navigation;
/** /**
@@ -28,6 +31,7 @@ class FireflyValidator extends Validator
*/ */
public function validateBelongsToUser($attribute, $value, $parameters) public function validateBelongsToUser($attribute, $value, $parameters)
{ {
$count = DB::table($parameters[0])->where('user_id', Auth::user()->id)->where('id', $value)->count(); $count = DB::table($parameters[0])->where('user_id', Auth::user()->id)->where('id', $value)->count();
if ($count == 1) { if ($count == 1) {
return true; return true;
@@ -79,44 +83,62 @@ class FireflyValidator extends Validator
*/ */
public function validateUniqueAccountForUser($attribute, $value, $parameters) public function validateUniqueAccountForUser($attribute, $value, $parameters)
{ {
// get account type from data, we must have this: $type = null;
$validTypes = array_keys(Config::get('firefly.subTitlesByIdentifier'));
$type = isset($this->data['what']) && in_array($this->data['what'], $validTypes) ? $this->data['what'] : null; /**
// some fallback: * Switch on different cases on which this method can respond:
if (is_null($type)) { */
$type = in_array(Input::get('what'), $validTypes) ? Input::get('what') : null; $hasWhat = isset($this->data['what']);
} $hasAccountTypeId = isset($this->data['account_type_id']) && isset($this->data['name']);
// still null? $hasAccountId = isset($this->data['id']);
if (is_null($type)) { $ignoreId = 0;
// find by other field:
$type = isset($this->data['account_type_id']) ? $this->data['account_type_id'] : 0;
$dbType = AccountType::find($type); if ($hasWhat) {
} else { $search = Config::get('firefly.accountTypeByIdentifier.' . $this->data['what']);
$longType = Config::get('firefly.accountTypeByIdentifier.' . $type); $type = AccountType::whereType($search)->first();
$dbType = AccountType::whereType($longType)->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; return false;
} }
// user id? // get all accounts with this type, and find the name.
$userId = Auth::check() ? Auth::user()->id : $this->data['user_id']; $userId = Auth::check() ? Auth::user()->id : 0;
$set = Account::where('account_type_id', $type->id)->where('id', '!=', $ignoreId)->where('user_id', $userId)->get();
$query = DB::table('accounts')->where('name', $value)->where('account_type_id', $dbType->id)->where('user_id', $userId); /** @var Account $entry */
foreach ($set as $entry) {
if (isset($parameters[0])) { if ($entry->name == $value) {
$query->where('id', '!=', $parameters[0]); return false;
}
} }
$count = $query->count();
if ($count == 0) {
return true; return true;
}
return false;
} }
@@ -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 $attribute
* @param $value * @param $value
@@ -152,18 +214,24 @@ class FireflyValidator extends Validator
*/ */
public function validateUniquePiggyBankForUser($attribute, $value, $parameters) 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->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id');
$query->where('accounts.user_id', Auth::user()->id); $query->where('accounts.user_id', Auth::user()->id);
if (isset($paramers[2])) { if (!is_null($exclude)) {
$query->where('piggy_banks.id', '!=', $parameters[2]); $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", "barryvdh/laravel-ide-helper": "~2.0",
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1", "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": { "autoload": {
"classmap": [ "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", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "b77b9f717b25e1e193bdc6edb18ad492", "hash": "0d43c4c85607c5cdc901cde2d18b75d5",
"packages": [ "packages": [
{ {
"name": "classpreloader/classpreloader", "name": "classpreloader/classpreloader",
@@ -412,16 +412,16 @@
}, },
{ {
"name": "doctrine/common", "name": "doctrine/common",
"version": "v2.4.2", "version": "v2.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/common.git", "url": "https://github.com/doctrine/common.git",
"reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b" "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/common/zipball/5db6ab40e4c531f14dad4ca96a394dfce5d4255b", "url": "https://api.github.com/repos/doctrine/common/zipball/cd8daf2501e10c63dced7b8b9b905844316ae9d3",
"reference": "5db6ab40e4c531f14dad4ca96a394dfce5d4255b", "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -438,7 +438,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.4.x-dev" "dev-master": "2.6.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -451,17 +451,6 @@
"MIT" "MIT"
], ],
"authors": [ "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", "name": "Roman Borschel",
"email": "roman@code-factory.org" "email": "roman@code-factory.org"
@@ -470,11 +459,17 @@
"name": "Benjamin Eberlei", "name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de" "email": "kontakt@beberlei.de"
}, },
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{ {
"name": "Johannes Schmitt", "name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com", "email": "schmittjoh@gmail.com"
"homepage": "https://github.com/schmittjoh",
"role": "Developer of wrapped JMSSerializerBundle"
} }
], ],
"description": "Common Library for Doctrine projects", "description": "Common Library for Doctrine projects",
@@ -486,7 +481,7 @@
"persistence", "persistence",
"spl" "spl"
], ],
"time": "2014-05-21 19:28:51" "time": "2015-04-02 19:55:44"
}, },
{ {
"name": "doctrine/dbal", "name": "doctrine/dbal",
@@ -950,16 +945,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v5.0.23", "version": "v5.0.26",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "59219f7afb60be05d74ce01fcb5d2440f7a1b13d" "reference": "8e53c33e144f94032cc6ecbfee0be2a96ed63be0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/59219f7afb60be05d74ce01fcb5d2440f7a1b13d", "url": "https://api.github.com/repos/laravel/framework/zipball/8e53c33e144f94032cc6ecbfee0be2a96ed63be0",
"reference": "59219f7afb60be05d74ce01fcb5d2440f7a1b13d", "reference": "8e53c33e144f94032cc6ecbfee0be2a96ed63be0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1072,7 +1067,7 @@
"framework", "framework",
"laravel" "laravel"
], ],
"time": "2015-03-28 16:56:59" "time": "2015-04-03 02:58:05"
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
@@ -1135,16 +1130,16 @@
}, },
{ {
"name": "league/flysystem", "name": "league/flysystem",
"version": "1.0.2", "version": "1.0.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/flysystem.git", "url": "https://github.com/thephpleague/flysystem.git",
"reference": "51cd7cd7ee0defbaafc6ec0d3620110a5d71e11a" "reference": "3c2400a99ccc3be6884d40361890010449c6b447"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/51cd7cd7ee0defbaafc6ec0d3620110a5d71e11a", "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3c2400a99ccc3be6884d40361890010449c6b447",
"reference": "51cd7cd7ee0defbaafc6ec0d3620110a5d71e11a", "reference": "3c2400a99ccc3be6884d40361890010449c6b447",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1154,7 +1149,7 @@
"ext-fileinfo": "*", "ext-fileinfo": "*",
"league/phpunit-coverage-listener": "~1.1", "league/phpunit-coverage-listener": "~1.1",
"mockery/mockery": "~0.9", "mockery/mockery": "~0.9",
"phpspec/phpspec": "~2.0.0", "phpspec/phpspec": "~2.0",
"phpspec/prophecy-phpunit": "~1.0", "phpspec/prophecy-phpunit": "~1.0",
"phpunit/phpunit": "~4.1", "phpunit/phpunit": "~4.1",
"predis/predis": "~1.0", "predis/predis": "~1.0",
@@ -1214,7 +1209,7 @@
"sftp", "sftp",
"storage" "storage"
], ],
"time": "2015-03-10 11:04:14" "time": "2015-03-29 14:01:43"
}, },
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
@@ -1588,17 +1583,17 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Console", "target-dir": "Symfony/Component/Console",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Console.git", "url": "https://github.com/symfony/Console.git",
"reference": "53f86497ccd01677e22435cfb7262599450a90d1" "reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/53f86497ccd01677e22435cfb7262599450a90d1", "url": "https://api.github.com/repos/symfony/Console/zipball/5b91dc4ed5eb08553f57f6df04c4730a73992667",
"reference": "53f86497ccd01677e22435cfb7262599450a90d1", "reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1642,21 +1637,21 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/debug", "name": "symfony/debug",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Debug", "target-dir": "Symfony/Component/Debug",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Debug.git", "url": "https://github.com/symfony/Debug.git",
"reference": "5c1570dea188ade0c6c5e874c2f0a6570587aa1c" "reference": "d49a46a20a8f0544aedac54466750ad787d3d3e3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Debug/zipball/5c1570dea188ade0c6c5e874c2f0a6570587aa1c", "url": "https://api.github.com/repos/symfony/Debug/zipball/d49a46a20a8f0544aedac54466750ad787d3d3e3",
"reference": "5c1570dea188ade0c6c5e874c2f0a6570587aa1c", "reference": "d49a46a20a8f0544aedac54466750ad787d3d3e3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1703,11 +1698,11 @@
], ],
"description": "Symfony Debug Component", "description": "Symfony Debug Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22" "time": "2015-03-22 16:55:57"
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/EventDispatcher", "target-dir": "Symfony/Component/EventDispatcher",
"source": { "source": {
"type": "git", "type": "git",
@@ -1766,17 +1761,17 @@
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Filesystem", "target-dir": "Symfony/Component/Filesystem",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Filesystem.git", "url": "https://github.com/symfony/Filesystem.git",
"reference": "fdc5f151bc2db066b51870d5bea3773d915ced0b" "reference": "4983964b3693e4f13449cb3800c64a9112c301b4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/fdc5f151bc2db066b51870d5bea3773d915ced0b", "url": "https://api.github.com/repos/symfony/Filesystem/zipball/4983964b3693e4f13449cb3800c64a9112c301b4",
"reference": "fdc5f151bc2db066b51870d5bea3773d915ced0b", "reference": "4983964b3693e4f13449cb3800c64a9112c301b4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1812,21 +1807,21 @@
], ],
"description": "Symfony Filesystem Component", "description": "Symfony Filesystem Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44" "time": "2015-03-22 16:55:57"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Finder", "target-dir": "Symfony/Component/Finder",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Finder.git", "url": "https://github.com/symfony/Finder.git",
"reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e" "reference": "5dbe2e73a580618f5b4880fda93406eed25de251"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/bebc7479c566fa4f14b9bcef9e32e719eabec74e", "url": "https://api.github.com/repos/symfony/Finder/zipball/5dbe2e73a580618f5b4880fda93406eed25de251",
"reference": "bebc7479c566fa4f14b9bcef9e32e719eabec74e", "reference": "5dbe2e73a580618f5b4880fda93406eed25de251",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1862,21 +1857,21 @@
], ],
"description": "Symfony Finder Component", "description": "Symfony Finder Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/http-foundation", "name": "symfony/http-foundation",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/HttpFoundation", "target-dir": "Symfony/Component/HttpFoundation",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/HttpFoundation.git", "url": "https://github.com/symfony/HttpFoundation.git",
"reference": "d527885e37b55ec0e3dc6f4b70566d0f9b2f2388" "reference": "8a6337233f08f7520de97f4ffd6f00e947d892f9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/d527885e37b55ec0e3dc6f4b70566d0f9b2f2388", "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/8a6337233f08f7520de97f4ffd6f00e947d892f9",
"reference": "d527885e37b55ec0e3dc6f4b70566d0f9b2f2388", "reference": "8a6337233f08f7520de97f4ffd6f00e947d892f9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1916,21 +1911,21 @@
], ],
"description": "Symfony HttpFoundation Component", "description": "Symfony HttpFoundation Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22" "time": "2015-04-01 16:50:12"
}, },
{ {
"name": "symfony/http-kernel", "name": "symfony/http-kernel",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/HttpKernel", "target-dir": "Symfony/Component/HttpKernel",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/HttpKernel.git", "url": "https://github.com/symfony/HttpKernel.git",
"reference": "6f7b2d3ba8bf02cf77edb399696e85ef24a888a4" "reference": "3829cacfe21eaf3f73604a62d79183d1f6e792c4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/6f7b2d3ba8bf02cf77edb399696e85ef24a888a4", "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/3829cacfe21eaf3f73604a62d79183d1f6e792c4",
"reference": "6f7b2d3ba8bf02cf77edb399696e85ef24a888a4", "reference": "3829cacfe21eaf3f73604a62d79183d1f6e792c4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1994,21 +1989,21 @@
], ],
"description": "Symfony HttpKernel Component", "description": "Symfony HttpKernel Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-17 14:58:46" "time": "2015-04-01 16:55:26"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Process", "target-dir": "Symfony/Component/Process",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Process.git", "url": "https://github.com/symfony/Process.git",
"reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc" "reference": "a8bebaec1a9dc6cde53e0250e32917579b0be552"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc", "url": "https://api.github.com/repos/symfony/Process/zipball/a8bebaec1a9dc6cde53e0250e32917579b0be552",
"reference": "4d717f34f3d1d6ab30fbe79f7132960a27f4a0dc", "reference": "a8bebaec1a9dc6cde53e0250e32917579b0be552",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2044,21 +2039,21 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/routing", "name": "symfony/routing",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Routing", "target-dir": "Symfony/Component/Routing",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Routing.git", "url": "https://github.com/symfony/Routing.git",
"reference": "a7f3eb540e5c553c3c95993c6fc2e7edb2f3b9d2" "reference": "4e173a645b63ff60a124f3741b4f15feebd908fa"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/a7f3eb540e5c553c3c95993c6fc2e7edb2f3b9d2", "url": "https://api.github.com/repos/symfony/Routing/zipball/4e173a645b63ff60a124f3741b4f15feebd908fa",
"reference": "a7f3eb540e5c553c3c95993c6fc2e7edb2f3b9d2", "reference": "4e173a645b63ff60a124f3741b4f15feebd908fa",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2113,21 +2108,21 @@
"uri", "uri",
"url" "url"
], ],
"time": "2015-03-13 17:37:22" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/security-core", "name": "symfony/security-core",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Security/Core", "target-dir": "Symfony/Component/Security/Core",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/security-core.git", "url": "https://github.com/symfony/security-core.git",
"reference": "889290a5c00d3f174cc73ce13a11a0a6406939e9" "reference": "d25c17db741f58c0f615e52006a47f6fb23cd9b3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/security-core/zipball/889290a5c00d3f174cc73ce13a11a0a6406939e9", "url": "https://api.github.com/repos/symfony/security-core/zipball/d25c17db741f58c0f615e52006a47f6fb23cd9b3",
"reference": "889290a5c00d3f174cc73ce13a11a0a6406939e9", "reference": "d25c17db741f58c0f615e52006a47f6fb23cd9b3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2177,21 +2172,21 @@
], ],
"description": "Symfony Security Component - Core Library", "description": "Symfony Security Component - Core Library",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/translation", "name": "symfony/translation",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Translation", "target-dir": "Symfony/Component/Translation",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Translation.git", "url": "https://github.com/symfony/Translation.git",
"reference": "043db5f1eef9598d1bc1d75b93304984c003d7d9" "reference": "bd939f05cdaca128f4ddbae1b447d6f0203b60af"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Translation/zipball/043db5f1eef9598d1bc1d75b93304984c003d7d9", "url": "https://api.github.com/repos/symfony/Translation/zipball/bd939f05cdaca128f4ddbae1b447d6f0203b60af",
"reference": "043db5f1eef9598d1bc1d75b93304984c003d7d9", "reference": "bd939f05cdaca128f4ddbae1b447d6f0203b60af",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2236,21 +2231,21 @@
], ],
"description": "Symfony Translation Component", "description": "Symfony Translation Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-14 11:42:25" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/var-dumper", "name": "symfony/var-dumper",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/VarDumper", "target-dir": "Symfony/Component/VarDumper",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/var-dumper.git", "url": "https://github.com/symfony/var-dumper.git",
"reference": "61ee6c848fd2c623e13f59df48833f8b8bad7fda" "reference": "aafae00236e147568832de3c65ccb94cfc836278"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/61ee6c848fd2c623e13f59df48833f8b8bad7fda", "url": "https://api.github.com/repos/symfony/var-dumper/zipball/aafae00236e147568832de3c65ccb94cfc836278",
"reference": "61ee6c848fd2c623e13f59df48833f8b8bad7fda", "reference": "aafae00236e147568832de3c65ccb94cfc836278",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2296,7 +2291,7 @@
"debug", "debug",
"dump" "dump"
], ],
"time": "2015-03-06 16:45:31" "time": "2015-03-31 08:12:29"
}, },
{ {
"name": "vlucas/phpdotenv", "name": "vlucas/phpdotenv",
@@ -2577,6 +2572,54 @@
], ],
"time": "2014-10-13 12:58:55" "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", "name": "guzzle/guzzle",
"version": "v3.9.3", "version": "v3.9.3",
@@ -2672,6 +2715,112 @@
], ],
"time": "2015-03-18 18:23:50" "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", "name": "maximebf/debugbar",
"version": "v1.10.4", "version": "v1.10.4",
@@ -2728,6 +2877,71 @@
], ],
"time": "2015-02-05 07:51:20" "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", "name": "phpdocumentor/reflection-docblock",
"version": "2.0.4", "version": "2.0.4",
@@ -2889,21 +3103,22 @@
}, },
{ {
"name": "phpspec/prophecy", "name": "phpspec/prophecy",
"version": "v1.3.1", "version": "1.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpspec/prophecy.git", "url": "https://github.com/phpspec/prophecy.git",
"reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9" "reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/9ca52329bcdd1500de24427542577ebf3fc2f1c9", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5",
"reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9", "reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/instantiator": "~1.0,>=1.0.2", "doctrine/instantiator": "^1.0.2",
"phpdocumentor/reflection-docblock": "~2.0" "phpdocumentor/reflection-docblock": "~2.0",
"sebastian/comparator": "~1.1"
}, },
"require-dev": { "require-dev": {
"phpspec/phpspec": "~2.0" "phpspec/phpspec": "~2.0"
@@ -2911,7 +3126,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.2.x-dev" "dev-master": "1.4.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -2935,7 +3150,7 @@
} }
], ],
"description": "Highly opinionated mocking framework for PHP 5.3+", "description": "Highly opinionated mocking framework for PHP 5.3+",
"homepage": "http://phpspec.org", "homepage": "https://github.com/phpspec/prophecy",
"keywords": [ "keywords": [
"Double", "Double",
"Dummy", "Dummy",
@@ -2944,7 +3159,7 @@
"spy", "spy",
"stub" "stub"
], ],
"time": "2014-11-17 16:23:49" "time": "2015-03-27 19:31:25"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@@ -3192,16 +3407,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "4.5.0", "version": "4.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5" "reference": "d6429b0995b24a2d9dfe5587ee3a7071c1161af4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5b578d3865a9128b9c209b011fda6539ec06e7a5", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d6429b0995b24a2d9dfe5587ee3a7071c1161af4",
"reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5", "reference": "d6429b0995b24a2d9dfe5587ee3a7071c1161af4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3211,8 +3426,8 @@
"ext-reflection": "*", "ext-reflection": "*",
"ext-spl": "*", "ext-spl": "*",
"php": ">=5.3.3", "php": ">=5.3.3",
"phpspec/prophecy": "~1.3.1", "phpspec/prophecy": "~1.3,>=1.3.1",
"phpunit/php-code-coverage": "~2.0", "phpunit/php-code-coverage": "~2.0,>=2.0.11",
"phpunit/php-file-iterator": "~1.3.2", "phpunit/php-file-iterator": "~1.3.2",
"phpunit/php-text-template": "~1.2", "phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "~1.0.2", "phpunit/php-timer": "~1.0.2",
@@ -3260,29 +3475,29 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2015-02-05 15:51:19" "time": "2015-03-29 09:24:05"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
"version": "2.3.0", "version": "2.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "c63d2367247365f688544f0d500af90a11a44c65" "reference": "74ffb87f527f24616f72460e54b595f508dccb5c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65", "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/74ffb87f527f24616f72460e54b595f508dccb5c",
"reference": "c63d2367247365f688544f0d500af90a11a44c65", "reference": "74ffb87f527f24616f72460e54b595f508dccb5c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/instantiator": "~1.0,>=1.0.1", "doctrine/instantiator": "~1.0,>=1.0.2",
"php": ">=5.3.3", "php": ">=5.3.3",
"phpunit/php-text-template": "~1.2" "phpunit/php-text-template": "~1.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.3" "phpunit/phpunit": "~4.4"
}, },
"suggest": { "suggest": {
"ext-soap": "*" "ext-soap": "*"
@@ -3315,7 +3530,7 @@
"mock", "mock",
"xunit" "xunit"
], ],
"time": "2014-10-03 05:12:11" "time": "2015-04-02 05:36:41"
}, },
{ {
"name": "satooshi/php-coveralls", "name": "satooshi/php-coveralls",
@@ -3758,17 +3973,17 @@
}, },
{ {
"name": "symfony/class-loader", "name": "symfony/class-loader",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/ClassLoader", "target-dir": "Symfony/Component/ClassLoader",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/ClassLoader.git", "url": "https://github.com/symfony/ClassLoader.git",
"reference": "56bf6fe551ca013471541d866f73a6cc70ece9c5" "reference": "861765b3e5f32979de5bd19ad2577cbb830a29d5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/ClassLoader/zipball/56bf6fe551ca013471541d866f73a6cc70ece9c5", "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/861765b3e5f32979de5bd19ad2577cbb830a29d5",
"reference": "56bf6fe551ca013471541d866f73a6cc70ece9c5", "reference": "861765b3e5f32979de5bd19ad2577cbb830a29d5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3805,21 +4020,21 @@
], ],
"description": "Symfony ClassLoader Component", "description": "Symfony ClassLoader Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-13 17:37:22" "time": "2015-03-27 10:19:51"
}, },
{ {
"name": "symfony/config", "name": "symfony/config",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Config", "target-dir": "Symfony/Component/Config",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Config.git", "url": "https://github.com/symfony/Config.git",
"reference": "7a47189c7667ca69bcaafd19ef8a8941db449a2c" "reference": "d91be01336605db8da21b79bc771e46a7276d1bc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/7a47189c7667ca69bcaafd19ef8a8941db449a2c", "url": "https://api.github.com/repos/symfony/Config/zipball/d91be01336605db8da21b79bc771e46a7276d1bc",
"reference": "7a47189c7667ca69bcaafd19ef8a8941db449a2c", "reference": "d91be01336605db8da21b79bc771e46a7276d1bc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3856,21 +4071,21 @@
], ],
"description": "Symfony Config Component", "description": "Symfony Config Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44" "time": "2015-03-30 15:54:10"
}, },
{ {
"name": "symfony/stopwatch", "name": "symfony/stopwatch",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Stopwatch", "target-dir": "Symfony/Component/Stopwatch",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Stopwatch.git", "url": "https://github.com/symfony/Stopwatch.git",
"reference": "ba4e774f71e2ce3e3f65cabac4031b9029972af5" "reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/ba4e774f71e2ce3e3f65cabac4031b9029972af5", "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/5f196e84b5640424a166d2ce9cca161ce1e9d912",
"reference": "ba4e774f71e2ce3e3f65cabac4031b9029972af5", "reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3906,21 +4121,21 @@
], ],
"description": "Symfony Stopwatch Component", "description": "Symfony Stopwatch Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-02-24 11:52:21" "time": "2015-03-22 16:55:57"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v2.6.5", "version": "v2.6.6",
"target-dir": "Symfony/Component/Yaml", "target-dir": "Symfony/Component/Yaml",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Yaml.git", "url": "https://github.com/symfony/Yaml.git",
"reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d" "reference": "174f009ed36379a801109955fc5a71a49fe62dd4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/0cd8e72071e46e15fc072270ae39ea1b66b10a9d", "url": "https://api.github.com/repos/symfony/Yaml/zipball/174f009ed36379a801109955fc5a71a49fe62dd4",
"reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d", "reference": "174f009ed36379a801109955fc5a71a49fe62dd4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3956,7 +4171,7 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2015-03-12 10:28:44" "time": "2015-03-30 15:54:10"
} }
], ],
"aliases": [], "aliases": [],

View File

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

View File

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

View File

@@ -1,13 +1,12 @@
<?php <?php
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\Component; use FireflyIII\Models\Component;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
/** /**
* @SuppressWarnings(PHPMD.ShortMethodName) // method names are mandated by laravel. * @SuppressWarnings(PHPMD.ShortMethodName) // method names are mandated by laravel.
* @SuppressWarnings("TooManyMethods") // I'm fine with this * @SuppressWarnings("TooManyMethods") // I'm fine with this
@@ -427,28 +426,28 @@ class ChangesForV321 extends Migration
public function moveComponentIdToBudgetId() public function moveComponentIdToBudgetId()
{ {
\Log::debug('Now in moveComponentIdToBudgetId()'); //Log::debug('Now in moveComponentIdToBudgetId()');
BudgetLimit::get()->each( BudgetLimit::get()->each(
function (BudgetLimit $bl) { 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); $component = Component::find($bl->component_id);
if ($component) { 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(); $budget = Budget::whereName($component->name)->whereUserId($component->user_id)->first();
if ($budget) { 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->budget_id = $budget->id;
$bl->save(); $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 { } 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 { } 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,12 +1,23 @@
<?php <?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/** /**
* Class ChangesForV332 * Class ChangesForV332
*/ */
class ChangesForV332 extends Migration { class ChangesForV332 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/** /**
* Run the migrations. * Run the migrations.
@@ -33,14 +44,4 @@ class ChangesForV332 extends Migration {
} }
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
} }

View File

@@ -1,9 +1,20 @@
<?php <?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class ChangesForV333 extends Migration { class ChangesForV333 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/** /**
* Run the migrations. * Run the migrations.
@@ -20,14 +31,4 @@ class ChangesForV333 extends Migration {
); );
} }
/**
* 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('TransactionCurrencySeeder');
$this->call('TransactionTypeSeeder'); $this->call('TransactionTypeSeeder');
if (App::environment() == 'testing' || App::environment() == 'homestead') { if (App::environment() == 'testing' || App::environment() == 'homestead' || gethostname() == 'vagrant-firefly-iii') {
$this->call('TestDataSeeder'); $this->call('TestDataSeeder');
} }
} }

View File

@@ -113,7 +113,9 @@ class TestDataSeeder extends Seeder
*/ */
public function createUsers() 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' => '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]); 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() public function createPiggyBanks()
{ {
// account // account
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first(); $savings = $this->findAccount('Savings account');
// some dates // some dates
$endDate = clone $this->_startOfMonth; $endDate = clone $this->_startOfMonth;
@@ -257,10 +260,6 @@ class TestDataSeeder extends Seeder
'targetamount' => 2000, 'targetamount' => 2000,
'startdate' => $this->som, 'startdate' => $this->som,
'targetdate' => null, 'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null, 'reminder' => null,
'reminder_skip' => 0, 'reminder_skip' => 0,
'remind_me' => 0, 'remind_me' => 0,
@@ -278,10 +277,6 @@ class TestDataSeeder extends Seeder
'targetamount' => 2000, 'targetamount' => 2000,
'startdate' => $this->som, 'startdate' => $this->som,
'targetdate' => $end, 'targetdate' => $end,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => null, 'reminder' => null,
'reminder_skip' => 0, 'reminder_skip' => 0,
'remind_me' => 0, 'remind_me' => 0,
@@ -307,10 +302,6 @@ class TestDataSeeder extends Seeder
'targetamount' => 1000, 'targetamount' => 1000,
'startdate' => $this->som, 'startdate' => $this->som,
'targetdate' => $nextYear, 'targetdate' => $nextYear,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => $entry, 'reminder' => $entry,
'reminder_skip' => 0, 'reminder_skip' => 0,
'remind_me' => 1, 'remind_me' => 1,
@@ -324,10 +315,6 @@ class TestDataSeeder extends Seeder
'targetamount' => 1000, 'targetamount' => 1000,
'startdate' => $this->som, 'startdate' => $this->som,
'targetdate' => null, 'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => $entry, 'reminder' => $entry,
'reminder_skip' => 0, 'reminder_skip' => 0,
'remind_me' => 1, '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) public function createMonthlyExpenses(Carbon $date)
{ {
// get some objects from the database: // get some objects from the database:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first(); $checking = $this->findAccount('Checking account');
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first(); $savings = $this->findAccount('Savings account');
$landLord = Account::whereName('Land lord')->orderBy('id', 'DESC')->first(); $landLord = $this->findAccount('Land lord');
$utilities = Account::whereName('Utilities company')->orderBy('id', 'DESC')->first(); $utilities = $this->findAccount('Utilities company');
$television = Account::whereName('TV company')->orderBy('id', 'DESC')->first(); $television = $this->findAccount('TV company');
$phone = Account::whereName('Phone agency')->orderBy('id', 'DESC')->first(); $phone = $this->findAccount('Phone agency');
$employer = Account::whereName('Employer')->orderBy('id', 'DESC')->first(); $employer = $this->findAccount('Employer');
$bills = Budget::whereName('Bills')->orderBy('id', 'DESC')->first(); $bills = $this->findBudget('Bills');
$house = Category::whereName('House')->orderBy('id', 'DESC')->first(); $house = $this->findCategory('House');
$withdrawal = TransactionType::whereType('Withdrawal')->first(); $withdrawal = TransactionType::whereType('Withdrawal')->first();
$deposit = TransactionType::whereType('Deposit')->first(); $deposit = TransactionType::whereType('Deposit')->first();
$transfer = TransactionType::whereType('Transfer')->first(); $transfer = TransactionType::whereType('Transfer')->first();
$euro = TransactionCurrency::whereCode('EUR')->first(); $euro = TransactionCurrency::whereCode('EUR')->first();
$rentBill = Bill::where('name', 'Rent')->first(); $rentBill = $this->findBill('Rent');
$cur = $date->format('Y-m-d'); $cur = $date->format('Y-m-d');
$formatted = $date->format('F Y'); $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 * @param Carbon $date
*/ */
public function createGroceries(Carbon $date) public function createGroceries(Carbon $date)
{ {
// variables we need: // variables we need:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first(); $checking = $this->findAccount('Checking account');
$shopOne = Account::whereName('Groceries House')->orderBy('id', 'DESC')->first(); $shopOne = $this->findAccount('Groceries House');
$shopTwo = Account::whereName('Super savers')->orderBy('id', 'DESC')->first(); $shopTwo = $this->findAccount('Super savers');
$lunchHouse = Account::whereName('Lunch House')->orderBy('id', 'DESC')->first(); $lunchHouse = $this->findAccount('Lunch House');
$lunch = Category::whereName('Lunch')->orderBy('id', 'DESC')->first(); $lunch = $this->findCategory('Lunch');
$daily = Category::whereName('DailyGroceries')->orderBy('id', 'DESC')->first(); $daily = $this->findCategory('DailyGroceries');
$euro = TransactionCurrency::whereCode('EUR')->first(); $euro = TransactionCurrency::whereCode('EUR')->first();
$withdrawal = TransactionType::whereType('Withdrawal')->first(); $withdrawal = TransactionType::whereType('Withdrawal')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first(); $groceries = $this->findBudget('Groceries');
$shops = [$shopOne, $shopTwo]; $shops = [$shopOne, $shopTwo];
@@ -534,9 +622,9 @@ class TestDataSeeder extends Seeder
{ {
$date->addDays(12); $date->addDays(12);
$dollar = TransactionCurrency::whereCode('USD')->first(); $dollar = TransactionCurrency::whereCode('USD')->first();
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first(); $checking = $this->findAccount('Checking account');
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first(); $savings = $this->findAccount('Savings account');
$buyMore = Account::whereName('Buy More')->orderBy('id', 'DESC')->first(); $buyMore = $this->findAccount('Buy More');
$withdrawal = TransactionType::whereType('Withdrawal')->first(); $withdrawal = TransactionType::whereType('Withdrawal')->first();
$user = User::whereEmail('thegrumpydictator@gmail.com')->first(); $user = User::whereEmail('thegrumpydictator@gmail.com')->first();
@@ -571,13 +659,13 @@ class TestDataSeeder extends Seeder
// piggy bank event // piggy bank event
// add money to this piggy bank // add money to this piggy bank
// create a piggy bank event to match: // create a piggy bank event to match:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first(); $checking = $this->findAccount('Checking account');
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first(); $savings = $this->findAccount('Savings account');
$transfer = TransactionType::whereType('Transfer')->first(); $transfer = TransactionType::whereType('Transfer')->first();
$euro = TransactionCurrency::whereCode('EUR')->first(); $euro = TransactionCurrency::whereCode('EUR')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first(); $groceries = $this->findBudget('Groceries');
$house = Category::whereName('House')->orderBy('id', 'DESC')->first(); $house = $this->findCategory('House');
$piggyBank = PiggyBank::whereName('New camera')->orderBy('id', 'DESC')->first(); $piggyBank = $this->findPiggyBank('New camera');
$intoPiggy = $this->createJournal( $intoPiggy = $this->createJournal(
['from' => $checking, 'to' => $savings, 'amount' => 100, 'transactionType' => $transfer, 'description' => 'Money for piggy', ['from' => $checking, 'to' => $savings, 'amount' => 100, 'transactionType' => $transfer, 'description' => 'Money for piggy',
'date' => $this->yaeom, 'transactionCurrency' => $euro, 'category' => $house, 'budget' => $groceries] 'date' => $this->yaeom, 'transactionCurrency' => $euro, 'category' => $house, 'budget' => $groceries]

13
pu.sh
View File

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

View File

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

View File

@@ -36,7 +36,8 @@
{!! ExpandedForm::balance('openingBalance') !!} {!! ExpandedForm::balance('openingBalance') !!}
{!! ExpandedForm::date('openingBalanceDate', date('Y-m-d')) !!} {!! 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>
</div> </div>

View File

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

View File

@@ -2,6 +2,9 @@
@section('content') @section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) !!} {!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) !!}
{!! Form::model($account, ['class' => 'form-horizontal','id' => 'update','url' => route('accounts.update',$account->id)]) !!} {!! 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="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary"> <div class="panel panel-primary">
@@ -28,12 +31,26 @@
{!! ExpandedForm::balance('openingBalance',null, ['currency' => $openingBalance ? $openingBalance->transactionCurrency : null]) !!} {!! ExpandedForm::balance('openingBalance',null, ['currency' => $openingBalance ? $openingBalance->transactionCurrency : null]) !!}
{!! ExpandedForm::date('openingBalanceDate') !!} {!! ExpandedForm::date('openingBalanceDate') !!}
{!! ExpandedForm::select('accountRole',Config::get('firefly.accountRoles')) !!} {!! ExpandedForm::select('accountRole',Config::get('firefly.accountRoles')) !!}
{!! ExpandedForm::balance('virtualBalance',null) !!}
{!! Form::hidden('id',$account->id) !!} {!! Form::hidden('id',$account->id) !!}
@endif @endif
{!! ExpandedForm::checkbox('active','1') !!} {!! ExpandedForm::checkbox('active','1') !!}
</div> </div>
</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 --> <!-- panel for options -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">

View File

@@ -10,9 +10,16 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<p> <p>
Are you sure? Are you sure that you want to delete bill "{{{$bill->name}}}"?
</p> </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> <p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button> <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > <a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >

View File

@@ -10,9 +10,16 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
<p> <p>
Are you sure? Are you sure that you want to delete budget "{{{$budget->name}}}"?
</p> </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> <p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button> <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > <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 <i class="fa fa-fw fa-exclamation"></i> Mandatory fields
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! ExpandedForm::checkbox('active') !!}
{!! ExpandedForm::text('name') !!} {!! ExpandedForm::text('name') !!}
</div> </div>
</div> </div>

View File

@@ -64,6 +64,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -144,6 +145,26 @@
</div> </div>
</div> </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 --> <!-- DIALOG -->
<div class="modal fade" id="monthlyBudgetModal"> <div class="modal fade" id="monthlyBudgetModal">

View File

@@ -6,6 +6,21 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
Overview 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>
<div class="panel-body"> <div class="panel-body">
<div id="budgetOverview"></div> <div id="budgetOverview"></div>
@@ -46,7 +61,8 @@
?> ?>
@if($overspent) @if($overspent)
<?php <?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 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> <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> </div>
@else @else
<?php <?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 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)}}%;"> <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>
<div class="panel-body"> <div class="panel-body">
<p> <p>
Are you sure? Are you sure that you want to delete category "{{$category->name}}"?
</p> </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> <p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button> <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a > <a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >

View File

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

View File

@@ -1,6 +1,6 @@
<html> <html>
<head> <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> <style>
body { body {

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<?php $r = Route::getCurrentRoute()->getName();?>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <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" src="js/daterangepicker.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var start = "{{Session::get('start')->format('d-m-Y')}}"; var start = "{{Session::get('start', new Carbon\Carbon)->format('d-m-Y')}}";
var end = "{{Session::get('end')->format('d-m-Y')}}"; var end = "{{Session::get('end', new Carbon\Carbon)->format('d-m-Y')}}";
var titleString = "{{Session::get('start')->format('j M Y')}} - {{Session::get('end')->format('j 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 dateRangeURL = "{{route('daterange')}}";
var token = "{{csrf_token()}}"; var token = "{{csrf_token()}}";
var firstDate = moment("{{Session::get('first')->format('Y-m-d')}}"); var firstDate = moment("{{Session::get('first', new Carbon\Carbon)->format('Y-m-d')}}");
var currentMonthName = "{{$currentMonthName}}"; var currentMonthName = "{{isset($currentMonthName) ? $currentMonthName : 'Month'}}";
var previousMonthName = "{{$previousMonthName}}"; var previousMonthName = "{{isset($previousMonthName) ? $previousMonthName : 'Month'}}";
var nextMonthName = "{{$nextMonthName}}"; var nextMonthName = "{{isset($nextMonthName) ? $nextMonthName : 'Month'}}";
$('#daterange span').text(titleString); $('#daterange span').text(titleString);
</script> </script>
<script type="text/javascript" src="js/firefly.js"></script> <script type="text/javascript" src="js/firefly.js"></script>
@yield('scripts') @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> </body>
</html> </html>

View File

@@ -9,6 +9,7 @@
<th>Is active</th> <th>Is active</th>
<th>Will be automatched</th> <th>Will be automatched</th>
<th>Repeats every</th> <th>Repeats every</th>
<th>&nbsp;</th>
</tr> </tr>
@foreach($bills as $entry) @foreach($bills as $entry)
<tr> <tr>
@@ -66,6 +67,11 @@
skips over {{$entry->skip}} skips over {{$entry->skip}}
@endif @endif
</td> </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> </tr>
@endforeach @endforeach

View File

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

View File

@@ -1,4 +1,6 @@
<!-- Navigation --> <!-- Navigation -->
<?php $r = Route::getCurrentRoute()->getName();?>
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0"> <nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <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"> <ul class="nav navbar-top-links navbar-right">
<!-- reminders --> <!-- reminders -->
@if($reminders->count() > 0) @if(isset($reminders) && $reminders->count() > 0)
<li class="dropdown"> <li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" aria-expanded="false"> <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> <i class="fa fa-envelope fa-fw"></i> <i class="fa fa-caret-down"></i>
@@ -147,10 +149,9 @@
</li> </li>
<?php <?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); $isPiggy = !(strpos($r,'piggy-banks') === false);
$isBill = !(strpos($r,'bills') === false) && strpos($r,'bills.create') === false; $isBill = !(strpos($r,'bills') === false) && strpos($r,'bills.create') === false;
$isRep = !(strpos($r,'repeated') === false);
?> ?>
<li @if($isMM)class="active"@endif> <li @if($isMM)class="active"@endif>
<a href="#"><i class="fa fa-euro fa-fw"></i> Money management<span class="fa arrow"></span></a> <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) @foreach($income as $entry)
<tr> <tr>
<td> <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> <a href="{{route('transactions.show',$entry->id)}}" title="{{{$entry->description}}}">{{{$entry->description}}}</a>
@endif
</td> </td>
<td> <td>
<?php $tableSum += floatval($entry->amount);?> <?php $tableSum += floatval($entry->amount);?>
@@ -67,7 +63,9 @@
<table class="table table-bordered"> <table class="table table-bordered">
<?php $sum = 0;?> <?php $sum = 0;?>
@foreach($expenses as $id => $expense) @foreach($expenses as $id => $expense)
<?php $sum += floatval($expense['amount']);?> <?php
$sum += floatval($expense['amount']);
?>
<tr> <tr>
@if($id > 0) @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>
@@ -233,7 +231,7 @@
</div> </div>
</div> </div>
<div class="row"> <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 panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-sort-amount-asc fa-fw"></i> <i class="fa fa-sort-amount-asc fa-fw"></i>
@@ -242,15 +240,6 @@
<div class="panel-body">Body</div> <div class="panel-body">Body</div>
</div> </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>
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">

View File

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

View File

@@ -1,5 +1,7 @@
<?php <?php
use League\FactoryMuffin\Facade as FactoryMuffin;
/** /**
* Class TestCase * Class TestCase
*/ */
@@ -20,4 +22,44 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
return $app; 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;
}
} }

View File

@@ -1,4 +1,8 @@
<?php <?php
use Carbon\Carbon;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use League\FactoryMuffin\Facade as FactoryMuffin;
/** /**
* Generated by PHPUnit_SkeletonGenerator on 2015-03-08 at 20:05:14. * Generated by PHPUnit_SkeletonGenerator on 2015-03-08 at 20:05:14.
@@ -13,11 +17,10 @@ class AccountControllerTest extends TestCase
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
$this->be(FireflyIII\User::whereEmail('thegrumpydictator@gmail.com')->first());
} }
/** /**
* Tears down the fixture, for example, closes a network connection. * Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed. * This method is called after a test is executed.
@@ -30,8 +33,22 @@ class AccountControllerTest extends TestCase
public function testCreate() public function testCreate()
{ {
$response = $this->call('GET', '/accounts/create/asset'); $pref = FactoryMuffin::create('FireflyIII\Models\Preference');
$pref->data = '1M';
$this->be($pref->user);
Preferences::shouldReceive('get', 'viewRange')->andReturn($pref);
// CURRENCY:
$currency = FactoryMuffin::create('FireflyIII\Models\TransactionCurrency');
Amount::shouldReceive('getDefaultCurrency')->once()->andReturn($currency);
Amount::shouldReceive('getAllCurrencies')->once()->andReturn([$currency]);
$this->call('GET', '/accounts/create/asset');
$this->assertResponseOk(); $this->assertResponseOk();
$this->assertViewHas('subTitle', 'Create a new asset account'); $this->assertViewHas('subTitle', 'Create a new asset account');
$this->assertViewHas('subTitleIcon', 'fa-money'); $this->assertViewHas('subTitleIcon', 'fa-money');
$this->assertViewHas('what', 'asset'); $this->assertViewHas('what', 'asset');
@@ -40,22 +57,86 @@ class AccountControllerTest extends TestCase
public function testDelete() public function testDelete()
{ {
$this->markTestIncomplete(); // fake an account.
$account = FactoryMuffin::create('FireflyIII\Models\Account');
$account->accountType->type = 'Asset account';
$account->accountType->save();
$this->be($account->user);
$this->call('GET', '/accounts/delete/' . $account->id);
$this->assertResponseOk();
$this->assertViewHas('subTitle', 'Delete ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"');
} }
public function testDestroy() public function testDestroy()
{ {
$this->markTestIncomplete(); // fake an account.
$account = FactoryMuffin::create('FireflyIII\Models\Account');
$account->accountType->type = 'Asset account';
$account->accountType->save();
$this->be($account->user);
$this->assertCount(1, DB::table('accounts')->where('id', $account->id)->whereNull('deleted_at')->get());
// post it!
$this->call('POST', '/accounts/destroy/' . $account->id, ['_token' => 'replaceme']);
$this->assertSessionHas('success');
$this->assertCount(0, DB::table('accounts')->where('id', $account->id)->whereNull('deleted_at')->get());
} }
public function testEdit() public function testEdit()
{ {
$this->markTestIncomplete(); // fake an account.
$account = FactoryMuffin::create('FireflyIII\Models\Account');
$account->accountType->type = 'Asset account';
$account->accountType->save();
$this->be($account->user);
$this->assertCount(1, DB::table('accounts')->where('id', $account->id)->whereNull('deleted_at')->get());
// create a transaction journal that will act as opening balance:
$openingBalance = FactoryMuffin::create('FireflyIII\Models\TransactionJournal');
$repository = $this->mock('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$repository->shouldReceive('openingBalanceTransaction')->andReturn($openingBalance);
// create a transaction that will be returned for the opening balance transaction:
$openingBalanceTransaction = FactoryMuffin::create('FireflyIII\Models\Transaction');
$repository->shouldReceive('getFirstTransaction')->andReturn($openingBalanceTransaction);
// CURRENCY:
$currency = FactoryMuffin::create('FireflyIII\Models\TransactionCurrency');
Amount::shouldReceive('getDefaultCurrency')->once()->andReturn($currency);
Amount::shouldReceive('getAllCurrencies')->once()->andReturn([$currency]);
// get edit page:
$this->call('GET', '/accounts/edit/' . $account->id);
// assert stuff:
$this->assertResponseOk();
$this->assertSessionHas('preFilled');
$this->assertViewHas('subTitle', 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"');
} }
public function testIndex() public function testIndex()
{ {
$this->markTestIncomplete(); $user = FactoryMuffin::create('FireflyIII\User');
$this->be($user);
// get currency code:
$currency = FactoryMuffin::create('FireflyIII\Models\TransactionCurrency');
Amount::shouldReceive('getCurrencyCode')->andReturn($currency->code);
// put stuff in session:
$this->session(['start' => new Carbon, 'end' => new Carbon]);
// get edit page:
$this->call('GET', '/accounts/asset');
$this->assertResponseOk();
$this->assertViewHas('what', 'asset');
} }
public function testShow() public function testShow()

View File

@@ -13,8 +13,7 @@ class HomeControllerTest extends TestCase
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
Artisan::call('migrate');
} }
/** /**
@@ -33,8 +32,7 @@ class HomeControllerTest extends TestCase
{ {
$start = '2014-03-01'; $start = '2014-03-01';
$end = '2015-03-31'; $end = '2015-03-31';
$this->be(new FireflyIII\User);
$this->be(FireflyIII\User::whereEmail('thegrumpydictator@gmail.com')->first());
$this->call('POST', '/daterange', ['end' => $end, 'start' => $start,'_token' => 'replaceme']); $this->call('POST', '/daterange', ['end' => $end, 'start' => $start,'_token' => 'replaceme']);
$this->assertResponseOk(); $this->assertResponseOk();
@@ -51,8 +49,9 @@ class HomeControllerTest extends TestCase
{ {
$start = '2015-03-01'; $start = '2015-03-01';
$end = '2015-03-31'; $end = '2015-03-31';
$this->be(new FireflyIII\User);
$this->be(FireflyIII\User::whereEmail('thegrumpydictator@gmail.com')->first());
$this->call('POST', '/daterange', ['end' => $end, 'start' => $start,'_token' => 'replaceme']); $this->call('POST', '/daterange', ['end' => $end, 'start' => $start,'_token' => 'replaceme']);
$this->assertResponseOk(); $this->assertResponseOk();
@@ -66,7 +65,9 @@ class HomeControllerTest extends TestCase
*/ */
public function testIndexLoggedIn() public function testIndexLoggedIn()
{ {
$this->be(FireflyIII\User::whereEmail('thegrumpydictator@gmail.com')->first()); $this->be(new FireflyIII\User);
Amount::shouldReceive('getCurrencyCode')->andReturn('EUR');
$response = $this->call('GET', '/'); $response = $this->call('GET', '/');
$this->assertResponseOk(); $this->assertResponseOk();

100
tests/factories/all.php Normal file
View File

@@ -0,0 +1,100 @@
<?php
use League\FactoryMuffin\Facade as FactoryMuffin;
if (!class_exists('RandomString')) {
/**
* Class RandomString
*/
class RandomString
{
/**
* @param int $length
*
* @return string
*/
public static function generateRandomString($length = 10)
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
}
}
FactoryMuffin::define(
'FireflyIII\Models\Account', [
'user_id' => 'factory|FireflyIII\User',
'account_type_id' => 'factory|FireflyIII\Models\AccountType',
'name' => 'word',
'active' => 'boolean',
'encrypted' => 'boolean',
'virtual_balance' => 0
]
);
FactoryMuffin::define(
'FireflyIII\Models\Preference', [
'name' => 'word',
'data' => 'sentence',
'user_id' => 'factory|FireflyIII\User',
]
);
FactoryMuffin::define(
'FireflyIII\Models\AccountType', [
'type' => 'word',
'editable' => 1,
]
);
FactoryMuffin::define(
'FireflyIII\Models\TransactionCurrency', [
'code' => function () {
return RandomString::generateRandomString(3);
},
'symbol' => function () {
return RandomString::generateRandomString(1);
},
'name' => 'word'
]
);
FactoryMuffin::define(
'FireflyIII\User', [
'email' => 'email',
'password' => bcrypt('james'),
]
);
FactoryMuffin::define(
'FireflyIII\Models\Transaction', [
'transaction_journal_id' => 'factory|FireflyIII\Models\TransactionJournal',
'amount' => 'integer',
'account_id' => 'factory|FireflyIII\Models\Account'
]
);
FactoryMuffin::define(
'FireflyIII\Models\TransactionType', [
'type' => 'word',
]
);
FactoryMuffin::define(
'FireflyIII\Models\TransactionJournal', [
'user_id' => 'factory|FireflyIII\User',
'transaction_type_id' => 'factory|FireflyIII\Models\TransactionType',
'transaction_currency_id' => 'factory|FireflyIII\Models\TransactionCurrency',
'description' => 'sentence',
'completed' => '1',
'date' => 'date',
'encrypted' => '1',
'order' => '0',
]
);