Merge branch 'release/3.3.3'

This commit is contained in:
James Cole
2015-03-27 09:36:03 +01:00
84 changed files with 1149 additions and 420 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ db.sqlite-journal
tests/_output/* tests/_output/*
.env .env
clover.xml clover.xml
node_modules/

View File

@@ -1,4 +1,6 @@
language: php language: php
sudo: false
php: php:
- 5.5 - 5.5
@@ -13,9 +15,11 @@ install:
- composer install - 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 - phpunit --debug
after_script: after_script:
- php vendor/bin/coveralls - php vendor/bin/coveralls

View File

@@ -1,4 +1,4 @@
Firefly III (v3.3.2) Firefly III (v3.3.3)
=========== ===========
[![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)

View File

@@ -5,6 +5,11 @@ use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/**
* Class JournalCreated
*
* @package FireflyIII\Events
*/
class JournalCreated extends Event { class JournalCreated extends Event {
use SerializesModels; use SerializesModels;
@@ -23,9 +28,6 @@ class JournalCreated extends Event {
$this->journal = $journal; $this->journal = $journal;
$this->piggyBankId = $piggyBankId; $this->piggyBankId = $piggyBankId;
} }
} }

View File

@@ -38,6 +38,9 @@ class ConnectJournalToPiggyBank
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
$journal = $event->journal; $journal = $event->journal;
$piggyBankId = $event->piggyBankId; $piggyBankId = $event->piggyBankId;
if(intval($piggyBankId) < 1) {
return;
}
Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId); Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId);
@@ -63,6 +66,7 @@ class ConnectJournalToPiggyBank
// update piggy bank rep for date of transaction journal. // update piggy bank rep for date of transaction journal.
$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'));
return; return;
} }

View File

@@ -25,10 +25,7 @@ class ReminderHelper implements ReminderHelperInterface
*/ */
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end) public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end)
{ {
$reminder = Auth::user()->reminders() $reminder = Auth::user()->reminders()->where('remindersable_id', $piggyBank->id)->onDates($start, $end)->first();
->where('remindersable_id', $piggyBank->id)
->onDates($start, $end)
->first();
if (is_null($reminder)) { if (is_null($reminder)) {
if (!is_null($piggyBank->targetdate)) { if (!is_null($piggyBank->targetdate)) {
@@ -134,6 +131,7 @@ class ReminderHelper implements ReminderHelperInterface
{ {
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
$piggyBank = $reminder->remindersable; $piggyBank = $reminder->remindersable;
if (is_null($piggyBank)) { if (is_null($piggyBank)) {
return 'Piggy bank no longer exists.'; return 'Piggy bank no longer exists.';
} }

View File

@@ -87,6 +87,9 @@ class ReportQuery implements ReportQueryInterface
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at') ->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at') ->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id) ->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id') ->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->get( ->get(
[ [

View File

@@ -168,7 +168,7 @@ class AccountController extends Controller
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); $what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$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);
return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle')); return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle'));
} }
@@ -196,7 +196,7 @@ class AccountController extends Controller
Session::flash('success', 'New account "' . $account->name . '" stored!'); Session::flash('success', 'New account "' . $account->name . '" stored!');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
return Redirect::route('accounts.create', $request->input('what')); return Redirect::route('accounts.create', $request->input('what'))->withInput();
} }
return Redirect::route('accounts.index', $request->input('what')); return Redirect::route('accounts.index', $request->input('what'));

View File

@@ -63,7 +63,10 @@ class AuthController extends Controller
); );
} }
$this->auth->login($this->registrar->create($request->all())); $data =$request->all();
$data['password'] = bcrypt($data['password']);
$this->auth->login($this->registrar->create($data));
// get the email address // get the email address
$email = $this->auth->user()->email; $email = $this->auth->user()->email;

View File

@@ -138,7 +138,12 @@ class BillController extends Controller
*/ */
public function show(Bill $bill, BillRepositoryInterface $repository) public function show(Bill $bill, BillRepositoryInterface $repository)
{ {
$journals = $bill->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get(); $journals = $bill->transactionjournals()->withRelevantData()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get();
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$hideBill = true; $hideBill = true;
@@ -210,42 +215,6 @@ class BillController extends Controller
return Redirect::route('bills.index'); return Redirect::route('bills.index');
// $data = Input::except('_token');
// $data['active'] = intval(Input::get('active'));
// $data['automatch'] = intval(Input::get('automatch'));
// $data['user_id'] = Auth::user()->id;
//
// // always validate:
// $messages = $this->_repository->validate($data);
//
// // flash messages:
// Session::flash('warnings', $messages['warnings']);
// Session::flash('successes', $messages['successes']);
// Session::flash('errors', $messages['errors']);
// if ($messages['errors']->count() > 0) {
// Session::flash('error', 'Could not update bill: ' . $messages['errors']->first());
//
// return Redirect::route('bills.edit', $bill->id)->withInput();
// }
//
// // return to update screen:
// if ($data['post_submit_action'] == 'validate_only') {
// return Redirect::route('bills.edit', $bill->id)->withInput();
// }
//
// // update
// $this->_repository->update($bill, $data);
// Session::flash('success', 'Bill "' . e($data['name']) . '" updated.');
//
// // go back to list
// if ($data['post_submit_action'] == 'update') {
// return Redirect::route('bills.index');
// }
//
// // go back to update screen.
// return Redirect::route('bills.edit', $bill->id)->withInput(['post_submit_action' => 'return_to_edit']);
} }
} }

View File

@@ -133,7 +133,9 @@ class BudgetController extends Controller
->whereNull('budget_transaction_journal.id') ->whereNull('budget_transaction_journal.id')
->before($end) ->before($end)
->after($start) ->after($start)
->orderBy('transaction_journals.date') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get(['transaction_journals.*']); ->get(['transaction_journals.*']);
$subTitle = 'Transactions without a budget in ' . $start->format('F Y'); $subTitle = 'Transactions without a budget in ' . $start->format('F Y');
@@ -152,6 +154,12 @@ class BudgetController extends Controller
return Redirect::route('budgets.index'); return Redirect::route('budgets.index');
} }
/**
* @param BudgetFormRequest $request
* @param BudgetRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository) public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{ {
$budgetData = [ $budgetData = [
@@ -204,6 +212,10 @@ 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) {
return Redirect::route('budgets.edit', $budget->id);
}
return Redirect::route('budgets.index'); return Redirect::route('budgets.index');
} }

View File

@@ -88,7 +88,11 @@ class CategoryController extends Controller
$categories->each( $categories->each(
function (Category $category) { function (Category $category) {
$latest = $category->transactionjournals()->orderBy('date', 'DESC')->first(); $latest = $category->transactionjournals()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->first();
if ($latest) { if ($latest) {
$category->lastActivity = $latest->date; $category->lastActivity = $latest->date;
} }
@@ -111,7 +115,9 @@ class CategoryController extends Controller
->whereNull('category_transaction_journal.id') ->whereNull('category_transaction_journal.id')
->before($end) ->before($end)
->after($start) ->after($start)
->orderBy('transaction_journals.date') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get(['transaction_journals.*']); ->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y'); $subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
@@ -129,7 +135,13 @@ class CategoryController extends Controller
$hideCategory = true; // used in list. $hideCategory = true; // used in list.
$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)->orderBy('date', 'DESC')->get( $set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get(
['transaction_journals.*'] ['transaction_journals.*']
); );
$count = $category->transactionJournals()->count(); $count = $category->transactionJournals()->count();
@@ -155,6 +167,10 @@ 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) {
return Redirect::route('categories.create')->withInput();
}
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
return Redirect::route('categories.create'); return Redirect::route('categories.create');
} }
@@ -181,6 +197,10 @@ 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) {
return Redirect::route('categories.edit', $category->id);
}
return Redirect::route('categories.index'); return Redirect::route('categories.index');
} }

View File

@@ -148,7 +148,7 @@ 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) {
return Redirect::route('currency.create'); return Redirect::route('currency.create')->withInput();
} }
return Redirect::route('currency.index'); return Redirect::route('currency.index');

View File

@@ -235,7 +235,9 @@ class GoogleChartController extends Controller
// query! // query!
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$set = TransactionJournal::leftJoin( $set = TransactionJournal::
where('transaction_journals.user_id',Auth::user()->id)
->leftJoin(
'transactions', 'transactions',
function (JoinClause $join) { function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0); $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
@@ -247,6 +249,7 @@ class GoogleChartController extends Controller
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id') ->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end) ->before($end)
->where('categories.user_id',Auth::user()->id)
->after($start) ->after($start)
->where('transaction_types.type', 'Withdrawal') ->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id') ->groupBy('categories.id')
@@ -503,7 +506,7 @@ class GoogleChartController extends Controller
{ {
// oldest transaction in category: // oldest transaction in category:
/** @var TransactionJournal $first */ /** @var TransactionJournal $first */
$start = Session::get('start'); $start = clone Session::get('start');
$chart->addColumn('Period', 'date'); $chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number'); $chart->addColumn('Spent', 'number');
@@ -532,10 +535,13 @@ class GoogleChartController extends Controller
$chart->addColumn('Date', 'date'); $chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number'); $chart->addColumn('Balance', 'number');
$set = \DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]); /** @var Collection $set */
$set = DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
$sum = 0;
foreach ($set as $entry) { foreach ($set as $entry) {
$chart->addRow(new Carbon($entry->date), floatval($entry->sum)); $sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
} }
$chart->generate(); $chart->generate();

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Cache; use Cache;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -64,6 +63,7 @@ class HomeController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage); $accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts();
foreach ($accounts as $account) { foreach ($accounts as $account) {
$set = $repository->getFrontpageTransactions($account, $start, $end); $set = $repository->getFrontpageTransactions($account, $start, $end);
@@ -74,7 +74,7 @@ class HomeController extends Controller
// var_dump($transactions); // var_dump($transactions);
return view('index', compact('count', 'title', 'subTitle', 'mainTitleIcon', 'transactions')); return view('index', compact('count', 'title','savings', 'subTitle', 'mainTitleIcon', 'transactions'));
} }

View File

@@ -136,14 +136,13 @@ class PiggyBankController extends Controller
return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled')); return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled'));
} }
/** /**
* @return $this * @return $this
*/ */
public function index(AccountRepositoryInterface $repository) public function index(AccountRepositoryInterface $repository)
{ {
/** @var Collection $piggyBanks */ /** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->get(); $piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->orderBy('order', 'ASC')->get();
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
@@ -175,6 +174,22 @@ class PiggyBankController extends Controller
return view('piggy-banks.index', compact('piggyBanks', 'accounts')); return view('piggy-banks.index', compact('piggyBanks', 'accounts'));
} }
/**
* Allow user to order piggy banks.
*/
public function order(PiggyBankRepositoryInterface $repository)
{
$data = Input::get('order');
// set all users piggy banks to zero:
$repository->reset();
if (is_array($data)) {
foreach ($data as $order => $id) {
$repository->setOrder(intval($id), (intval($order) + 1));
}
}
}
/** /**
* POST add money to piggy bank * POST add money to piggy bank
@@ -276,7 +291,10 @@ class PiggyBankController extends Controller
} }
/** /**
* @param PiggyBankFormRequest $request
* @param PiggyBankRepositoryInterface $repository
* *
* @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository) public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{ {

View File

@@ -12,6 +12,7 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Redirect; use Redirect;
use Session; use Session;
use View; use View;
use Input;
/** /**
* Class RepeatedExpenseController * Class RepeatedExpenseController
@@ -144,7 +145,10 @@ class RepeatedExpenseController extends Controller
} }
/** /**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind. * @param PiggyBankFormRequest $request
* @param PiggyBankRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository) public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{ {
@@ -159,6 +163,7 @@ class RepeatedExpenseController extends Controller
'reminder' => $request->get('reminder'), 'reminder' => $request->get('reminder'),
'skip' => intval($request->get('skip')), 'skip' => intval($request->get('skip')),
'rep_every' => intval($request->get('rep_every')), 'rep_every' => intval($request->get('rep_every')),
'rep_length' => $request->get('rep_length'),
'rep_times' => intval($request->get('rep_times')), 'rep_times' => intval($request->get('rep_times')),
]; ];
@@ -166,6 +171,11 @@ class RepeatedExpenseController extends Controller
Session::flash('success', 'Stored repeated expense "' . e($piggyBank->name) . '".'); Session::flash('success', 'Stored repeated expense "' . e($piggyBank->name) . '".');
if (intval(Input::get('create_another')) === 1) {
return Redirect::route('repeated.create', $request->input('what'))->withInput();
}
return Redirect::route('repeated.index'); return Redirect::route('repeated.index');
} }
@@ -194,6 +204,10 @@ class RepeatedExpenseController extends Controller
$piggyBank = $repository->update($repeatedExpense, $piggyBankData); $piggyBank = $repository->update($repeatedExpense, $piggyBankData);
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('repeated.edit', $piggyBank->id);
}
Session::flash('success', 'Updated repeated expense "' . e($piggyBank->name) . '".'); Session::flash('success', 'Updated repeated expense "' . e($piggyBank->name) . '".');
return Redirect::route('repeated.index'); return Redirect::route('repeated.index');

View File

@@ -15,7 +15,7 @@ use Input;
use Redirect; use Redirect;
use Session; use Session;
use View; use View;
use Response;
/** /**
* Class TransactionController * Class TransactionController
@@ -180,21 +180,18 @@ class TransactionController extends Controller
case 'withdrawal': case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left'; $subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses'; $subTitle = 'Expenses';
//$journals = $this->_repository->getWithdrawalsPaginated(50);
$types = ['Withdrawal']; $types = ['Withdrawal'];
break; break;
case 'revenue': case 'revenue':
case 'deposit': case 'deposit':
$subTitleIcon = 'fa-long-arrow-right'; $subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits'; $subTitle = 'Revenue, income and deposits';
// $journals = $this->_repository->getDepositsPaginated(50);
$types = ['Deposit']; $types = ['Deposit'];
break; break;
case 'transfer': case 'transfer':
case 'transfers': case 'transfers':
$subTitleIcon = 'fa-arrows-h'; $subTitleIcon = 'fa-exchange';
$subTitle = 'Transfers'; $subTitle = 'Transfers';
//$journals = $this->_repository->getTransfersPaginated(50);
$types = ['Transfer']; $types = ['Transfer'];
break; break;
} }
@@ -202,7 +199,14 @@ 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()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get( $set = Auth::user()->
transactionJournals()->
transactionTypes($types)->
withRelevantData()->take(50)->offset($offset)
->orderBy('date', 'DESC')
->orderBy('order','ASC')
->orderBy('id','DESC')
->get(
['transaction_journals.*'] ['transaction_journals.*']
); );
$count = Auth::user()->transactionJournals()->transactionTypes($types)->count(); $count = Auth::user()->transactionJournals()->transactionTypes($types)->count();
@@ -213,6 +217,27 @@ class TransactionController extends Controller
} }
/**
* Reorder transactions (which all must have the same date)
*/
public function reorder()
{
$ids = Input::get('items');
if (count($ids) > 0) {
$order = 0;
foreach ($ids as $id) {
$journal = Auth::user()->transactionjournals()->where('id', $id)->where('date', Input::get('date'))->first();
if($journal) {
$journal->order = $order;
$order++;
$journal->save();
}
}
}
return Response::json(true);
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* *
@@ -225,9 +250,11 @@ class TransactionController extends Controller
$t->before = floatval( $t->before = floatval(
$t->account->transactions()->leftJoin( $t->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))->where( )
'transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s') ->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
)->where('transaction_journals.id', '!=', $journal->id)->sum('transactions.amount') ->where('transaction_journals.order','>=',$journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->sum('transactions.amount')
); );
$t->after = $t->before + $t->amount; $t->after = $t->before + $t->amount;
} }
@@ -239,6 +266,12 @@ class TransactionController extends Controller
); );
} }
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{ {
$journalData = [ $journalData = [
@@ -284,7 +317,6 @@ class TransactionController extends Controller
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind. * @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
* *
* @return $this * @return $this
* @throws FireflyException
*/ */
public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository) public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository)
{ {
@@ -317,6 +349,7 @@ class TransactionController extends Controller
return Redirect::route('transactions.edit', $journal->id); return Redirect::route('transactions.edit', $journal->id);
} }
return Redirect::route('transactions.index', $journalData['what']); return Redirect::route('transactions.index', $journalData['what']);
} }

View File

@@ -37,6 +37,7 @@ class Kernel extends HttpKernel
'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated', 'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated',
'range' => 'FireflyIII\Http\Middleware\Range', 'range' => 'FireflyIII\Http\Middleware\Range',
'reminders' => 'FireflyIII\Http\Middleware\Reminders', 'reminders' => 'FireflyIII\Http\Middleware\Reminders',
'piggybanks' => 'FireflyIII\Http\Middleware\PiggyBanks',
]; ];
} }

View File

@@ -0,0 +1,141 @@
<?php
namespace FireflyIII\Http\Middleware;
use Carbon\Carbon;
use Closure;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Navigation;
use Session;
/**
* Class PiggyBanks
*
* @package FireflyIII\Http\Middleware
*/
class PiggyBanks
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
*
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// get piggy banks without a repetition:
/** @var Collection $set */
$set = $this->auth->user()->piggybanks()
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->where('piggy_banks.repeats', 0)
->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) {
/** @var PiggyBank $partialPiggy */
foreach ($set as $partialPiggy) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($partialPiggy);
$repetition->startdate = is_null($partialPiggy->startdate) ? null : $partialPiggy->startdate;
$repetition->targetdate = is_null($partialPiggy->targetdate) ? null : $partialPiggy->targetdate;
$repetition->currentamount = 0;
$repetition->save();
}
}
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;
$index = 0;
// 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:
//$repetition->save();
// then, loop from original target up to now.
}
}
}
return $next($request);
}
}

View File

@@ -45,7 +45,7 @@ class Reminders
*/ */
public function handle($request, Closure $next) public function handle($request, Closure $next)
{ {
if ($this->auth->check()) { if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// 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;
@@ -67,13 +67,12 @@ class Reminders
} }
} }
// delete invalid reminders // delete invalid reminders
$reminders = $this->auth->user()->reminders()->get(); $set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get(
foreach($reminders as $reminder) { ['reminders.id']
if(is_null($reminder->remindersable)) { );
foreach ($set as $reminder) {
$reminder->delete(); $reminder->delete();
} }
}
// get and list active reminders: // get and list active reminders:

View File

@@ -31,9 +31,9 @@ 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')));
$nameRule = 'required|between:1,100|uniqueForUser:accounts,name'; $nameRule = 'required|between:1,100|uniqueAccountForUser';
if (Account::find(Input::get('id'))) { if (Account::find(Input::get('id'))) {
$nameRule = 'required|between:1,100'; $nameRule = 'required|between:1,100|belongsToUser:accounts|uniqueForUser:'.Input::get('id');
} }
return [ return [

View File

@@ -3,8 +3,10 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use Input; use Input;
use Navigation;
/** /**
* Class PiggyBankFormRequest * Class PiggyBankFormRequest
@@ -36,7 +38,13 @@ class PiggyBankFormRequest extends Request
if (intval(Input::get('repeats')) == 1) { if (intval(Input::get('repeats')) == 1) {
$targetDateRule = 'required|date|after:' . date('Y-m-d'); $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', 'repeats' => 'required|boolean',

View File

@@ -156,7 +156,7 @@ Route::controllers(
* Home Controller * Home Controller
*/ */
Route::group( Route::group(
['middleware' => ['auth', 'range', 'reminders']], function () { ['middleware' => ['auth', 'range', 'reminders','piggybanks']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']); Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']); Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']); Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
@@ -279,6 +279,7 @@ Route::group(
Route::post('/piggy-banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy-banks.destroy']); Route::post('/piggy-banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy-banks.destroy']);
Route::post('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy-banks.add']); # add money Route::post('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy-banks.add']); # add money
Route::post('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy-banks.remove']); # remove money. Route::post('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy-banks.remove']); # remove money.
Route::post('/piggy-banks/sort', ['uses' => 'PiggyBankController@order', 'as' => 'piggy-banks.order']);
/** /**
* Preferences Controller * Preferences Controller
@@ -365,6 +366,7 @@ Route::group(
); );
Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']); Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']);
Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']); Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']);
Route::post('/transaction/reorder', ['uses' => 'TransactionController@reorder', 'as' => 'transactions.reorder']);
/** /**
* Auth\Auth Controller * Auth\Auth Controller

View File

@@ -18,7 +18,7 @@ class Account extends Model
= [ = [
'user_id' => 'required|exists:users,id', 'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id', 'account_type_id' => 'required|exists:account_types,id',
'name' => 'required|between:1,1024|uniqueForUser:accounts,name', 'name' => 'required|between:1,1024|uniqueAccountForUser',
'active' => 'required|boolean' 'active' => 'required|boolean'
]; ];

View File

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

View File

@@ -14,7 +14,9 @@ class PiggyBank extends Model
{ {
use SoftDeletes; use SoftDeletes;
protected $fillable = ['repeats', 'name', 'account_id','rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder','remind_me']; protected $fillable
= ['repeats', 'name', 'account_id', 'rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me',
'rep_length'];
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
@@ -24,15 +26,6 @@ class PiggyBank extends Model
return $this->belongsTo('FireflyIII\Models\Account'); return $this->belongsTo('FireflyIII\Models\Account');
} }
/**
* @param $value
*
* @return int
*/
public function getRemindMeAttribute($value) {
return intval($value) == 1;
}
/** /**
* Grabs the PiggyBankRepetition that's currently relevant / active * Grabs the PiggyBankRepetition that's currently relevant / active
* *
@@ -40,10 +33,10 @@ class PiggyBank extends Model
*/ */
public function currentRelevantRep() public function currentRelevantRep()
{ {
if ($this->currentRep) { if (!is_null($this->currentRep)) {
return $this->currentRep; return $this->currentRep;
} }
if ($this->repeats == 0) { 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;
@@ -104,6 +97,16 @@ class PiggyBank extends Model
return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate']; return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate'];
} }
/**
* @param $value
*
* @return int
*/
public function getRemindMeAttribute($value)
{
return intval($value) == 1;
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -37,15 +37,27 @@ class PiggyBankRepetition extends Model
{ {
return $query->where( return $query->where(
function($q) use ($date) { function($q) use ($date) {
$q->where('startdate', '>=', $date->format('Y-m-d 00:00:00')); $q->where('startdate', '<=', $date->format('Y-m-d 00:00:00'));
$q->orWhereNull('startdate'); $q->orWhereNull('startdate');
}) })
->where(function($q) use ($date) { ->where(function($q) use ($date) {
$q->where('targetdate', '<=', $date->format('Y-m-d 00:00:00')); $q->where('targetdate', '>=', $date->format('Y-m-d 00:00:00'));
$q->orWhereNull('targetdate'); $q->orWhereNull('targetdate');
}); });
} }
/**
* @param EloquentBuilder $query
* @param Carbon $start
* @param Carbon $target
*
* @return $this
*/
public function scopeOnDates(EloquentBuilder $query, Carbon $start, Carbon $target)
{
return $query->where('startdate',$start->format('Y-m-d'))->where('targetdate',$target->format('Y-m-d'));
}
} }

View File

@@ -74,6 +74,8 @@ class EventServiceProvider extends ServiceProvider
} }
); );
// move this routine to a filter
// in case of repeated piggy banks and/or other problems.
PiggyBank::created( PiggyBank::created(
function (PiggyBank $piggyBank) { function (PiggyBank $piggyBank) {
$repetition = new PiggyBankRepetition; $repetition = new PiggyBankRepetition;

View File

@@ -18,6 +18,7 @@ use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Session; use Session;
use Steam;
/** /**
* Class AccountRepository * Class AccountRepository
@@ -64,6 +65,10 @@ class AccountRepository implements AccountRepositoryInterface
} }
/** /**
* This method is used on the front page where (in turn) its viewed journals-tiny.php which (in turn)
* is almost the only place where formatJournal is used. Aka, we can use some custom querying to get some specific.
* fields using left joins.
*
* @param Account $account * @param Account $account
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@@ -74,15 +79,17 @@ class AccountRepository implements AccountRepositoryInterface
{ {
return Auth::user() return Auth::user()
->transactionjournals() ->transactionjournals()
->with(['transactions', 'transactioncurrency', 'transactiontype']) ->with(['transactions'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id) ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id)
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transaction_journals.transaction_currency_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end) ->before($end)
->after($start) ->after($start)
->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.id', 'DESC')
->take(10) ->take(10)
->get(['transaction_journals.*']); ->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']);
} }
/** /**
@@ -99,7 +106,9 @@ class AccountRepository implements AccountRepositoryInterface
->withRelevantData() ->withRelevantData()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id) ->where('transactions.account_id', $account->id)
->orderBy('date', 'DESC'); ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC');
$query->before(Session::get('end', Carbon::now()->endOfMonth())); $query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth())); $query->after(Session::get('start', Carbon::now()->startOfMonth()));
@@ -114,6 +123,48 @@ class AccountRepository implements AccountRepositoryInterface
} }
/**
* Get savings accounts and the balance difference in the period.
*
* @return Collection
*/
public function getSavingsAccounts()
{
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', 'accountRole')
->where('account_meta.data', '"savingAsset"')
->get(['accounts.*']);
$start = clone Session::get('start');
$end = clone Session::get('end');
$accounts->each(
function (Account $account) use ($start, $end) {
$account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, $end);
// diff (negative when lost, positive when gained)
$diff = $account->endBalance - $account->startBalance;
if ($diff < 0 && $account->startBalance > 0) {
// percentage lost compared to start.
$pct = (($diff * -1) / $account->startBalance) * 100;
} else {
if ($diff >= 0 && $account->startBalance > 0) {
$pct = ($diff / $account->startBalance) * 100;
} else {
$pct = 100;
}
}
$pct = $pct > 100 ? 100 : $pct;
$account->difference = $diff;
$account->percentage = round($pct);
}
);
return $accounts;
}
/** /**
* @param Account $account * @param Account $account
* *
@@ -236,12 +287,14 @@ class AccountRepository implements AccountRepositoryInterface
'active' => $data['active'] === true ? true : false, 'active' => $data['active'] === true ? true : false,
] ]
); );
if (!$newAccount->isValid()) { if (!$newAccount->isValid()) {
// does the account already exist? // does the account already exist?
$existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first(); $existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first();
if (!$existingAccount) { if (!$existingAccount) {
Log::error('Account create error: ' . $newAccount->getErrors()->toJson()); Log::error('Account create error: ' . $newAccount->getErrors()->toJson());
var_dump($newAccount->getErrors()->toArray()); App::abort(500);
} }
$newAccount = $existingAccount; $newAccount = $existingAccount;
} }

View File

@@ -78,4 +78,11 @@ interface AccountRepositoryInterface
* @return float * @return float
*/ */
public function leftOnAccount(Account $account); public function leftOnAccount(Account $account);
/**
* Get savings accounts and the balance difference in the period.
*
* @return Collection
*/
public function getSavingsAccounts();
} }

View File

@@ -42,7 +42,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $take : 0; $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $take : 0;
$setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)->orderBy('date', 'DESC'); $setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC');
$countQuery = $budget->transactionJournals(); $countQuery = $budget->transactionJournals();

View File

@@ -11,6 +11,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

View File

@@ -2,9 +2,8 @@
namespace FireflyIII\Repositories\PiggyBank; namespace FireflyIII\Repositories\PiggyBank;
use Amount;
use Auth; use Auth;
use Carbon\Carbon; use DB;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
@@ -88,6 +87,39 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $part; return $part;
} }
/**
* Set all piggy banks to order 0.
*
* @return void
*/
public function reset()
{
DB::table('piggy_banks')
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id')
->where('accounts.user_id', Auth::user()->id)
->update(['order' => 0, 'piggy_banks.updated_at' => DB::Raw('NOW()')]);
//Auth::user()->piggyBanks()->update(['order' => 0]);
}
/**
*
* set id of piggy bank.
*
* @param int $id
* @param int $order
*
* @return void
*/
public function setOrder($id, $order)
{
$piggyBank = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', Auth::user()->id)
->where('piggy_banks.id',$id)->first(['piggy_banks.*']);
if ($piggyBank) {
$piggyBank->order = $order;
$piggyBank->save();
}
}
/** /**
* @param array $data * @param array $data
* *
@@ -132,5 +164,4 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $piggyBank; return $piggyBank;
} }
} }

View File

@@ -37,6 +37,23 @@ interface PiggyBankRepositoryInterface
*/ */
public function createPiggyBankPart(array $data); public function createPiggyBankPart(array $data);
/**
* Set all piggy banks to order 0.
* @return void
*/
public function reset();
/**
*
* set id of piggy bank.
*
* @param int $id
* @param int $order
*
* @return void
*/
public function setOrder($id, $order);
/** /**

View File

@@ -5,6 +5,7 @@ namespace FireflyIII\Support;
use Cache; use Cache;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use Preferences as Prefs; use Preferences as Prefs;
/** /**
@@ -30,19 +31,27 @@ class Amount
} }
/** /**
* @param Transaction|\Transaction $transaction
* @param bool $coloured
*
* @return string * @return string
*/ */
public function formatTransaction(Transaction $transaction, $coloured = true) public function getCurrencySymbol()
{ {
$symbol = $transaction->transactionJournal->transactionCurrency->symbol; if (defined('FFCURRENCYSYMBOL')) {
$amount = floatval($transaction->amount); return FFCURRENCYSYMBOL;
}
if (\Cache::has('FFCURRENCYSYMBOL')) {
define('FFCURRENCYSYMBOL', \Cache::get('FFCURRENCYSYMBOL'));
return $this->formatWithSymbol($symbol, $amount, $coloured); return FFCURRENCYSYMBOL;
}
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
\Cache::forever('FFCURRENCYSYMBOL', $currency->symbol);
define('FFCURRENCYSYMBOL', $currency->symbol);
return $currency->symbol;
} }
/** /**
@@ -73,29 +82,56 @@ class Amount
return $symbol . ' ' . $string; return $symbol . ' ' . $string;
} }
/** /**
* @return string * @return string
*
* @param TransactionJournal $journal
*/ */
public function getCurrencySymbol() public function formatJournal(TransactionJournal $journal, $coloured = true)
{ {
if (defined('FFCURRENCYSYMBOL')) { $showPositive = true;
return FFCURRENCYSYMBOL; if (is_null($journal->symbol)) {
$symbol = $journal->transactionCurrency->symbol;
} else {
$symbol = $journal->symbol;
} }
if (\Cache::has('FFCURRENCYSYMBOL')) { $amount = 0;
define('FFCURRENCYSYMBOL', \Cache::get('FFCURRENCYSYMBOL'));
return FFCURRENCYSYMBOL; if (is_null($journal->type)) {
$type = $journal->transactionType->type;
} else {
$type = $journal->type;
} }
$currencyPreference = Prefs::get('currencyPreference', 'EUR'); if ($type == 'Withdrawal') {
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $showPositive = false;
}
\Cache::forever('FFCURRENCYSYMBOL', $currency->symbol); foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0 && $showPositive === true) {
$amount = floatval($t->amount);
break;
}
if (floatval($t->amount) < 0 && $showPositive === false) {
$amount = floatval($t->amount);
}
}
define('FFCURRENCYSYMBOL', $currency->symbol); return $this->formatWithSymbol($symbol, $amount, $coloured);
}
return $currency->symbol; /**
* @param Transaction $transaction
* @param bool $coloured
*
* @return string
*/
public function formatTransaction(Transaction $transaction, $coloured = true)
{
$symbol = $transaction->transactionJournal->transactionCurrency->symbol;
$amount = floatval($transaction->amount);
return $this->formatWithSymbol($symbol, $amount, $coloured);
} }
/** /**

View File

@@ -2,13 +2,13 @@
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Amount as Amt;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
use Input; use Input;
use Session; use Session;
use View; use View;
use Amount as Amt;
/** /**
* Class ExpandedForm * Class ExpandedForm
@@ -96,24 +96,14 @@ class ExpandedForm
public function getHolderClasses($name) public function getHolderClasses($name)
{ {
/* /*
* Get errors, warnings and successes from session: * Get errors from session:
*/ */
/** @var MessageBag $errors */ /** @var MessageBag $errors */
$errors = Session::get('errors'); $errors = Session::get('errors');
/** @var MessageBag $successes */
$successes = Session::get('successes');
switch (true) {
case (!is_null($errors) && $errors->has($name)):
$classes = 'form-group has-error has-feedback';
break;
case (!is_null($successes) && $successes->has($name)):
$classes = 'form-group has-success has-feedback';
break;
default:
$classes = 'form-group'; $classes = 'form-group';
break;
if (!is_null($errors) && $errors->has($name)) {
$classes = 'form-group has-error has-feedback';
} }
return $classes; return $classes;

View File

@@ -3,11 +3,12 @@
namespace FireflyIII\Support\Search; namespace FireflyIII\Support\Search;
use Illuminate\Support\Collection; use Auth;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/** /**
* Class Search * Class Search
@@ -23,7 +24,7 @@ class Search implements SearchInterface
*/ */
public function searchAccounts(array $words) public function searchAccounts(array $words)
{ {
return \Auth::user()->accounts()->with('accounttype')->where( return Auth::user()->accounts()->with('accounttype')->where(
function (EloquentBuilder $q) use ($words) { function (EloquentBuilder $q) use ($words) {
foreach ($words as $word) { foreach ($words as $word) {
$q->orWhere('name', 'LIKE', '%' . e($word) . '%'); $q->orWhere('name', 'LIKE', '%' . e($word) . '%');
@@ -40,7 +41,7 @@ class Search implements SearchInterface
public function searchBudgets(array $words) public function searchBudgets(array $words)
{ {
/** @var Collection $set */ /** @var Collection $set */
$set = \Auth::user()->budgets()->get(); $set = Auth::user()->budgets()->get();
$newSet = $set->filter( $newSet = $set->filter(
function (Budget $b) use ($words) { function (Budget $b) use ($words) {
$found = 0; $found = 0;
@@ -65,7 +66,7 @@ class Search implements SearchInterface
public function searchCategories(array $words) public function searchCategories(array $words)
{ {
/** @var Collection $set */ /** @var Collection $set */
$set = \Auth::user()->categories()->get(); $set = Auth::user()->categories()->get();
$newSet = $set->filter( $newSet = $set->filter(
function (Category $c) use ($words) { function (Category $c) use ($words) {
$found = 0; $found = 0;
@@ -101,12 +102,39 @@ class Search implements SearchInterface
*/ */
public function searchTransactions(array $words) public function searchTransactions(array $words)
{ {
return \Auth::user()->transactionjournals()->withRelevantData()->where( // decrypted transaction journals:
$decrypted = Auth::user()->transactionjournals()->withRelevantData()->where('encrypted', 0)->where(
function (EloquentBuilder $q) use ($words) { function (EloquentBuilder $q) use ($words) {
foreach ($words as $word) { foreach ($words as $word) {
$q->orWhere('description', 'LIKE', '%' . e($word) . '%'); $q->orWhere('description', 'LIKE', '%' . e($word) . '%');
} }
} }
)->get(); )->get();
// encrypted
$all = Auth::user()->transactionjournals()->withRelevantData()->where('encrypted', 1)->get();
$set = $all->filter(
function (TransactionJournal $journal) use ($words) {
foreach ($words as $word) {
$haystack = strtolower($journal->description);
$word = strtolower($word);
if (!(strpos($haystack, $word) === false)) {
return $journal;
}
}
}
);
$filtered = $set->merge($decrypted);
$filtered->sortBy(
function (TransactionJournal $journal) {
return intval($journal->date->format('U'));
}
);
$filtered = $filtered->reverse();
return $filtered;
} }
} }

View File

@@ -4,8 +4,11 @@ namespace FireflyIII\Validation;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config;
use DB; use DB;
use FireflyIII\Models\AccountType;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use Input;
use Navigation; use Navigation;
/** /**
@@ -34,6 +37,13 @@ class FireflyValidator extends Validator
} }
/**
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validatePiggyBankReminder($attribute, $value, $parameters) public function validatePiggyBankReminder($attribute, $value, $parameters)
{ {
$array = $this->data; $array = $this->data;
@@ -56,9 +66,60 @@ class FireflyValidator extends Validator
if ($nextReminder > $targetDate) { if ($nextReminder > $targetDate) {
return false; return false;
} }
return true; return true;
} }
/**
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validateUniqueAccountForUser($attribute, $value, $parameters)
{
// get account type from data, we must have this:
$validTypes = array_keys(Config::get('firefly.subTitlesByIdentifier'));
$type = isset($this->data['what']) && in_array($this->data['what'],$validTypes) ? $this->data['what'] : null;
// some fallback:
if(is_null($type)) {
$type = in_array(Input::get('what'),$validTypes) ? Input::get('what') : null;
}
// still null?
if(is_null($type)) {
// find by other field:
$type = isset($this->data['account_type_id']) ? $this->data['account_type_id'] : 0;
$dbType = AccountType::find($type);
} else {
$longType = Config::get('firefly.accountTypeByIdentifier.' . $type);
$dbType = AccountType::whereType($longType)->first();
}
if (is_null($dbType)) {
return false;
}
// user id?
$userId = Auth::check() ? Auth::user()->id : $this->data['user_id'];
$query = DB::table('accounts')->where('name', $value)->where('account_type_id', $dbType->id)->where('user_id', $userId);
if (isset($parameters[0])) {
$query->where('id', '!=', $parameters[0]);
}
$count = $query->count();
if ($count == 0) {
return true;
}
return false;
}
/** /**
* @param $attribute * @param $attribute
* @param $value * @param $value
@@ -69,6 +130,7 @@ class FireflyValidator extends Validator
public function validateUniqueForUser($attribute, $value, $parameters) public function validateUniqueForUser($attribute, $value, $parameters)
{ {
$query = DB::table($parameters[0])->where($parameters[1], $value); $query = DB::table($parameters[0])->where($parameters[1], $value);
$query->where('user_id', Auth::user()->id);
if (isset($paramers[2])) { if (isset($paramers[2])) {
$query->where('id', '!=', $parameters[2]); $query->where('id', '!=', $parameters[2]);
} }

View File

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

View File

@@ -19,7 +19,8 @@ return [
'accountRoles' => [ 'accountRoles' => [
'defaultAsset' => 'Default asset account', 'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account' 'sharedAsset' => 'Shared asset account',
'savingAsset' => 'Savings account'
], ],
'range_to_text' => [ 'range_to_text' => [

View File

@@ -18,14 +18,14 @@ class ChangesForV332 extends Migration {
Schema::table( Schema::table(
'accounts', function (Blueprint $table) { 'accounts', function (Blueprint $table) {
$table->boolean('encrypted'); $table->boolean('encrypted')->default(0);
} }
); );
Schema::table( Schema::table(
'reminders', function (Blueprint $table) { 'reminders', function (Blueprint $table) {
$table->text('metadata'); $table->text('metadata')->nullable();
} }
); );

View File

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

View File

@@ -116,9 +116,9 @@ class TestDataSeeder extends Seeder
*/ */
public function createUsers() public function createUsers()
{ {
User::create(['email' => 'reset@example.com', 'password' => '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' => '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' => 'james', 'reset' => null, 'remember_token' => null]); User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]);
} }
/** /**
@@ -139,7 +139,7 @@ class TestDataSeeder extends Seeder
// create account meta: // create account meta:
$meta_a = AccountMeta::create(['account_id' => $acc_a->id, 'name' => 'accountRole', 'data' => 'defaultAsset']); $meta_a = AccountMeta::create(['account_id' => $acc_a->id, 'name' => 'accountRole', 'data' => 'defaultAsset']);
$meta_b = AccountMeta::create(['account_id' => $acc_b->id, 'name' => 'accountRole', 'data' => 'defaultAsset']); $meta_b = AccountMeta::create(['account_id' => $acc_b->id, 'name' => 'accountRole', 'data' => 'savingAsset']);
$meta_c = AccountMeta::create(['account_id' => $acc_c->id, 'name' => 'accountRole', 'data' => 'defaultAsset']); $meta_c = AccountMeta::create(['account_id' => $acc_c->id, 'name' => 'accountRole', 'data' => 'defaultAsset']);
// var_dump($meta_a->toArray()); // var_dump($meta_a->toArray());
// var_dump($meta_b->toArray()); // var_dump($meta_b->toArray());

View File

@@ -21,11 +21,9 @@
</filter> </filter>
<!-- code coverage --> <!-- code coverage -->
<!--
<logging> <logging>
<log type="coverage-clover" target="./storage/coverage/clover.xml" charset="UTF-8" /> <log type="coverage-clover" target="./storage/coverage/clover.xml" charset="UTF-8" />
</logging> </logging>
-->
<php> <php>
<env name="APP_ENV" value="testing"/> <env name="APP_ENV" value="testing"/>

View File

@@ -1,5 +1,5 @@
/*! /*!
* Bootstrap v3.3.2 (http://getbootstrap.com) * Bootstrap v3.3.4 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/ */

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
/*! /*!
* Bootstrap v3.3.2 (http://getbootstrap.com) * Bootstrap v3.3.4 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/ */
@@ -958,12 +958,24 @@ th {
.glyphicon-bitcoin:before { .glyphicon-bitcoin:before {
content: "\e227"; content: "\e227";
} }
.glyphicon-btc:before {
content: "\e227";
}
.glyphicon-xbt:before {
content: "\e227";
}
.glyphicon-yen:before { .glyphicon-yen:before {
content: "\00a5"; content: "\00a5";
} }
.glyphicon-jpy:before {
content: "\00a5";
}
.glyphicon-ruble:before { .glyphicon-ruble:before {
content: "\20bd"; content: "\20bd";
} }
.glyphicon-rub:before {
content: "\20bd";
}
.glyphicon-scale:before { .glyphicon-scale:before {
content: "\e230"; content: "\e230";
} }
@@ -1074,7 +1086,7 @@ html {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
} }
body { body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px; font-size: 14px;
line-height: 1.42857143; line-height: 1.42857143;
color: #333; color: #333;
@@ -1161,6 +1173,9 @@ hr {
overflow: visible; overflow: visible;
clip: auto; clip: auto;
} }
[role="button"] {
cursor: pointer;
}
h1, h1,
h2, h2,
h3, h3,
@@ -2564,10 +2579,13 @@ output {
.form-control[disabled], .form-control[disabled],
.form-control[readonly], .form-control[readonly],
fieldset[disabled] .form-control { fieldset[disabled] .form-control {
cursor: not-allowed;
background-color: #eee; background-color: #eee;
opacity: 1; opacity: 1;
} }
.form-control[disabled],
fieldset[disabled] .form-control {
cursor: not-allowed;
}
textarea.form-control { textarea.form-control {
height: auto; height: auto;
} }
@@ -2634,6 +2652,7 @@ input[type="search"] {
} }
.radio-inline, .radio-inline,
.checkbox-inline { .checkbox-inline {
position: relative;
display: inline-block; display: inline-block;
padding-left: 20px; padding-left: 20px;
margin-bottom: 0; margin-bottom: 0;
@@ -2667,6 +2686,7 @@ fieldset[disabled] .checkbox label {
cursor: not-allowed; cursor: not-allowed;
} }
.form-control-static { .form-control-static {
min-height: 34px;
padding-top: 7px; padding-top: 7px;
padding-bottom: 7px; padding-bottom: 7px;
margin-bottom: 0; margin-bottom: 0;
@@ -2708,6 +2728,7 @@ select[multiple].form-group-sm .form-control {
} }
.form-group-sm .form-control-static { .form-group-sm .form-control-static {
height: 30px; height: 30px;
min-height: 32px;
padding: 5px 10px; padding: 5px 10px;
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
@@ -2744,6 +2765,7 @@ select[multiple].form-group-lg .form-control {
} }
.form-group-lg .form-control-static { .form-group-lg .form-control-static {
height: 46px; height: 46px;
min-height: 38px;
padding: 10px 16px; padding: 10px 16px;
font-size: 18px; font-size: 18px;
line-height: 1.3333333; line-height: 1.3333333;
@@ -3365,11 +3387,9 @@ input[type="button"].btn-block {
} }
.collapse { .collapse {
display: none; display: none;
visibility: hidden;
} }
.collapse.in { .collapse.in {
display: block; display: block;
visibility: visible;
} }
tr.collapse.in { tr.collapse.in {
display: table-row; display: table-row;
@@ -3397,7 +3417,7 @@ tbody.collapse.in {
height: 0; height: 0;
margin-left: 2px; margin-left: 2px;
vertical-align: middle; vertical-align: middle;
border-top: 4px solid; border-top: 4px dashed;
border-right: 4px solid transparent; border-right: 4px solid transparent;
border-left: 4px solid transparent; border-left: 4px solid transparent;
} }
@@ -4037,11 +4057,9 @@ select[multiple].input-group-sm > .input-group-btn > .btn {
} }
.tab-content > .tab-pane { .tab-content > .tab-pane {
display: none; display: none;
visibility: hidden;
} }
.tab-content > .active { .tab-content > .active {
display: block; display: block;
visibility: visible;
} }
.nav-tabs .dropdown-menu { .nav-tabs .dropdown-menu {
margin-top: -1px; margin-top: -1px;
@@ -4088,7 +4106,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn {
height: auto !important; height: auto !important;
padding-bottom: 0; padding-bottom: 0;
overflow: visible !important; overflow: visible !important;
visibility: visible !important;
} }
.navbar-collapse.in { .navbar-collapse.in {
overflow-y: visible; overflow-y: visible;
@@ -4813,7 +4830,8 @@ a.label:focus {
position: relative; position: relative;
top: -1px; top: -1px;
} }
.btn-xs .badge { .btn-xs .badge,
.btn-group-xs > .btn .badge {
top: 0; top: 0;
padding: 1px 5px; padding: 1px 5px;
} }
@@ -5645,10 +5663,10 @@ a.list-group-item-danger.active:focus {
height: 100%; height: 100%;
border: 0; border: 0;
} }
.embed-responsive.embed-responsive-16by9 { .embed-responsive-16by9 {
padding-bottom: 56.25%; padding-bottom: 56.25%;
} }
.embed-responsive.embed-responsive-4by3 { .embed-responsive-4by3 {
padding-bottom: 75%; padding-bottom: 75%;
} }
.well { .well {
@@ -5707,7 +5725,7 @@ button.close {
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 1040; z-index: 1050;
display: none; display: none;
overflow: hidden; overflow: hidden;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
@@ -5750,10 +5768,12 @@ button.close {
box-shadow: 0 3px 9px rgba(0, 0, 0, .5); box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
} }
.modal-backdrop { .modal-backdrop {
position: absolute; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0;
left: 0; left: 0;
z-index: 1040;
background-color: #000; background-color: #000;
} }
.modal-backdrop.fade { .modal-backdrop.fade {
@@ -5824,11 +5844,10 @@ button.close {
position: absolute; position: absolute;
z-index: 1070; z-index: 1070;
display: block; display: block;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
line-height: 1.4; line-height: 1.4;
visibility: visible;
filter: alpha(opacity=0); filter: alpha(opacity=0);
opacity: 0; opacity: 0;
} }
@@ -5932,7 +5951,7 @@ button.close {
display: none; display: none;
max-width: 276px; max-width: 276px;
padding: 1px; padding: 1px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
line-height: 1.42857143; line-height: 1.42857143;
@@ -6348,7 +6367,6 @@ button.close {
} }
.hidden { .hidden {
display: none !important; display: none !important;
visibility: hidden !important;
} }
.affix { .affix {
position: fixed; position: fixed;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
/*! /*!
* Bootstrap v3.3.2 (http://getbootstrap.com) * Bootstrap v3.3.4 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/ */
@@ -17,7 +17,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: transition.js v3.3.2 * Bootstrap: transition.js v3.3.4
* http://getbootstrap.com/javascript/#transitions * http://getbootstrap.com/javascript/#transitions
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -77,7 +77,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: alert.js v3.3.2 * Bootstrap: alert.js v3.3.4
* http://getbootstrap.com/javascript/#alerts * http://getbootstrap.com/javascript/#alerts
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -96,7 +96,7 @@ if (typeof jQuery === 'undefined') {
$(el).on('click', dismiss, this.close) $(el).on('click', dismiss, this.close)
} }
Alert.VERSION = '3.3.2' Alert.VERSION = '3.3.4'
Alert.TRANSITION_DURATION = 150 Alert.TRANSITION_DURATION = 150
@@ -172,7 +172,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: button.js v3.3.2 * Bootstrap: button.js v3.3.4
* http://getbootstrap.com/javascript/#buttons * http://getbootstrap.com/javascript/#buttons
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -192,7 +192,7 @@ if (typeof jQuery === 'undefined') {
this.isLoading = false this.isLoading = false
} }
Button.VERSION = '3.3.2' Button.VERSION = '3.3.4'
Button.DEFAULTS = { Button.DEFAULTS = {
loadingText: 'loading...' loadingText: 'loading...'
@@ -289,7 +289,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: carousel.js v3.3.2 * Bootstrap: carousel.js v3.3.4
* http://getbootstrap.com/javascript/#carousel * http://getbootstrap.com/javascript/#carousel
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -307,10 +307,10 @@ if (typeof jQuery === 'undefined') {
this.$element = $(element) this.$element = $(element)
this.$indicators = this.$element.find('.carousel-indicators') this.$indicators = this.$element.find('.carousel-indicators')
this.options = options this.options = options
this.paused = this.paused = null
this.sliding = this.sliding = null
this.interval = this.interval = null
this.$active = this.$active = null
this.$items = null this.$items = null
this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
@@ -320,7 +320,7 @@ if (typeof jQuery === 'undefined') {
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
} }
Carousel.VERSION = '3.3.2' Carousel.VERSION = '3.3.4'
Carousel.TRANSITION_DURATION = 600 Carousel.TRANSITION_DURATION = 600
@@ -527,7 +527,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: collapse.js v3.3.2 * Bootstrap: collapse.js v3.3.4
* http://getbootstrap.com/javascript/#collapse * http://getbootstrap.com/javascript/#collapse
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -544,7 +544,8 @@ if (typeof jQuery === 'undefined') {
var Collapse = function (element, options) { var Collapse = function (element, options) {
this.$element = $(element) this.$element = $(element)
this.options = $.extend({}, Collapse.DEFAULTS, options) this.options = $.extend({}, Collapse.DEFAULTS, options)
this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
'[data-toggle="collapse"][data-target="#' + element.id + '"]')
this.transitioning = null this.transitioning = null
if (this.options.parent) { if (this.options.parent) {
@@ -556,13 +557,12 @@ if (typeof jQuery === 'undefined') {
if (this.options.toggle) this.toggle() if (this.options.toggle) this.toggle()
} }
Collapse.VERSION = '3.3.2' Collapse.VERSION = '3.3.4'
Collapse.TRANSITION_DURATION = 350 Collapse.TRANSITION_DURATION = 350
Collapse.DEFAULTS = { Collapse.DEFAULTS = {
toggle: true, toggle: true
trigger: '[data-toggle="collapse"]'
} }
Collapse.prototype.dimension = function () { Collapse.prototype.dimension = function () {
@@ -700,7 +700,7 @@ if (typeof jQuery === 'undefined') {
var data = $this.data('bs.collapse') var data = $this.data('bs.collapse')
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data && options.toggle && option == 'show') options.toggle = false if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]() if (typeof option == 'string') data[option]()
}) })
@@ -731,7 +731,7 @@ if (typeof jQuery === 'undefined') {
var $target = getTargetFromTrigger($this) var $target = getTargetFromTrigger($this)
var data = $target.data('bs.collapse') var data = $target.data('bs.collapse')
var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) var option = data ? 'toggle' : $this.data()
Plugin.call($target, option) Plugin.call($target, option)
}) })
@@ -739,7 +739,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: dropdown.js v3.3.2 * Bootstrap: dropdown.js v3.3.4
* http://getbootstrap.com/javascript/#dropdowns * http://getbootstrap.com/javascript/#dropdowns
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -759,7 +759,7 @@ if (typeof jQuery === 'undefined') {
$(element).on('click.bs.dropdown', this.toggle) $(element).on('click.bs.dropdown', this.toggle)
} }
Dropdown.VERSION = '3.3.2' Dropdown.VERSION = '3.3.4'
Dropdown.prototype.toggle = function (e) { Dropdown.prototype.toggle = function (e) {
var $this = $(this) var $this = $(this)
@@ -812,7 +812,7 @@ if (typeof jQuery === 'undefined') {
return $this.trigger('click') return $this.trigger('click')
} }
var desc = ' li:not(.divider):visible a' var desc = ' li:not(.disabled):visible a'
var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc) var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
if (!$items.length) return if (!$items.length) return
@@ -901,7 +901,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: modal.js v3.3.2 * Bootstrap: modal.js v3.3.4
* http://getbootstrap.com/javascript/#modals * http://getbootstrap.com/javascript/#modals
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -919,9 +919,12 @@ if (typeof jQuery === 'undefined') {
this.options = options this.options = options
this.$body = $(document.body) this.$body = $(document.body)
this.$element = $(element) this.$element = $(element)
this.$backdrop = this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0 this.scrollbarWidth = 0
this.ignoreBackdropClick = false
if (this.options.remote) { if (this.options.remote) {
this.$element this.$element
@@ -932,7 +935,7 @@ if (typeof jQuery === 'undefined') {
} }
} }
Modal.VERSION = '3.3.2' Modal.VERSION = '3.3.4'
Modal.TRANSITION_DURATION = 300 Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.BACKDROP_TRANSITION_DURATION = 150
@@ -966,6 +969,12 @@ if (typeof jQuery === 'undefined') {
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
})
})
this.backdrop(function () { this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade') var transition = $.support.transition && that.$element.hasClass('fade')
@@ -977,7 +986,6 @@ if (typeof jQuery === 'undefined') {
.show() .show()
.scrollTop(0) .scrollTop(0)
if (that.options.backdrop) that.adjustBackdrop()
that.adjustDialog() that.adjustDialog()
if (transition) { if (transition) {
@@ -993,7 +1001,7 @@ if (typeof jQuery === 'undefined') {
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ? transition ?
that.$element.find('.modal-dialog') // wait for modal to slide in that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () { .one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e) that.$element.trigger('focus').trigger(e)
}) })
@@ -1022,6 +1030,9 @@ if (typeof jQuery === 'undefined') {
.removeClass('in') .removeClass('in')
.attr('aria-hidden', true) .attr('aria-hidden', true)
.off('click.dismiss.bs.modal') .off('click.dismiss.bs.modal')
.off('mouseup.dismiss.bs.modal')
this.$dialog.off('mousedown.dismiss.bs.modal')
$.support.transition && this.$element.hasClass('fade') ? $.support.transition && this.$element.hasClass('fade') ?
this.$element this.$element
@@ -1082,12 +1093,17 @@ if (typeof jQuery === 'undefined') {
var doAnimate = $.support.transition && animate var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.prependTo(this.$element) .appendTo(this.$body)
.on('click.dismiss.bs.modal', $.proxy(function (e) {
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (this.ignoreBackdropClick) {
this.ignoreBackdropClick = false
return
}
if (e.target !== e.currentTarget) return if (e.target !== e.currentTarget) return
this.options.backdrop == 'static' this.options.backdrop == 'static'
? this.$element[0].focus.call(this.$element[0]) ? this.$element[0].focus()
: this.hide.call(this) : this.hide()
}, this)) }, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
@@ -1123,16 +1139,9 @@ if (typeof jQuery === 'undefined') {
// these following methods are used to handle overflowing modals // these following methods are used to handle overflowing modals
Modal.prototype.handleUpdate = function () { Modal.prototype.handleUpdate = function () {
if (this.options.backdrop) this.adjustBackdrop()
this.adjustDialog() this.adjustDialog()
} }
Modal.prototype.adjustBackdrop = function () {
this.$backdrop
.css('height', 0)
.css('height', this.$element[0].scrollHeight)
}
Modal.prototype.adjustDialog = function () { Modal.prototype.adjustDialog = function () {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
@@ -1150,17 +1159,23 @@ if (typeof jQuery === 'undefined') {
} }
Modal.prototype.checkScrollbar = function () { Modal.prototype.checkScrollbar = function () {
this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight var fullWindowWidth = window.innerWidth
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
var documentElementRect = document.documentElement.getBoundingClientRect()
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
this.scrollbarWidth = this.measureScrollbar() this.scrollbarWidth = this.measureScrollbar()
} }
Modal.prototype.setScrollbar = function () { Modal.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
} }
Modal.prototype.resetScrollbar = function () { Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', '') this.$body.css('padding-right', this.originalBodyPad)
} }
Modal.prototype.measureScrollbar = function () { // thx walsh Modal.prototype.measureScrollbar = function () { // thx walsh
@@ -1226,7 +1241,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: tooltip.js v3.3.2 * Bootstrap: tooltip.js v3.3.4
* http://getbootstrap.com/javascript/#tooltip * http://getbootstrap.com/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame * Inspired by the original jQuery.tipsy by Jason Frame
* ======================================================================== * ========================================================================
@@ -1242,17 +1257,17 @@ if (typeof jQuery === 'undefined') {
// =============================== // ===============================
var Tooltip = function (element, options) { var Tooltip = function (element, options) {
this.type = this.type = null
this.options = this.options = null
this.enabled = this.enabled = null
this.timeout = this.timeout = null
this.hoverState = this.hoverState = null
this.$element = null this.$element = null
this.init('tooltip', element, options) this.init('tooltip', element, options)
} }
Tooltip.VERSION = '3.3.2' Tooltip.VERSION = '3.3.4'
Tooltip.TRANSITION_DURATION = 150 Tooltip.TRANSITION_DURATION = 150
@@ -1279,6 +1294,10 @@ if (typeof jQuery === 'undefined') {
this.options = this.getOptions(options) this.options = this.getOptions(options)
this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport) this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
if (this.$element[0] instanceof document.constructor && !this.options.selector) {
throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
}
var triggers = this.options.trigger.split(' ') var triggers = this.options.trigger.split(' ')
for (var i = triggers.length; i--;) { for (var i = triggers.length; i--;) {
@@ -1499,10 +1518,10 @@ if (typeof jQuery === 'undefined') {
this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
} }
Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) { Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
this.arrow() this.arrow()
.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
.css(isHorizontal ? 'top' : 'left', '') .css(isVertical ? 'top' : 'left', '')
} }
Tooltip.prototype.setContent = function () { Tooltip.prototype.setContent = function () {
@@ -1515,7 +1534,7 @@ if (typeof jQuery === 'undefined') {
Tooltip.prototype.hide = function (callback) { Tooltip.prototype.hide = function (callback) {
var that = this var that = this
var $tip = this.tip() var $tip = $(this.$tip)
var e = $.Event('hide.bs.' + this.type) var e = $.Event('hide.bs.' + this.type)
function complete() { function complete() {
@@ -1532,7 +1551,7 @@ if (typeof jQuery === 'undefined') {
$tip.removeClass('in') $tip.removeClass('in')
$.support.transition && this.$tip.hasClass('fade') ? $.support.transition && $tip.hasClass('fade') ?
$tip $tip
.one('bsTransitionEnd', complete) .one('bsTransitionEnd', complete)
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
@@ -1676,7 +1695,7 @@ if (typeof jQuery === 'undefined') {
var data = $this.data('bs.tooltip') var data = $this.data('bs.tooltip')
var options = typeof option == 'object' && option var options = typeof option == 'object' && option
if (!data && option == 'destroy') return if (!data && /destroy|hide/.test(option)) return
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]() if (typeof option == 'string') data[option]()
}) })
@@ -1699,7 +1718,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: popover.js v3.3.2 * Bootstrap: popover.js v3.3.4
* http://getbootstrap.com/javascript/#popovers * http://getbootstrap.com/javascript/#popovers
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -1719,7 +1738,7 @@ if (typeof jQuery === 'undefined') {
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
Popover.VERSION = '3.3.2' Popover.VERSION = '3.3.4'
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
placement: 'right', placement: 'right',
@@ -1775,11 +1794,6 @@ if (typeof jQuery === 'undefined') {
return (this.$arrow = this.$arrow || this.tip().find('.arrow')) return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
} }
Popover.prototype.tip = function () {
if (!this.$tip) this.$tip = $(this.options.template)
return this.$tip
}
// POPOVER PLUGIN DEFINITION // POPOVER PLUGIN DEFINITION
// ========================= // =========================
@@ -1790,7 +1804,7 @@ if (typeof jQuery === 'undefined') {
var data = $this.data('bs.popover') var data = $this.data('bs.popover')
var options = typeof option == 'object' && option var options = typeof option == 'object' && option
if (!data && option == 'destroy') return if (!data && /destroy|hide/.test(option)) return
if (!data) $this.data('bs.popover', (data = new Popover(this, options))) if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]() if (typeof option == 'string') data[option]()
}) })
@@ -1813,7 +1827,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: scrollspy.js v3.3.2 * Bootstrap: scrollspy.js v3.3.4
* http://getbootstrap.com/javascript/#scrollspy * http://getbootstrap.com/javascript/#scrollspy
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -1828,10 +1842,8 @@ if (typeof jQuery === 'undefined') {
// ========================== // ==========================
function ScrollSpy(element, options) { function ScrollSpy(element, options) {
var process = $.proxy(this.process, this) this.$body = $(document.body)
this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
this.$body = $('body')
this.$scrollElement = $(element).is('body') ? $(window) : $(element)
this.options = $.extend({}, ScrollSpy.DEFAULTS, options) this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
this.selector = (this.options.target || '') + ' .nav li > a' this.selector = (this.options.target || '') + ' .nav li > a'
this.offsets = [] this.offsets = []
@@ -1839,12 +1851,12 @@ if (typeof jQuery === 'undefined') {
this.activeTarget = null this.activeTarget = null
this.scrollHeight = 0 this.scrollHeight = 0
this.$scrollElement.on('scroll.bs.scrollspy', process) this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
this.refresh() this.refresh()
this.process() this.process()
} }
ScrollSpy.VERSION = '3.3.2' ScrollSpy.VERSION = '3.3.4'
ScrollSpy.DEFAULTS = { ScrollSpy.DEFAULTS = {
offset: 10 offset: 10
@@ -1855,19 +1867,18 @@ if (typeof jQuery === 'undefined') {
} }
ScrollSpy.prototype.refresh = function () { ScrollSpy.prototype.refresh = function () {
var that = this
var offsetMethod = 'offset' var offsetMethod = 'offset'
var offsetBase = 0 var offsetBase = 0
if (!$.isWindow(this.$scrollElement[0])) {
offsetMethod = 'position'
offsetBase = this.$scrollElement.scrollTop()
}
this.offsets = [] this.offsets = []
this.targets = [] this.targets = []
this.scrollHeight = this.getScrollHeight() this.scrollHeight = this.getScrollHeight()
var self = this if (!$.isWindow(this.$scrollElement[0])) {
offsetMethod = 'position'
offsetBase = this.$scrollElement.scrollTop()
}
this.$body this.$body
.find(this.selector) .find(this.selector)
@@ -1883,8 +1894,8 @@ if (typeof jQuery === 'undefined') {
}) })
.sort(function (a, b) { return a[0] - b[0] }) .sort(function (a, b) { return a[0] - b[0] })
.each(function () { .each(function () {
self.offsets.push(this[0]) that.offsets.push(this[0])
self.targets.push(this[1]) that.targets.push(this[1])
}) })
} }
@@ -1913,7 +1924,7 @@ if (typeof jQuery === 'undefined') {
for (i = offsets.length; i--;) { for (i = offsets.length; i--;) {
activeTarget != targets[i] activeTarget != targets[i]
&& scrollTop >= offsets[i] && scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1]) && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
&& this.activate(targets[i]) && this.activate(targets[i])
} }
} }
@@ -1989,7 +2000,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: tab.js v3.3.2 * Bootstrap: tab.js v3.3.4
* http://getbootstrap.com/javascript/#tabs * http://getbootstrap.com/javascript/#tabs
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -2007,7 +2018,7 @@ if (typeof jQuery === 'undefined') {
this.element = $(element) this.element = $(element)
} }
Tab.VERSION = '3.3.2' Tab.VERSION = '3.3.4'
Tab.TRANSITION_DURATION = 150 Tab.TRANSITION_DURATION = 150
@@ -2078,7 +2089,7 @@ if (typeof jQuery === 'undefined') {
element.removeClass('fade') element.removeClass('fade')
} }
if (element.parent('.dropdown-menu')) { if (element.parent('.dropdown-menu').length) {
element element
.closest('li.dropdown') .closest('li.dropdown')
.addClass('active') .addClass('active')
@@ -2143,7 +2154,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery); }(jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: affix.js v3.3.2 * Bootstrap: affix.js v3.3.4
* http://getbootstrap.com/javascript/#affix * http://getbootstrap.com/javascript/#affix
* ======================================================================== * ========================================================================
* Copyright 2011-2015 Twitter, Inc. * Copyright 2011-2015 Twitter, Inc.
@@ -2165,14 +2176,14 @@ if (typeof jQuery === 'undefined') {
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
this.$element = $(element) this.$element = $(element)
this.affixed = this.affixed = null
this.unpin = this.unpin = null
this.pinnedOffset = null this.pinnedOffset = null
this.checkPosition() this.checkPosition()
} }
Affix.VERSION = '3.3.2' Affix.VERSION = '3.3.4'
Affix.RESET = 'affix affix-top affix-bottom' Affix.RESET = 'affix affix-top affix-bottom'
@@ -2222,7 +2233,7 @@ if (typeof jQuery === 'undefined') {
var offset = this.options.offset var offset = this.options.offset
var offsetTop = offset.top var offsetTop = offset.top
var offsetBottom = offset.bottom var offsetBottom = offset.bottom
var scrollHeight = $('body').height() var scrollHeight = $(document.body).height()
if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offset != 'object') offsetBottom = offsetTop = offset
if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)

File diff suppressed because one or more lines are too long

View File

@@ -54,7 +54,7 @@
} }
.daterangepicker .calendar th, .daterangepicker .calendar td { .daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
min-width: 32px; min-width: 32px;

View File

@@ -1,2 +1,3 @@
#daterange {cursor:pointer;} #daterange {cursor:pointer;}
.google-chart-error {height:30px;background:url('/images/error.png') no-repeat center center;} .google-chart-error {height:30px;background:url('/images/error.png') no-repeat center center;}
.handle {cursor:move;}

View File

@@ -1,5 +1,5 @@
var google = google || {}; var google = google || {};
google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'sankey', 'table']}); google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'line', 'sankey', 'table']});
function googleChart(chartType, URL, container, options) { function googleChart(chartType, URL, container, options) {
if ($('#' + container).length === 1) { if ($('#' + container).length === 1) {
@@ -24,14 +24,14 @@ function googleChart(chartType, URL, container, options) {
/* /*
Create a new google charts object. Create a new google charts object.
*/ */
var chart = false var chart = false;
var options = false; var options = false;
if (chartType === 'line') { if (chartType === 'line') {
chart = new google.visualization.LineChart(document.getElementById(container)); chart = new google.visualization.LineChart(document.getElementById(container));
options = options || defaultLineChartOptions; options = options || defaultLineChartOptions;
} }
if (chartType === 'column') { if (chartType === 'column') {
chart = new google.charts.Bar(document.getElementById(container)); chart = new google.visualization.ColumnChart(document.getElementById(container));
options = options || defaultColumnChartOptions; options = options || defaultColumnChartOptions;
} }
if (chartType === 'pie') { if (chartType === 'pie') {
@@ -39,7 +39,7 @@ function googleChart(chartType, URL, container, options) {
options = options || defaultPieChartOptions; options = options || defaultPieChartOptions;
} }
if (chartType === 'bar') { if (chartType === 'bar') {
chart = new google.charts.Bar(document.getElementById(container)); chart = new google.visualization.BarChart(document.getElementById(container));
options = options || defaultBarChartOptions; options = options || defaultBarChartOptions;
} }
if (chartType === 'stackedColumn') { if (chartType === 'stackedColumn') {

View File

@@ -16,19 +16,19 @@ var defaultLineChartOptions = {
hAxis: { hAxis: {
textStyle: { textStyle: {
color: '#838383', color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
}, },
baselineColor: '#aaaaaa',
gridlines: { gridlines: {
color: 'transparent' color: 'transparent'
} }
}, },
fontName: 'Roboto',
fontSize: 11,
vAxis: { vAxis: {
textStyle: { textStyle: {
color: '#838383', color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
}, },
baselineColor: '#aaaaaa',
format: '\u20AC #' format: '\u20AC #'
} }
@@ -38,9 +38,30 @@ var defaultLineChartOptions = {
var defaultBarChartOptions = { var defaultBarChartOptions = {
height: 400, height: 400,
bars: 'horizontal', bars: 'horizontal',
hAxis: {format: '\u20AC #'}, hAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
},
fontName: 'Roboto',
fontSize: 11,
colors: ["#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
vAxis: {
textStyle: {
color: '#838383',
},
textPosition: 'in',
gridlines: {
color: 'transparent'
},
baselineColor: '#aaaaaa'
},
chartArea: { chartArea: {
left: 75, left: 15,
top: 10, top: 10,
width: '100%', width: '100%',
height: '90%' height: '90%'
@@ -63,6 +84,8 @@ var defaultComboChartOptions = {
minValue: 0, minValue: 0,
format: '\u20AC #' format: '\u20AC #'
}, },
fontName: 'Roboto',
fontSize: 11,
legend: { legend: {
position: 'none' position: 'none'
}, },
@@ -82,7 +105,26 @@ var defaultColumnChartOptions = {
width: '85%', width: '85%',
height: '80%' height: '80%'
}, },
vAxis: {format: '\u20AC #'}, fontName: 'Roboto',
fontSize: 11,
hAxis: {
textStyle: {
color: '#838383',
},
gridlines: {
color: 'transparent'
},
baselineColor: '#aaaaaa'
},
colors: ["#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
vAxis: {
textStyle: {
color: '#838383',
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
},
legend: { legend: {
position: 'none' position: 'none'
}, },
@@ -99,13 +141,13 @@ var defaultStackedColumnChartOptions = {
legend: { legend: {
position: 'none' position: 'none'
}, },
fontName: 'Roboto',
fontSize: 11,
isStacked: true, isStacked: true,
colors: ["#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"], colors: ["#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
hAxis: { hAxis: {
textStyle: { textStyle: {
color: '#838383', color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
}, },
gridlines: { gridlines: {
color: 'transparent' color: 'transparent'
@@ -114,8 +156,6 @@ var defaultStackedColumnChartOptions = {
vAxis: { vAxis: {
textStyle: { textStyle: {
color: '#838383', color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
}, },
format: '\u20AC #' format: '\u20AC #'
} }
@@ -128,6 +168,8 @@ var defaultPieChartOptions = {
width: '100%', width: '100%',
height: '100%' height: '100%'
}, },
fontName: 'Roboto',
fontSize: 11,
height: 200, height: 200,
legend: { legend: {
position: 'none' position: 'none'

7
public/js/jquery-ui.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -5,18 +5,41 @@ $(function () {
if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') { if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') {
googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history'); googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history');
} }
$('#sortable').sortable(
{
stop: stopSorting,
handle: '.handle'
}
);
}); });
function addMoney(e) { function addMoney(e) {
var pigID = parseInt($(e.target).data('id')); var pigID = parseInt($(e.target).data('id'));
$('#moneyManagementModal').empty().load('piggy-banks/add/' + pigID, function() {$('#moneyManagementModal').modal('show');}); $('#moneyManagementModal').empty().load('piggy-banks/add/' + pigID, function () {
$('#moneyManagementModal').modal('show');
});
return false; return false;
} }
function removeMoney(e) { function removeMoney(e) {
var pigID = parseInt($(e.target).data('id')); var pigID = parseInt($(e.target).data('id'));
$('#moneyManagementModal').empty().load('piggy-banks/remove/' + pigID, function() {$('#moneyManagementModal').modal('show');}); $('#moneyManagementModal').empty().load('piggy-banks/remove/' + pigID, function () {
$('#moneyManagementModal').modal('show');
});
return false; return false;
} }
function stopSorting() {
$('.loadSpin').addClass('fa fa-refresh fa-spin');
var order = [];
$.each($('#sortable>div'), function(i,v) {
var holder = $(v);
var id = holder.data('id');
order.push(id);
});
$.post('/piggy-banks/sort',{_token: token, order: order}).success(function(data) {
"use strict";
$('.loadSpin').removeClass('fa fa-refresh fa-spin');
});
}

View File

@@ -14,8 +14,80 @@ if ($('input[name="category"]').length > 0) {
}); });
} }
// Return a helper with preserved width of cells
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};
$(document).ready(function () { $(document).ready(function () {
if (typeof googleTablePaged != 'undefined') { if (typeof googleTablePaged != 'undefined') {
googleTablePaged('table/transactions/' + what, 'transaction-table'); googleTablePaged('table/transactions/' + what, 'transaction-table');
} }
// sortable!
$(".sortable-table tbody").sortable(
{
helper: fixHelper,
items: 'tr:not(.ignore)',
stop: sortStop,
handle: '.handle'
}
).disableSelection();
}); });
function sortStop(event, ui) {
var current = $(ui.item);
var thisDate = current.data('date');
var originalBG = current.css('backgroundColor');
if (current.prev().data('date') != thisDate && current.next().data('date') != thisDate) {
//console.log('False!');
//console.log('[' + current.prev().data('date') + '] [' + thisDate + '] [' + current.next().data('date') + ']');
// animate something with color:
current.animate({
backgroundColor: "#d9534f"
}, 200, function () {
$(this).animate({
backgroundColor: originalBG
}, 200);
});
return false;
}
// do update
var list = $('tr[data-date="' + thisDate + '"]');
var submit = [];
$.each(list, function (i, v) {
var row = $(v);
var id = row.data('id');
submit.push(id);
});
// do extra animation when done?
$.post('/transaction/reorder',{items: submit,date: thisDate,_token:token});
console.log(submit);
//console.log('TRUE!');
//console.log('[' + current.prev().data('date') + '] [' + thisDate + '] [' + current.next().data('date') + ']');
current.animate({
backgroundColor: "#5cb85c"
}, 200, function () {
$(this).animate({
backgroundColor: originalBG
}, 200);
});
//else update some order thing bla bla.
//check if the item above OR under this one have the same date
//if not. return false
}

View File

@@ -37,7 +37,7 @@
<i class="fa fa-repeat fa-fw"></i> Transactions <i class="fa fa-repeat fa-fw"></i> Transactions
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full') @include('list.journals-full',['sorting' => true])
</div> </div>
</div> </div>
</div> </div>
@@ -49,10 +49,14 @@
<script type="text/javascript"> <script type="text/javascript">
var accountID = {{{$account->id}}}; var accountID = {{{$account->id}}};
var currencyCode = '{{Amount::getCurrencyCode()}}'; var currencyCode = '{{Amount::getCurrencyCode()}}';
var token = "{{csrf_token()}}";
</script> </script>
<!-- load the libraries and scripts necessary for Google Charts: --> <!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="js/gcharts.options.js"></script> <script type="text/javascript" src="js/gcharts.options.js"></script>
<script type="text/javascript" src="js/gcharts.js"></script> <script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript" src="js/accounts.js"></script> <script type="text/javascript" src="js/accounts.js"></script>
<script src="js/jquery-ui.min.js" type="text/javascript"></script>
<script src="js/transactions.js" type="text/javascript"></script>
@stop @stop

View File

@@ -8,15 +8,15 @@
<i class="fa fa-rotate-right"></i> {{{$bill->name}}} <i class="fa fa-rotate-right"></i> {{{$bill->name}}}
@if($bill->active) @if($bill->active)
<span class="glyphicon glyphicon-ok" title="Active"></span> <i class="fa fa-check fa-fw" title="Active"></i>
@else @else
<span class="glyphicon glyphicon-remove" title="Inactive"></span> <i class="fa fa-times fa-fw" title="Inactive"></i>
@endif @endif
@if($bill->automatch) @if($bill->automatch)
<span class="glyphicon glyphicon-ok" title="Automatically matched by Firefly"></span> <i class="fa fa-check fa-fw" title="Automatically matched by Firefly"></i>
@else @else
<span class="glyphicon glyphicon-remove" title="Not automatically matched by Firefly"></span> <i class="fa fa-times fa-fw" title="Not automatically matched by Firefly"></i>
@endif @endif
<!-- ACTIONS MENU --> <!-- ACTIONS MENU -->
@@ -27,8 +27,8 @@
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu pull-right" role="menu"> <ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('bills.edit',$bill->id)}}"><span class="glyphicon glyphicon-pencil"></span> edit</a></li> <li><a href="{{route('bills.edit',$bill->id)}}"><i class="fa fa-fw fa-pencil"></i> edit</a></li>
<li><a href="{{route('bills.delete',$bill->id)}}"><span class="glyphicon glyphicon-trash"></span> delete</a></li> <li><a href="{{route('bills.delete',$bill->id)}}"><i class="fa fa-fw fa-trash-o"></i> delete</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -94,7 +94,7 @@
Connected transaction journals Connected transaction journals
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full') @include('list.journals-full',['sorting' => false])
</div> </div>
</div> </div>
</div> </div>

View File

@@ -8,7 +8,7 @@
{{{$subTitle}}} {{{$subTitle}}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['journals' => $list]) @include('list.journals-full',['journals' => $list,'sorting' => false])
</div> </div>
</div> </div>
</div> </div>

View File

@@ -16,7 +16,7 @@
<div class="panel-heading"> <div class="panel-heading">
Transactions Transactions
</div> </div>
@include('list.journals-full') @include('list.journals-full',['sorting' => false])
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-3 col-sm-5"> <div class="col-lg-3 col-md-3 col-sm-5">

View File

@@ -8,7 +8,7 @@
{{{$subTitle}}} {{{$subTitle}}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full',['journals' => $list]) @include('list.journals-full',['journals' => $list,'sorting' => false])
</div> </div>
</div> </div>
</div> </div>

View File

@@ -34,7 +34,7 @@
Transactions Transactions
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-full') @include('list.journals-full',['sorting' => false])
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,12 +1,4 @@
@if($errors->has($name)) @if($errors->has($name))
<span class="glyphicon glyphicon-remove form-control-feedback"></span> <span class="form-control-feedback"><i class="fa fa-fw fa-remove"></i></span>
<p class="text-danger">{{{$errors->first($name)}}}</p> <p class="text-danger">{{{$errors->first($name)}}}</p>
@endif @endif
@if(Session::has('warnings') && Session::get('warnings')->has($name))
<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
<p class="text-warning">{{{Session::get('warnings')->first($name)}}}</p>
@endif
@if(Session::has('successes') && Session::get('successes')->has($name))
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
<p class="text-success">{{{Session::get('successes')->first($name)}}}</p>
@endif

View File

@@ -57,7 +57,54 @@
<i class="fa fa-line-chart"></i> Savings <i class="fa fa-line-chart"></i> Savings
</div> </div>
<div class="panel-body"> <div class="panel-body">
(todo) @if(count($savings) == 0)
<p class="small"><em>Mark your asset accounts as "Savings account" to fill this panel.</em></p>
@else
@foreach($savings as $account)
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"><h5><a href="{{route('accounts.show')}}">{{$account->name}}</a></h5></div>
</div>
<div class="row">
<!-- start -->
<div class="col-lg-2 col-md-2 col-sm-3 col-xs-4">{!! Amount::format($account->startBalance) !!}</div>
<!-- bar -->
<div class="col-lg-8 col-md-8 col-sm-6 col-xs-4">
@if($account->difference < 0)
<!-- green (100-pct), then red (pct) -->
<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped" style="width: {{100 - $account->percentage}}%">
@if($account->percentage <= 50)
{{Amount::format($account->difference,false)}}
@endif
</div>
<div class="progress-bar progress-bar-danger progress-bar-striped" style="width: {{$account->percentage}}%">
@if($account->percentage > 50)
{{Amount::format($account->difference,false)}}
@endif
</div>
</div>
@else
<!-- green (pct), then blue (100-pct) -->
<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped" style="width: {{$account->percentage}}%">
@if($account->percentage > 50)
{{Amount::format($account->difference,false)}}
@endif
</div>
<div class="progress-bar progress-bar-info progress-bar-striped" style="width: {{100 - $account->percentage}}%">
@if($account->percentage <= 50)
{{Amount::format($account->difference,false)}}
@endif
</div>
</div>
@endif
</div>
<!-- end -->
<div class="col-lg-2 col-md-2 col-sm-3 col-xs-4">{!! Amount::format($account->endBalance) !!}</div>
</div>
@endforeach
@endif
</div> </div>
</div> </div>
@@ -69,7 +116,7 @@
<!-- REMINDERS --> <!-- REMINDERS -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-calendar-o"></i> Bills <i class="fa fa-calendar-o"></i> <a href="{{route('bills.index')}}">Bills</a>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div id="bills-chart"></div> <div id="bills-chart"></div>
@@ -81,7 +128,7 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-money fa-fw"></i> <i class="fa fa-money fa-fw"></i>
<a href="{{route('accounts.show',$data[1]->id)}}">{{{$data[1]->name}}}</a> <a href="{{route('accounts.show',$data[1]->id)}}">{{{$data[1]->name}}}</a> ({!! Amount::format(Steam::balance($data[1])) !!})
<!-- ACTIONS MENU --> <!-- ACTIONS MENU -->
@@ -94,7 +141,7 @@
<ul class="dropdown-menu pull-right" role="menu"> <ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('transactions.create','withdrawal')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-left fa-fw"></i> New withdrawal</a></li> <li><a href="{{route('transactions.create','withdrawal')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-left fa-fw"></i> New withdrawal</a></li>
<li><a href="{{route('transactions.create','deposit')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-right fa-fw"></i> New deposit</a></li> <li><a href="{{route('transactions.create','deposit')}}?account_id={{{$data[1]->id}}}"><i class="fa fa-long-arrow-right fa-fw"></i> New deposit</a></li>
<li><a href="{{route('transactions.create','transfer')}}?account_from_id={{{$data[1]->id}}}"><i class="fa fa-arrows-h fa-fw"></i> New transfer</a></li> <li><a href="{{route('transactions.create','transfer')}}?account_from_id={{{$data[1]->id}}}"><i class="fa fa-fw fa-exchange"></i> New transfer</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -14,13 +14,12 @@
// {{{$subTitle}}} // {{{$subTitle}}}
@endif @endif
</title> </title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,700,300italic" type="ext/css" media="all" />
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" type="text/css" media="all" /> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="bootstrap/css/bootstrap-theme.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="css/metisMenu.min.css" type="text/css" media="all" /> <link rel="stylesheet" href="css/metisMenu.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="css/sb-admin-2.css" type="text/css" media="all" /> <link rel="stylesheet" href="css/sb-admin-2.css" type="text/css" media="all" />
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto2" type="text/css" media="all" />
<!-- date range --> <!-- date range -->
<link rel="stylesheet" href="css/daterangepicker-bs3.css" type="text/css" media="all" /> <link rel="stylesheet" href="css/daterangepicker-bs3.css" type="text/css" media="all" />
@@ -29,11 +28,6 @@
@yield('styles') @yield('styles')
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">

View File

@@ -17,8 +17,8 @@
<tr> <tr>
<td> <td>
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a class="btn btn-default btn-xs" href="{{route('accounts.edit',$account->id)}}"><span class="glyphicon glyphicon-pencil"></span></a> <a class="btn btn-default btn-xs" href="{{route('accounts.edit',$account->id)}}"><i class="fa fa-fw fa-pencil"></i></a>
<a class="btn btn-danger btn-xs" href="{{route('accounts.delete',$account->id)}}"><span class="glyphicon glyphicon-trash"></span></a> <a class="btn btn-danger btn-xs" href="{{route('accounts.delete',$account->id)}}"><i class="fa fa-fw fa-trash-o"></i></a>
</div> </div>
</td> </td>
<td><a href="{{route('accounts.show',$account->id)}}">{{{$account->name}}}</a></td> <td><a href="{{route('accounts.show',$account->id)}}">{{{$account->name}}}</a></td>

View File

@@ -14,8 +14,8 @@
<tr> <tr>
<td> <td>
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="{{route('bills.edit',$entry->id)}}" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-pencil"></span></a> <a href="{{route('bills.edit',$entry->id)}}" class="btn btn-default btn-xs"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{{route('bills.delete',$entry->id)}}" class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-trash"></span></a> <a href="{{route('bills.delete',$entry->id)}}" class="btn btn-danger btn-xs"><i class="fa fa-fw fa-trash-o"></i></a>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -13,8 +13,8 @@
<tr> <tr>
<td> <td>
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="{{route('categories.edit',$category->id)}}" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-pencil"></span></a> <a href="{{route('categories.edit',$category->id)}}" class="btn btn-default btn-xs"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{{route('categories.delete',$category->id)}}" class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-trash"></span></a> <a href="{{route('categories.delete',$category->id)}}" class="btn btn-danger btn-xs"><i class="fa fa-fw fa-trash-o"></i></a>
</div> </div>
</td> </td>
<td> <td>

View File

@@ -1,8 +1,8 @@
@if(is_object($journals) && method_exists($journals, 'render')) @if(is_object($journals) && method_exists($journals, 'render'))
{!! $journals->render() !!} {!! $journals->render() !!}
@endif @endif
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered sortable-table">
<tr> <tr class="ignore">
<th colspan="2">&nbsp;</th> <th colspan="2">&nbsp;</th>
<th>Description</th> <th>Description</th>
<th>Amount</th> <th>Amount</th>
@@ -21,10 +21,10 @@
</tr> </tr>
@foreach($journals as $journal) @foreach($journals as $journal)
@if(!isset($journal->transactions[1]) || !isset($journal->transactions[0])) @if(!isset($journal->transactions[1]) || !isset($journal->transactions[0]))
<tr> <tr class="ignore">
<td> <td>
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span></a> <a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a>
</div> </div>
</td> </td>
<td>&nbsp;</td> <td>&nbsp;</td>
@@ -32,11 +32,14 @@
<td colspan="7"><em>Invalid journal: Found {{$journal->transactions()->count()}} transaction(s)</td> <td colspan="7"><em>Invalid journal: Found {{$journal->transactions()->count()}} transaction(s)</td>
</tr> </tr>
@else @else
<tr> <tr class="drag" data-date="{{$journal->date->format('Y-m-d')}}" data-id="{{$journal->id}}">
<td> <td>
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="{{route('transactions.edit',$journal->id)}}" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span></a> @if($sorting === true)
<a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span></a> <a href="#" class="handle btn btn-default btn-xs"><i class="fa fa-fw fa-arrows-v"></i></a>
@endif
<a href="{{route('transactions.edit',$journal->id)}}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a>
</div> </div>
</td> </td>
<td> <td>
@@ -47,7 +50,7 @@
<span class="glyphicon glyphicon-arrow-right" title="Deposit"></span> <span class="glyphicon glyphicon-arrow-right" title="Deposit"></span>
@endif @endif
@if($journal->transactiontype->type == 'Transfer') @if($journal->transactiontype->type == 'Transfer')
<span class="glyphicon glyphicon-resize-full" title="Transfer"></span> <i class="fa fa-fw fa-exchange" title="Transfer"></i>
@endif @endif
@if($journal->transactiontype->type == 'Opening balance') @if($journal->transactiontype->type == 'Opening balance')
<span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span> <span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span>

View File

@@ -2,6 +2,7 @@
@foreach($transactions as $journal) @foreach($transactions as $journal)
<a class="list-group-item" title="{{$journal->date->format('jS M Y')}}" href="{{route('transactions.show',$journal->id)}}"> <a class="list-group-item" title="{{$journal->date->format('jS M Y')}}" href="{{route('transactions.show',$journal->id)}}">
@if(is_null($journal->type))
@if($journal->transactiontype->type == 'Withdrawal') @if($journal->transactiontype->type == 'Withdrawal')
<i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i> <i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i>
@endif @endif
@@ -9,25 +10,24 @@
<i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i> <i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i>
@endif @endif
@if($journal->transactiontype->type == 'Transfer') @if($journal->transactiontype->type == 'Transfer')
<i class="fa fa-arrows-h fa-fw" title="Transfer"></i> <i class="fa fa-fw fa-exchange" title="Transfer"></i>
@endif
@else
@if($journal->type == 'Withdrawal')
<i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i>
@endif
@if($journal->type == 'Deposit')
<i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i>
@endif
@if($journal->type == 'Transfer')
<i class="fa fa-fw fa-exchange" title="Transfer"></i>
@endif
@endif @endif
{{{$journal->description}}} {{{$journal->description}}}
<span class="pull-right small"> <span class="pull-right small">
@if(isset($account)) {!! Amount::formatJournal($journal) !!}
@foreach($journal->transactions as $index => $t)
@if($t->account_id == $account->id)
{!! Amount::formatTransaction($t) !!}
@endif
@endforeach
@else
@foreach($journal->transactions as $index => $t)
@if($index == 0)
{!! Amount::formatTransaction($t) !!}
@endif
@endforeach
@endif
</span> </span>
</a> </a>

View File

@@ -24,8 +24,10 @@
</div> </div>
<div class="panel-footer"> <div class="panel-footer">
<div class="btn-group"> <div class="btn-group">
@if($reminder->active === true) @if($reminder->notnow !== true)
<a class="btn btn-warning" href="{{route('reminders.dismiss',$reminder->id)}}">Dismiss</a> <a class="btn btn-warning" href="{{route('reminders.dismiss',$reminder->id)}}">Dismiss</a>
@endif
@if($reminder->active === true)
<a class="btn btn-success" href="{{route('reminders.act',$reminder->id)}}">Act</a> <a class="btn btn-success" href="{{route('reminders.act',$reminder->id)}}">Act</a>
@endif @endif
</div> </div>

View File

@@ -141,7 +141,7 @@
<a @if($isDeposit)class="active"@endif href="{{route('transactions.index','deposit')}}"><i class="fa fa-long-arrow-right fa-fw"></i> Revenue / income</a> <a @if($isDeposit)class="active"@endif href="{{route('transactions.index','deposit')}}"><i class="fa fa-long-arrow-right fa-fw"></i> Revenue / income</a>
</li> </li>
<li> <li>
<a @if($isTransfer)class="active"@endif href="{{route('transactions.index','transfers')}}"><i class="fa fa-arrows-h fa-fw"></i> Transfers</a> <a @if($isTransfer)class="active"@endif href="{{route('transactions.index','transfers')}}"><i class="fa fa-fw fa-exchange" title="Transfer"></i> Transfers</a>
</li> </li>
</ul> </ul>
@@ -184,7 +184,7 @@
<a @if($isDeposit)class="active"@endif href="{{route('transactions.create','deposit')}}"><i class="fa fa-long-arrow-right fa-fw"></i> Deposit</a> <a @if($isDeposit)class="active"@endif href="{{route('transactions.create','deposit')}}"><i class="fa fa-long-arrow-right fa-fw"></i> Deposit</a>
</li> </li>
<li> <li>
<a @if($isTransfer)class="active"@endif href="{{route('transactions.create','transfer')}}"><i class="fa fa-arrows-h fa-fw"></i> Transfer</a> <a @if($isTransfer)class="active"@endif href="{{route('transactions.create','transfer')}}"><i class="fa fa-fw fa-exchange" title="Transfer"></i> Transfer</a>
</li> </li>
<li> <li>
<a @if($isBill)class="active"@endif href="{{route('bills.create')}}"><i class="fa fa-calendar-o fa-fw"></i> Bill</a> <a @if($isBill)class="active"@endif href="{{route('bills.create')}}"><i class="fa fa-calendar-o fa-fw"></i> Bill</a>

View File

@@ -8,12 +8,13 @@
</p> </p>
</div> </div>
</div> </div>
<div class="row"> <div class="row" id="sortable">
@foreach($piggyBanks as $piggyBank) @foreach($piggyBanks as $piggyBank)
<div class="col-lg-3 col-md-4 col-sm-12 col-xs-12"> <div class="col-lg-3 col-md-4 col-sm-12 col-xs-12" data-id="{{$piggyBank->id}}">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-fw fa-rocket"></i> <a href="{{route('piggy-banks.show',$piggyBank->id)}}" title="{{{$piggyBank->name}}}">{{{$piggyBank->name}}}</a> <i class="loadSpin"></i>
<i class="fa fa-fw fa-bars handle"></i> <a href="{{route('piggy-banks.show',$piggyBank->id)}}" title="{{{$piggyBank->order}}}">{{{$piggyBank->name}}}</a>
<!-- ACTIONS MENU --> <!-- ACTIONS MENU -->
<div class="pull-right"> <div class="pull-right">
@@ -62,34 +63,15 @@
</div> </div>
<!-- One block -->
<!-- <div class="col-lg-1 col-md-4 col-sm-4 col-xs-4">
{!! Amount::format($piggyBank->savedSoFar,true) !!}
</div> -->
<!-- One block -->
<!-- One block -->
<!-- <div class="col-lg-1 col-md-6 col-sm-6 col-xs-6">
{!! Amount::format($piggyBank->targetamount,true) !!}
</div> -->
<!-- One block -->
<!-- <div class="col-lg-1 col-md-6 col-sm-6 col-xs-6">
@if($piggyBank->leftToSave > 0)
{!! Amount::format($piggyBank->leftToSave) !!}
@endif
</div> -->
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-4 col-md-4 col-sm-4 col-xs4"> <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
<span title="Saved so far">{!! Amount::format($piggyBank->savedSoFar,true) !!}</span> <span title="Saved so far">{!! Amount::format($piggyBank->savedSoFar,true) !!}</span>
</div> </div>
<div class="col-lg-4 col-md-4 col-sm-4 col-xs4"> <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4" style="text-align: center;">
<span title="Target amount">{!! Amount::format($piggyBank->targetamount,true) !!}</span> <span title="Target amount">{!! Amount::format($piggyBank->targetamount,true) !!}</span>
</div> </div>
<div class="col-lg-4 col-md-4 col-sm-4 col-xs4"> <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4" style="text-align: right;">
@if($piggyBank->leftToSave > 0) @if($piggyBank->leftToSave > 0)
<span title="Left to save">{!! Amount::format($piggyBank->leftToSave) !!}</span> <span title="Left to save">{!! Amount::format($piggyBank->leftToSave) !!}</span>
@endif @endif
@@ -148,5 +130,9 @@
@stop @stop
@section('scripts') @section('scripts')
<script type="text/javascript">
var token = "{{csrf_token()}}";
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript" src="js/piggy-banks.js"></script> <script type="text/javascript" src="js/piggy-banks.js"></script>
@stop @stop

View File

@@ -2,7 +2,8 @@
<table class="table table-bordered table-striped table-condensed"> <table class="table table-bordered table-striped table-condensed">
@foreach($journals as $journal) @foreach($journals as $journal)
<tr> <tr>
<td><a title="Unlink" data-id="{{$journal->id}}" data-parent="{{$parent->id}}" class="btn unrelate btn-xs btn-default" href="#"><span class="glyphicon glyphicon-resize-full"></span></a></td> <td>
<a title="Unlink" data-id="{{$journal->id}}" data-parent="{{$parent->id}}" class="btn unrelate btn-xs btn-default" href="#"><i class="fa fa-fw fa-expand"></i></a></td>
<td> <td>
@if($journal->transactiontype->type == 'Withdrawal') @if($journal->transactiontype->type == 'Withdrawal')
<i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i> <i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i>
@@ -11,7 +12,7 @@
<i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i> <i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i>
@endif @endif
@if($journal->transactiontype->type == 'Transfer') @if($journal->transactiontype->type == 'Transfer')
<i class="fa fa-arrows-h fa-fw" title="Transfer"></i> <i class="fa fa-fw fa-exchange" title="Transfer"></i>
@endif @endif
</td> </td>
<td>{{$journal->date->format('jS M Y')}}</td> <td>{{$journal->date->format('jS M Y')}}</td>

View File

@@ -3,7 +3,7 @@
<table class="table table-bordered table-striped table-condensed"> <table class="table table-bordered table-striped table-condensed">
@foreach($journals as $journal) @foreach($journals as $journal)
<tr> <tr>
<td><a title="Link" data-id="{{$journal->id}}" data-parent="{{$parent->id}}" class="btn relate btn-xs btn-default" href="#"><span class="glyphicon glyphicon-resize-small"></span></a></td> <td><a title="Link" data-id="{{$journal->id}}" data-parent="{{$parent->id}}" class="btn relate btn-xs btn-default" href="#"><i class="fa fa-fw fa-expand"></i></a></td>
<td> <td>
@if($journal->transactiontype->type == 'Withdrawal') @if($journal->transactiontype->type == 'Withdrawal')
<i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i> <i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i>
@@ -12,7 +12,7 @@
<i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i> <i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i>
@endif @endif
@if($journal->transactiontype->type == 'Transfer') @if($journal->transactiontype->type == 'Transfer')
<i class="fa fa-arrows-h fa-fw" title="Transfer"></i> <i class="fa fa-fw fa-exchange" title="Transfer"></i>
@endif @endif
</td> </td>
<td>{{$journal->date->format('jS M Y')}}</td> <td>{{$journal->date->format('jS M Y')}}</td>

View File

@@ -5,7 +5,7 @@
<h4 class="modal-title">No budget bla bla.</h4> <h4 class="modal-title">No budget bla bla.</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@include('list.journals-full') @include('list.journals-full',['sorting' => false])
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>

View File

@@ -77,7 +77,7 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-arrows-h fa-fw"></i> <i class="fa fa-fw fa-exchange" title="Transfer"></i>
Income vs. expense Income vs. expense
</div> </div>
<?php <?php

View File

@@ -10,7 +10,7 @@
<i class="fa fa-repeat"></i> Transactions ({{$result['transactions']->count()}}) <i class="fa fa-repeat"></i> Transactions ({{$result['transactions']->count()}})
</div> </div>
<div class="panel-body"> <div class="panel-body">
@include('list.journals-small',['journals' => $result['transactions']]) @include('list.journals-tiny',['transactions' => $result['transactions']])
</div> </div>
</div> </div>
</div> </div>

View File

@@ -7,10 +7,18 @@
<div class="panel-heading"> <div class="panel-heading">
<i class="fa {{$subTitleIcon}}"></i> {{{$subTitle}}} <i class="fa {{$subTitleIcon}}"></i> {{{$subTitle}}}
</div> </div>
@include('list.journals-full') @include('list.journals-full',['sorting' => false])
</div> </div>
</div> </div>
</div> </div>
@stop
@section('scripts')
<script type="text/javascript">
var token = "{{csrf_token()}}";
</script>
<script src="js/jquery-ui.min.js" type="text/javascript"></script>
<script src="js/transactions.js" type="text/javascript"></script>
@stop @stop

View File

@@ -145,7 +145,7 @@
<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="btn-group"> <div class="btn-group">
<a class="btn btn-default" href="{{route('transactions.edit',$journal->id)}}"><span class="glyphicon glyphicon-pencil"></span> Edit</a> <a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> Delete</a> <a class="btn btn-default" href="{{route('transactions.edit',$journal->id)}}"><i class="fa fa-fw fa-pencil"></i> Edit</a> <a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> Delete</a>
</div> </div>
</div> </div>
</div> </div>

1
tests/database/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.sqlite