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/*
.env
clover.xml
node_modules/

View File

@@ -1,4 +1,6 @@
language: php
sudo: false
php:
- 5.5
@@ -13,9 +15,11 @@ install:
- composer install
- php artisan env
- mv -v .env.testing .env
- touch tests/database/db.sqlite
- php artisan migrate --seed
script:
- phpunit
- phpunit --debug
after_script:
- 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)

View File

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

View File

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

View File

@@ -25,10 +25,7 @@ class ReminderHelper implements ReminderHelperInterface
*/
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end)
{
$reminder = Auth::user()->reminders()
->where('remindersable_id', $piggyBank->id)
->onDates($start, $end)
->first();
$reminder = Auth::user()->reminders()->where('remindersable_id', $piggyBank->id)->onDates($start, $end)->first();
if (is_null($reminder)) {
if (!is_null($piggyBank->targetdate)) {
@@ -134,7 +131,8 @@ class ReminderHelper implements ReminderHelperInterface
{
/** @var PiggyBank $piggyBank */
$piggyBank = $reminder->remindersable;
if(is_null($piggyBank)) {
if (is_null($piggyBank)) {
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('otherJournals.deleted_at')
->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')
->get(
[

View File

@@ -168,7 +168,7 @@ class AccountController extends Controller
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$journals->setPath('accounts/show/'.$account->id);
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!');
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'));

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
$email = $this->auth->user()->email;

View File

@@ -138,7 +138,12 @@ class BillController extends Controller
*/
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);
$hideBill = true;
@@ -210,42 +215,6 @@ class BillController extends Controller
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')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a budget in ' . $start->format('F Y');
@@ -152,6 +154,12 @@ class BudgetController extends Controller
return Redirect::route('budgets.index');
}
/**
* @param BudgetFormRequest $request
* @param BudgetRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
@@ -204,6 +212,10 @@ class BudgetController extends Controller
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('budgets.edit', $budget->id);
}
return Redirect::route('budgets.index');
}

View File

@@ -88,7 +88,11 @@ class CategoryController extends Controller
$categories->each(
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) {
$category->lastActivity = $latest->date;
}
@@ -111,7 +115,9 @@ class CategoryController extends Controller
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order','ASC')
->orderBy('transaction_journals.id','DESC')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
@@ -129,7 +135,13 @@ class CategoryController extends Controller
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$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.*']
);
$count = $category->transactionJournals()->count();
@@ -154,6 +166,10 @@ class CategoryController extends Controller
$category = $repository->store($categoryData);
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) {
return Redirect::route('categories.create');
@@ -181,6 +197,10 @@ class CategoryController extends Controller
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');
}

View File

@@ -148,7 +148,7 @@ class CurrencyController extends Controller
Session::flash('success', 'Currency "' . $currency->name . '" created');
if (intval(Input::get('create_another')) === 1) {
return Redirect::route('currency.create');
return Redirect::route('currency.create')->withInput();
}
return Redirect::route('currency.index');

View File

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

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Cache;
use Carbon\Carbon;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -64,6 +63,7 @@ class HomeController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts();
foreach ($accounts as $account) {
$set = $repository->getFrontpageTransactions($account, $start, $end);
@@ -74,7 +74,7 @@ class HomeController extends Controller
// 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 $this
*/
public function index(AccountRepositoryInterface $repository)
{
/** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->get();
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->orderBy('order', 'ASC')->get();
$accounts = [];
/** @var PiggyBank $piggyBank */
@@ -175,6 +174,22 @@ class PiggyBankController extends Controller
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
@@ -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)
{

View File

@@ -12,6 +12,7 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Redirect;
use Session;
use View;
use Input;
/**
* 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)
{
@@ -159,6 +163,7 @@ class RepeatedExpenseController extends Controller
'reminder' => $request->get('reminder'),
'skip' => intval($request->get('skip')),
'rep_every' => intval($request->get('rep_every')),
'rep_length' => $request->get('rep_length'),
'rep_times' => intval($request->get('rep_times')),
];
@@ -166,6 +171,11 @@ class RepeatedExpenseController extends Controller
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');
}
@@ -194,6 +204,10 @@ class RepeatedExpenseController extends Controller
$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) . '".');
return Redirect::route('repeated.index');

View File

@@ -15,7 +15,7 @@ use Input;
use Redirect;
use Session;
use View;
use Response;
/**
* Class TransactionController
@@ -180,29 +180,33 @@ class TransactionController extends Controller
case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses';
//$journals = $this->_repository->getWithdrawalsPaginated(50);
$types = ['Withdrawal'];
$types = ['Withdrawal'];
break;
case 'revenue':
case 'deposit':
$subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits';
// $journals = $this->_repository->getDepositsPaginated(50);
$types = ['Deposit'];
$types = ['Deposit'];
break;
case 'transfer':
case 'transfers':
$subTitleIcon = 'fa-arrows-h';
$subTitleIcon = 'fa-exchange';
$subTitle = 'Transfers';
//$journals = $this->_repository->getTransfersPaginated(50);
$types = ['Transfer'];
$types = ['Transfer'];
break;
}
$page = intval(\Input::get('page'));
$offset = $page > 0 ? ($page - 1) * 50 : 0;
$set = Auth::user()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get(
$set = Auth::user()->
transactionJournals()->
transactionTypes($types)->
withRelevantData()->take(50)->offset($offset)
->orderBy('date', 'DESC')
->orderBy('order','ASC')
->orderBy('id','DESC')
->get(
['transaction_journals.*']
);
$count = Auth::user()->transactionJournals()->transactionTypes($types)->count();
@@ -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
*
@@ -225,9 +250,11 @@ class TransactionController extends Controller
$t->before = floatval(
$t->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))->where(
'transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s')
)->where('transaction_journals.id', '!=', $journal->id)->sum('transactions.amount')
)
->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
->where('transaction_journals.order','>=',$journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->sum('transactions.amount')
);
$t->after = $t->before + $t->amount;
}
@@ -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)
{
$journalData = [
@@ -284,7 +317,6 @@ class TransactionController extends Controller
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @return $this
* @throws FireflyException
*/
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.index', $journalData['what']);
}

View File

@@ -37,6 +37,7 @@ class Kernel extends HttpKernel
'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated',
'range' => 'FireflyIII\Http\Middleware\Range',
'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)
{
if ($this->auth->check()) {
if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon;
@@ -67,15 +67,14 @@ class Reminders
}
}
// delete invalid reminders
$reminders = $this->auth->user()->reminders()->get();
foreach($reminders as $reminder) {
if(is_null($reminder->remindersable)) {
$reminder->delete();
}
$set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get(
['reminders.id']
);
foreach ($set as $reminder) {
$reminder->delete();
}
// get and list active reminders:
$reminders = $this->auth->user()->reminders()->today()->get();
$reminders->each(

View File

@@ -31,9 +31,9 @@ class AccountFormRequest extends Request
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$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'))) {
$nameRule = 'required|between:1,100';
$nameRule = 'required|between:1,100|belongsToUser:accounts|uniqueForUser:'.Input::get('id');
}
return [
@@ -46,4 +46,4 @@ class AccountFormRequest extends Request
'what' => 'in:' . $types
];
}
}
}

View File

@@ -3,8 +3,10 @@
namespace FireflyIII\Http\Requests;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Input;
use Navigation;
/**
* Class PiggyBankFormRequest
@@ -36,8 +38,14 @@ class PiggyBankFormRequest extends Request
if (intval(Input::get('repeats')) == 1) {
$targetDateRule = 'required|date|after:' . date('Y-m-d');
// switch on rep_every, make sure it's not too far away.
if (!is_null(Input::get('rep_length'))) {
$end = Navigation::addPeriod(new Carbon, Input::get('rep_length'), 0);
$targetDateRule .= '|before:' . $end->format('Y-m-d');
}
}
$rules = [
'repeats' => 'required|boolean',
'name' => $nameRule,

View File

@@ -156,7 +156,7 @@ Route::controllers(
* Home Controller
*/
Route::group(
['middleware' => ['auth', 'range', 'reminders']], function () {
['middleware' => ['auth', 'range', 'reminders','piggybanks']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
@@ -279,6 +279,7 @@ Route::group(
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/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
@@ -365,6 +366,7 @@ Route::group(
);
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/reorder', ['uses' => 'TransactionController@reorder', 'as' => 'transactions.reorder']);
/**
* Auth\Auth Controller

View File

@@ -18,7 +18,7 @@ class Account extends Model
= [
'user_id' => 'required|exists:users,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'
];

View File

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

View File

@@ -14,7 +14,9 @@ class PiggyBank extends Model
{
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
@@ -24,15 +26,6 @@ class PiggyBank extends Model
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
*
@@ -40,10 +33,10 @@ class PiggyBank extends Model
*/
public function currentRelevantRep()
{
if ($this->currentRep) {
if (!is_null($this->currentRep)) {
return $this->currentRep;
}
if ($this->repeats == 0) {
if (intval($this->repeats) === 0) {
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
$this->currentRep = $rep;
@@ -104,6 +97,16 @@ class PiggyBank extends Model
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
*/

View File

@@ -37,15 +37,27 @@ class PiggyBankRepetition extends Model
{
return $query->where(
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');
})
->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');
});
}
/**
* @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(
function (PiggyBank $piggyBank) {
$repetition = new PiggyBankRepetition;

View File

@@ -18,6 +18,7 @@ use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Session;
use Steam;
/**
* 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 Carbon $start
* @param Carbon $end
@@ -74,15 +79,17 @@ class AccountRepository implements AccountRepositoryInterface
{
return Auth::user()
->transactionjournals()
->with(['transactions', 'transactioncurrency', 'transactiontype'])
->with(['transactions'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.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)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->take(10)
->get(['transaction_journals.*']);
->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']);
}
/**
@@ -99,7 +106,9 @@ class AccountRepository implements AccountRepositoryInterface
->withRelevantData()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.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->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
*
@@ -236,12 +287,14 @@ class AccountRepository implements AccountRepositoryInterface
'active' => $data['active'] === true ? true : false,
]
);
if (!$newAccount->isValid()) {
// does the account already exist?
$existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first();
if (!$existingAccount) {
Log::error('Account create error: ' . $newAccount->getErrors()->toJson());
var_dump($newAccount->getErrors()->toArray());
App::abort(500);
}
$newAccount = $existingAccount;
}

View File

@@ -78,4 +78,11 @@ interface AccountRepositoryInterface
* @return float
*/
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;
$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();

View File

@@ -11,6 +11,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log;
/**
* Class JournalRepository

View File

@@ -2,9 +2,8 @@
namespace FireflyIII\Repositories\PiggyBank;
use Amount;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
@@ -88,6 +87,39 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
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
*
@@ -132,5 +164,4 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $piggyBank;
}
}

View File

@@ -37,6 +37,23 @@ interface PiggyBankRepositoryInterface
*/
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 FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use Preferences as Prefs;
/**
@@ -30,19 +31,27 @@ class Amount
}
/**
* @param Transaction|\Transaction $transaction
* @param bool $coloured
*
* @return string
*/
public function formatTransaction(Transaction $transaction, $coloured = true)
public function getCurrencySymbol()
{
$symbol = $transaction->transactionJournal->transactionCurrency->symbol;
$amount = floatval($transaction->amount);
if (defined('FFCURRENCYSYMBOL')) {
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 string
*
* @param TransactionJournal $journal
*/
public function getCurrencySymbol()
public function formatJournal(TransactionJournal $journal, $coloured = true)
{
if (defined('FFCURRENCYSYMBOL')) {
return FFCURRENCYSYMBOL;
$showPositive = true;
if (is_null($journal->symbol)) {
$symbol = $journal->transactionCurrency->symbol;
} else {
$symbol = $journal->symbol;
}
if (\Cache::has('FFCURRENCYSYMBOL')) {
define('FFCURRENCYSYMBOL', \Cache::get('FFCURRENCYSYMBOL'));
$amount = 0;
return FFCURRENCYSYMBOL;
if (is_null($journal->type)) {
$type = $journal->transactionType->type;
} else {
$type = $journal->type;
}
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
if ($type == 'Withdrawal') {
$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;
use Amount as Amt;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Input;
use Session;
use View;
use Amount as Amt;
/**
* Class ExpandedForm
@@ -96,24 +96,14 @@ class ExpandedForm
public function getHolderClasses($name)
{
/*
* Get errors, warnings and successes from session:
* Get errors from session:
*/
/** @var MessageBag $errors */
$errors = Session::get('errors');
$errors = Session::get('errors');
$classes = 'form-group';
/** @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';
break;
if (!is_null($errors) && $errors->has($name)) {
$classes = 'form-group has-error has-feedback';
}
return $classes;

View File

@@ -3,11 +3,12 @@
namespace FireflyIII\Support\Search;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Auth;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/**
* Class Search
@@ -23,7 +24,7 @@ class Search implements SearchInterface
*/
public function searchAccounts(array $words)
{
return \Auth::user()->accounts()->with('accounttype')->where(
return Auth::user()->accounts()->with('accounttype')->where(
function (EloquentBuilder $q) use ($words) {
foreach ($words as $word) {
$q->orWhere('name', 'LIKE', '%' . e($word) . '%');
@@ -40,7 +41,7 @@ class Search implements SearchInterface
public function searchBudgets(array $words)
{
/** @var Collection $set */
$set = \Auth::user()->budgets()->get();
$set = Auth::user()->budgets()->get();
$newSet = $set->filter(
function (Budget $b) use ($words) {
$found = 0;
@@ -65,7 +66,7 @@ class Search implements SearchInterface
public function searchCategories(array $words)
{
/** @var Collection $set */
$set = \Auth::user()->categories()->get();
$set = Auth::user()->categories()->get();
$newSet = $set->filter(
function (Category $c) use ($words) {
$found = 0;
@@ -101,12 +102,39 @@ class Search implements SearchInterface
*/
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) {
foreach ($words as $word) {
$q->orWhere('description', 'LIKE', '%' . e($word) . '%');
}
}
)->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 Carbon\Carbon;
use Config;
use DB;
use FireflyIII\Models\AccountType;
use Illuminate\Validation\Validator;
use Input;
use Navigation;
/**
@@ -34,6 +37,13 @@ class FireflyValidator extends Validator
}
/**
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validatePiggyBankReminder($attribute, $value, $parameters)
{
$array = $this->data;
@@ -51,14 +61,65 @@ class FireflyValidator extends Validator
return true;
}
$nextReminder = Navigation::addPeriod($startDate, $array['reminder'],0);
$nextReminder = Navigation::addPeriod($startDate, $array['reminder'], 0);
// reminder is beyond target?
if($nextReminder > $targetDate) {
if ($nextReminder > $targetDate) {
return false;
}
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 $value
@@ -69,6 +130,7 @@ class FireflyValidator extends Validator
public function validateUniqueForUser($attribute, $value, $parameters)
{
$query = DB::table($parameters[0])->where($parameters[1], $value);
$query->where('user_id', Auth::user()->id);
if (isset($paramers[2])) {
$query->where('id', '!=', $parameters[2]);
}

View File

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

View File

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

View File

@@ -18,14 +18,14 @@ class ChangesForV332 extends Migration {
Schema::table(
'accounts', function (Blueprint $table) {
$table->boolean('encrypted');
$table->boolean('encrypted')->default(0);
}
);
Schema::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()
{
User::create(['email' => 'reset@example.com', 'password' => 'functional', 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]);
User::create(['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'reset@example.com', 'password' => bcrypt('functional'), 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]);
User::create(['email' => 'functional@example.com', 'password' => bcrypt('functional'), 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => bcrypt('james'), 'reset' => null, 'remember_token' => null]);
}
/**
@@ -139,7 +139,7 @@ class TestDataSeeder extends Seeder
// create account meta:
$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']);
// var_dump($meta_a->toArray());
// var_dump($meta_b->toArray());

View File

@@ -21,11 +21,9 @@
</filter>
<!-- code coverage -->
<!--
<logging>
<log type="coverage-clover" target="./storage/coverage/clover.xml" charset="UTF-8" />
</logging>
-->
<php>
<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.
* 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.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
@@ -958,12 +958,24 @@ th {
.glyphicon-bitcoin:before {
content: "\e227";
}
.glyphicon-btc:before {
content: "\e227";
}
.glyphicon-xbt:before {
content: "\e227";
}
.glyphicon-yen:before {
content: "\00a5";
}
.glyphicon-jpy:before {
content: "\00a5";
}
.glyphicon-ruble:before {
content: "\20bd";
}
.glyphicon-rub:before {
content: "\20bd";
}
.glyphicon-scale:before {
content: "\e230";
}
@@ -1074,7 +1086,7 @@ html {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.42857143;
color: #333;
@@ -1161,6 +1173,9 @@ hr {
overflow: visible;
clip: auto;
}
[role="button"] {
cursor: pointer;
}
h1,
h2,
h3,
@@ -2564,10 +2579,13 @@ output {
.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
cursor: not-allowed;
background-color: #eee;
opacity: 1;
}
.form-control[disabled],
fieldset[disabled] .form-control {
cursor: not-allowed;
}
textarea.form-control {
height: auto;
}
@@ -2634,6 +2652,7 @@ input[type="search"] {
}
.radio-inline,
.checkbox-inline {
position: relative;
display: inline-block;
padding-left: 20px;
margin-bottom: 0;
@@ -2667,6 +2686,7 @@ fieldset[disabled] .checkbox label {
cursor: not-allowed;
}
.form-control-static {
min-height: 34px;
padding-top: 7px;
padding-bottom: 7px;
margin-bottom: 0;
@@ -2708,6 +2728,7 @@ select[multiple].form-group-sm .form-control {
}
.form-group-sm .form-control-static {
height: 30px;
min-height: 32px;
padding: 5px 10px;
font-size: 12px;
line-height: 1.5;
@@ -2744,6 +2765,7 @@ select[multiple].form-group-lg .form-control {
}
.form-group-lg .form-control-static {
height: 46px;
min-height: 38px;
padding: 10px 16px;
font-size: 18px;
line-height: 1.3333333;
@@ -3365,11 +3387,9 @@ input[type="button"].btn-block {
}
.collapse {
display: none;
visibility: hidden;
}
.collapse.in {
display: block;
visibility: visible;
}
tr.collapse.in {
display: table-row;
@@ -3397,7 +3417,7 @@ tbody.collapse.in {
height: 0;
margin-left: 2px;
vertical-align: middle;
border-top: 4px solid;
border-top: 4px dashed;
border-right: 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 {
display: none;
visibility: hidden;
}
.tab-content > .active {
display: block;
visibility: visible;
}
.nav-tabs .dropdown-menu {
margin-top: -1px;
@@ -4088,7 +4106,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn {
height: auto !important;
padding-bottom: 0;
overflow: visible !important;
visibility: visible !important;
}
.navbar-collapse.in {
overflow-y: visible;
@@ -4813,7 +4830,8 @@ a.label:focus {
position: relative;
top: -1px;
}
.btn-xs .badge {
.btn-xs .badge,
.btn-group-xs > .btn .badge {
top: 0;
padding: 1px 5px;
}
@@ -5645,10 +5663,10 @@ a.list-group-item-danger.active:focus {
height: 100%;
border: 0;
}
.embed-responsive.embed-responsive-16by9 {
.embed-responsive-16by9 {
padding-bottom: 56.25%;
}
.embed-responsive.embed-responsive-4by3 {
.embed-responsive-4by3 {
padding-bottom: 75%;
}
.well {
@@ -5707,7 +5725,7 @@ button.close {
right: 0;
bottom: 0;
left: 0;
z-index: 1040;
z-index: 1050;
display: none;
overflow: hidden;
-webkit-overflow-scrolling: touch;
@@ -5750,10 +5768,12 @@ button.close {
box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
}
.modal-backdrop {
position: absolute;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1040;
background-color: #000;
}
.modal-backdrop.fade {
@@ -5824,11 +5844,10 @@ button.close {
position: absolute;
z-index: 1070;
display: block;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px;
font-weight: normal;
line-height: 1.4;
visibility: visible;
filter: alpha(opacity=0);
opacity: 0;
}
@@ -5932,7 +5951,7 @@ button.close {
display: none;
max-width: 276px;
padding: 1px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.42857143;
@@ -6348,7 +6367,6 @@ button.close {
}
.hidden {
display: none !important;
visibility: hidden !important;
}
.affix {
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.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
@@ -17,7 +17,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: transition.js v3.3.2
* Bootstrap: transition.js v3.3.4
* http://getbootstrap.com/javascript/#transitions
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -77,7 +77,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: alert.js v3.3.2
* Bootstrap: alert.js v3.3.4
* http://getbootstrap.com/javascript/#alerts
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -96,7 +96,7 @@ if (typeof jQuery === 'undefined') {
$(el).on('click', dismiss, this.close)
}
Alert.VERSION = '3.3.2'
Alert.VERSION = '3.3.4'
Alert.TRANSITION_DURATION = 150
@@ -172,7 +172,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: button.js v3.3.2
* Bootstrap: button.js v3.3.4
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -192,7 +192,7 @@ if (typeof jQuery === 'undefined') {
this.isLoading = false
}
Button.VERSION = '3.3.2'
Button.VERSION = '3.3.4'
Button.DEFAULTS = {
loadingText: 'loading...'
@@ -289,7 +289,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: carousel.js v3.3.2
* Bootstrap: carousel.js v3.3.4
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -307,10 +307,10 @@ if (typeof jQuery === 'undefined') {
this.$element = $(element)
this.$indicators = this.$element.find('.carousel-indicators')
this.options = options
this.paused =
this.sliding =
this.interval =
this.$active =
this.paused = null
this.sliding = null
this.interval = null
this.$active = null
this.$items = null
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))
}
Carousel.VERSION = '3.3.2'
Carousel.VERSION = '3.3.4'
Carousel.TRANSITION_DURATION = 600
@@ -527,7 +527,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: collapse.js v3.3.2
* Bootstrap: collapse.js v3.3.4
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -544,7 +544,8 @@ if (typeof jQuery === 'undefined') {
var Collapse = function (element, options) {
this.$element = $(element)
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
if (this.options.parent) {
@@ -556,13 +557,12 @@ if (typeof jQuery === 'undefined') {
if (this.options.toggle) this.toggle()
}
Collapse.VERSION = '3.3.2'
Collapse.VERSION = '3.3.4'
Collapse.TRANSITION_DURATION = 350
Collapse.DEFAULTS = {
toggle: true,
trigger: '[data-toggle="collapse"]'
toggle: true
}
Collapse.prototype.dimension = function () {
@@ -700,7 +700,7 @@ if (typeof jQuery === 'undefined') {
var data = $this.data('bs.collapse')
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 (typeof option == 'string') data[option]()
})
@@ -731,7 +731,7 @@ if (typeof jQuery === 'undefined') {
var $target = getTargetFromTrigger($this)
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)
})
@@ -739,7 +739,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: dropdown.js v3.3.2
* Bootstrap: dropdown.js v3.3.4
* http://getbootstrap.com/javascript/#dropdowns
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -759,7 +759,7 @@ if (typeof jQuery === 'undefined') {
$(element).on('click.bs.dropdown', this.toggle)
}
Dropdown.VERSION = '3.3.2'
Dropdown.VERSION = '3.3.4'
Dropdown.prototype.toggle = function (e) {
var $this = $(this)
@@ -812,7 +812,7 @@ if (typeof jQuery === 'undefined') {
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)
if (!$items.length) return
@@ -901,7 +901,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: modal.js v3.3.2
* Bootstrap: modal.js v3.3.4
* http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -916,12 +916,15 @@ if (typeof jQuery === 'undefined') {
// ======================
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$backdrop =
this.isShown = null
this.scrollbarWidth = 0
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
if (this.options.remote) {
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.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.$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 () {
var transition = $.support.transition && that.$element.hasClass('fade')
@@ -977,7 +986,6 @@ if (typeof jQuery === 'undefined') {
.show()
.scrollTop(0)
if (that.options.backdrop) that.adjustBackdrop()
that.adjustDialog()
if (transition) {
@@ -993,7 +1001,7 @@ if (typeof jQuery === 'undefined') {
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ?
that.$element.find('.modal-dialog') // wait for modal to slide in
that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e)
})
@@ -1022,6 +1030,9 @@ if (typeof jQuery === 'undefined') {
.removeClass('in')
.attr('aria-hidden', true)
.off('click.dismiss.bs.modal')
.off('mouseup.dismiss.bs.modal')
this.$dialog.off('mousedown.dismiss.bs.modal')
$.support.transition && this.$element.hasClass('fade') ?
this.$element
@@ -1082,13 +1093,18 @@ if (typeof jQuery === 'undefined') {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.prependTo(this.$element)
.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus.call(this.$element[0])
: this.hide.call(this)
}, this))
.appendTo(this.$body)
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (this.ignoreBackdropClick) {
this.ignoreBackdropClick = false
return
}
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus()
: this.hide()
}, this))
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
Modal.prototype.handleUpdate = function () {
if (this.options.backdrop) this.adjustBackdrop()
this.adjustDialog()
}
Modal.prototype.adjustBackdrop = function () {
this.$backdrop
.css('height', 0)
.css('height', this.$element[0].scrollHeight)
}
Modal.prototype.adjustDialog = function () {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
@@ -1150,17 +1159,23 @@ if (typeof jQuery === 'undefined') {
}
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()
}
Modal.prototype.setScrollbar = function () {
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)
}
Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', '')
this.$body.css('padding-right', this.originalBodyPad)
}
Modal.prototype.measureScrollbar = function () { // thx walsh
@@ -1226,7 +1241,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: tooltip.js v3.3.2
* Bootstrap: tooltip.js v3.3.4
* http://getbootstrap.com/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
@@ -1242,17 +1257,17 @@ if (typeof jQuery === 'undefined') {
// ===============================
var Tooltip = function (element, options) {
this.type =
this.options =
this.enabled =
this.timeout =
this.hoverState =
this.type = null
this.options = null
this.enabled = null
this.timeout = null
this.hoverState = null
this.$element = null
this.init('tooltip', element, options)
}
Tooltip.VERSION = '3.3.2'
Tooltip.VERSION = '3.3.4'
Tooltip.TRANSITION_DURATION = 150
@@ -1279,6 +1294,10 @@ if (typeof jQuery === 'undefined') {
this.options = this.getOptions(options)
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(' ')
for (var i = triggers.length; i--;) {
@@ -1499,10 +1518,10 @@ if (typeof jQuery === 'undefined') {
this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
}
Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
this.arrow()
.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
.css(isHorizontal ? 'top' : 'left', '')
.css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
.css(isVertical ? 'top' : 'left', '')
}
Tooltip.prototype.setContent = function () {
@@ -1515,7 +1534,7 @@ if (typeof jQuery === 'undefined') {
Tooltip.prototype.hide = function (callback) {
var that = this
var $tip = this.tip()
var $tip = $(this.$tip)
var e = $.Event('hide.bs.' + this.type)
function complete() {
@@ -1532,7 +1551,7 @@ if (typeof jQuery === 'undefined') {
$tip.removeClass('in')
$.support.transition && this.$tip.hasClass('fade') ?
$.support.transition && $tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
@@ -1676,7 +1695,7 @@ if (typeof jQuery === 'undefined') {
var data = $this.data('bs.tooltip')
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 (typeof option == 'string') data[option]()
})
@@ -1699,7 +1718,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: popover.js v3.3.2
* Bootstrap: popover.js v3.3.4
* http://getbootstrap.com/javascript/#popovers
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -1719,7 +1738,7 @@ if (typeof jQuery === 'undefined') {
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, {
placement: 'right',
@@ -1775,11 +1794,6 @@ if (typeof jQuery === 'undefined') {
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
// =========================
@@ -1790,7 +1804,7 @@ if (typeof jQuery === 'undefined') {
var data = $this.data('bs.popover')
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 (typeof option == 'string') data[option]()
})
@@ -1813,7 +1827,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: scrollspy.js v3.3.2
* Bootstrap: scrollspy.js v3.3.4
* http://getbootstrap.com/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -1828,10 +1842,8 @@ if (typeof jQuery === 'undefined') {
// ==========================
function ScrollSpy(element, options) {
var process = $.proxy(this.process, this)
this.$body = $('body')
this.$scrollElement = $(element).is('body') ? $(window) : $(element)
this.$body = $(document.body)
this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
this.selector = (this.options.target || '') + ' .nav li > a'
this.offsets = []
@@ -1839,12 +1851,12 @@ if (typeof jQuery === 'undefined') {
this.activeTarget = null
this.scrollHeight = 0
this.$scrollElement.on('scroll.bs.scrollspy', process)
this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
this.refresh()
this.process()
}
ScrollSpy.VERSION = '3.3.2'
ScrollSpy.VERSION = '3.3.4'
ScrollSpy.DEFAULTS = {
offset: 10
@@ -1855,20 +1867,19 @@ if (typeof jQuery === 'undefined') {
}
ScrollSpy.prototype.refresh = function () {
var offsetMethod = 'offset'
var offsetBase = 0
var that = this
var offsetMethod = 'offset'
var offsetBase = 0
this.offsets = []
this.targets = []
this.scrollHeight = this.getScrollHeight()
if (!$.isWindow(this.$scrollElement[0])) {
offsetMethod = 'position'
offsetBase = this.$scrollElement.scrollTop()
}
this.offsets = []
this.targets = []
this.scrollHeight = this.getScrollHeight()
var self = this
this.$body
.find(this.selector)
.map(function () {
@@ -1883,8 +1894,8 @@ if (typeof jQuery === 'undefined') {
})
.sort(function (a, b) { return a[0] - b[0] })
.each(function () {
self.offsets.push(this[0])
self.targets.push(this[1])
that.offsets.push(this[0])
that.targets.push(this[1])
})
}
@@ -1913,7 +1924,7 @@ if (typeof jQuery === 'undefined') {
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
&& this.activate(targets[i])
}
}
@@ -1924,8 +1935,8 @@ if (typeof jQuery === 'undefined') {
this.clear()
var selector = this.selector +
'[data-target="' + target + '"],' +
this.selector + '[href="' + target + '"]'
'[data-target="' + target + '"],' +
this.selector + '[href="' + target + '"]'
var active = $(selector)
.parents('li')
@@ -1989,7 +2000,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: tab.js v3.3.2
* Bootstrap: tab.js v3.3.4
* http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -2007,7 +2018,7 @@ if (typeof jQuery === 'undefined') {
this.element = $(element)
}
Tab.VERSION = '3.3.2'
Tab.VERSION = '3.3.4'
Tab.TRANSITION_DURATION = 150
@@ -2078,7 +2089,7 @@ if (typeof jQuery === 'undefined') {
element.removeClass('fade')
}
if (element.parent('.dropdown-menu')) {
if (element.parent('.dropdown-menu').length) {
element
.closest('li.dropdown')
.addClass('active')
@@ -2143,7 +2154,7 @@ if (typeof jQuery === 'undefined') {
}(jQuery);
/* ========================================================================
* Bootstrap: affix.js v3.3.2
* Bootstrap: affix.js v3.3.4
* http://getbootstrap.com/javascript/#affix
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
@@ -2165,14 +2176,14 @@ if (typeof jQuery === 'undefined') {
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
this.$element = $(element)
this.affixed =
this.unpin =
this.affixed = null
this.unpin = null
this.pinnedOffset = null
this.checkPosition()
}
Affix.VERSION = '3.3.2'
Affix.VERSION = '3.3.4'
Affix.RESET = 'affix affix-top affix-bottom'
@@ -2222,7 +2233,7 @@ if (typeof jQuery === 'undefined') {
var offset = this.options.offset
var offsetTop = offset.top
var offsetBottom = offset.bottom
var scrollHeight = $('body').height()
var scrollHeight = $(document.body).height()
if (typeof offset != 'object') offsetBottom = offsetTop = offset
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 {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
min-width: 32px;

View File

@@ -1,2 +1,3 @@
#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 || {};
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) {
if ($('#' + container).length === 1) {
@@ -13,10 +13,10 @@ function googleChart(chartType, URL, container, options) {
Format as money
*/
var money = new google.visualization.NumberFormat({
decimalSymbol: ',',
groupingSymbol: '.',
prefix: currencyCode + ' '
});
decimalSymbol: ',',
groupingSymbol: '.',
prefix: currencyCode + ' '
});
for (var i = 1; i < gdata.getNumberOfColumns(); i++) {
money.format(gdata, i);
}
@@ -24,14 +24,14 @@ function googleChart(chartType, URL, container, options) {
/*
Create a new google charts object.
*/
var chart = false
var chart = false;
var options = false;
if (chartType === 'line') {
chart = new google.visualization.LineChart(document.getElementById(container));
options = options || defaultLineChartOptions;
}
if (chartType === 'column') {
chart = new google.charts.Bar(document.getElementById(container));
chart = new google.visualization.ColumnChart(document.getElementById(container));
options = options || defaultColumnChartOptions;
}
if (chartType === 'pie') {
@@ -39,7 +39,7 @@ function googleChart(chartType, URL, container, options) {
options = options || defaultPieChartOptions;
}
if (chartType === 'bar') {
chart = new google.charts.Bar(document.getElementById(container));
chart = new google.visualization.BarChart(document.getElementById(container));
options = options || defaultBarChartOptions;
}
if (chartType === 'stackedColumn') {

View File

@@ -16,19 +16,19 @@ var defaultLineChartOptions = {
hAxis: {
textStyle: {
color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
},
baselineColor: '#aaaaaa',
gridlines: {
color: 'transparent'
}
},
fontName: 'Roboto',
fontSize: 11,
vAxis: {
textStyle: {
color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
},
baselineColor: '#aaaaaa',
format: '\u20AC #'
}
@@ -38,9 +38,30 @@ var defaultLineChartOptions = {
var defaultBarChartOptions = {
height: 400,
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: {
left: 75,
left: 15,
top: 10,
width: '100%',
height: '90%'
@@ -63,6 +84,8 @@ var defaultComboChartOptions = {
minValue: 0,
format: '\u20AC #'
},
fontName: 'Roboto',
fontSize: 11,
legend: {
position: 'none'
},
@@ -82,7 +105,26 @@ var defaultColumnChartOptions = {
width: '85%',
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: {
position: 'none'
},
@@ -99,13 +141,13 @@ var defaultStackedColumnChartOptions = {
legend: {
position: 'none'
},
fontName: 'Roboto',
fontSize: 11,
isStacked: true,
colors: ["#4285f4", "#db4437", "#f4b400", "#0f9d58", "#ab47bc", "#00acc1", "#ff7043", "#9e9d24", "#5c6bc0", "#f06292", "#00796b", "#c2185b"],
hAxis: {
textStyle: {
color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
},
gridlines: {
color: 'transparent'
@@ -114,8 +156,6 @@ var defaultStackedColumnChartOptions = {
vAxis: {
textStyle: {
color: '#838383',
fontName: 'Roboto2',
fontSize: '12'
},
format: '\u20AC #'
}
@@ -128,6 +168,8 @@ var defaultPieChartOptions = {
width: '100%',
height: '100%'
},
fontName: 'Roboto',
fontSize: 11,
height: 200,
legend: {
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') {
googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history');
}
$('#sortable').sortable(
{
stop: stopSorting,
handle: '.handle'
}
);
});
function addMoney(e) {
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;
}
function removeMoney(e) {
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;
}
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 () {
if(typeof googleTablePaged != 'undefined') {
googleTablePaged('table/transactions/' + what,'transaction-table');
if (typeof googleTablePaged != 'undefined') {
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
</div>
<div class="panel-body">
@include('list.journals-full')
@include('list.journals-full',['sorting' => true])
</div>
</div>
</div>
@@ -49,10 +49,14 @@
<script type="text/javascript">
var accountID = {{{$account->id}}};
var currencyCode = '{{Amount::getCurrencyCode()}}';
var token = "{{csrf_token()}}";
</script>
<!-- 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="js/gcharts.options.js"></script>
<script type="text/javascript" src="js/gcharts.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

View File

@@ -8,15 +8,15 @@
<i class="fa fa-rotate-right"></i> {{{$bill->name}}}
@if($bill->active)
<span class="glyphicon glyphicon-ok" title="Active"></span>
<i class="fa fa-check fa-fw" title="Active"></i>
@else
<span class="glyphicon glyphicon-remove" title="Inactive"></span>
<i class="fa fa-times fa-fw" title="Inactive"></i>
@endif
@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
<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
<!-- ACTIONS MENU -->
@@ -27,8 +27,8 @@
<span class="caret"></span>
</button>
<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.delete',$bill->id)}}"><span class="glyphicon glyphicon-trash"></span> delete</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)}}"><i class="fa fa-fw fa-trash-o"></i> delete</a></li>
</ul>
</div>
</div>
@@ -94,7 +94,7 @@
Connected transaction journals
</div>
<div class="panel-body">
@include('list.journals-full')
@include('list.journals-full',['sorting' => false])
</div>
</div>
</div>

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,4 @@
@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>
@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
</div>
<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>
@@ -69,7 +116,7 @@
<!-- REMINDERS -->
<div class="panel panel-default">
<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 class="panel-body">
<div id="bills-chart"></div>
@@ -81,7 +128,7 @@
<div class="panel panel-default">
<div class="panel-heading">
<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 -->
@@ -94,7 +141,7 @@
<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','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>
</div>
</div>

View File

@@ -14,13 +14,12 @@
// {{{$subTitle}}}
@endif
</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-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/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 -->
<link rel="stylesheet" href="css/daterangepicker-bs3.css" type="text/css" media="all" />
@@ -29,11 +28,6 @@
@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="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">

View File

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

View File

@@ -14,8 +14,8 @@
<tr>
<td>
<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.delete',$entry->id)}}" class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-trash"></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"><i class="fa fa-fw fa-trash-o"></i></a>
</div>
</td>
<td>

View File

@@ -13,8 +13,8 @@
<tr>
<td>
<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.delete',$category->id)}}" class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-trash"></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"><i class="fa fa-fw fa-trash-o"></i></a>
</div>
</td>
<td>

View File

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

View File

@@ -2,32 +2,32 @@
@foreach($transactions as $journal)
<a class="list-group-item" title="{{$journal->date->format('jS M Y')}}" href="{{route('transactions.show',$journal->id)}}">
@if($journal->transactiontype->type == 'Withdrawal')
<i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i>
@endif
@if($journal->transactiontype->type == 'Deposit')
<i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i>
@endif
@if($journal->transactiontype->type == 'Transfer')
<i class="fa fa-arrows-h fa-fw" title="Transfer"></i>
@if(is_null($journal->type))
@if($journal->transactiontype->type == 'Withdrawal')
<i class="fa fa-long-arrow-left fa-fw" title="Withdrawal"></i>
@endif
@if($journal->transactiontype->type == 'Deposit')
<i class="fa fa-long-arrow-right fa-fw" title="Deposit"></i>
@endif
@if($journal->transactiontype->type == 'Transfer')
<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
{{{$journal->description}}}
<span class="pull-right small">
@if(isset($account))
@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
{!! Amount::formatJournal($journal) !!}
</span>
</a>

View File

@@ -24,8 +24,10 @@
</div>
<div class="panel-footer">
<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>
@endif
@if($reminder->active === true)
<a class="btn btn-success" href="{{route('reminders.act',$reminder->id)}}">Act</a>
@endif
</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>
</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>
</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>
</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>
<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>
</div>
</div>
<div class="row">
<div class="row" id="sortable">
@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-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 -->
<div class="pull-right">
@@ -62,34 +63,15 @@
</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 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>
</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>
</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)
<span title="Left to save">{!! Amount::format($piggyBank->leftToSave) !!}</span>
@endif
@@ -148,5 +130,9 @@
@stop
@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>
@stop

View File

@@ -2,7 +2,8 @@
<table class="table table-bordered table-striped table-condensed">
@foreach($journals as $journal)
<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>
@if($journal->transactiontype->type == 'Withdrawal')
<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>
@endif
@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
</td>
<td>{{$journal->date->format('jS M Y')}}</td>

View File

@@ -3,7 +3,7 @@
<table class="table table-bordered table-striped table-condensed">
@foreach($journals as $journal)
<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>
@if($journal->transactiontype->type == 'Withdrawal')
<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>
@endif
@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
</td>
<td>{{$journal->date->format('jS M Y')}}</td>

View File

@@ -5,7 +5,7 @@
<h4 class="modal-title">No budget bla bla.</h4>
</div>
<div class="modal-body">
@include('list.journals-full')
@include('list.journals-full',['sorting' => false])
</div>
<div class="modal-footer">
<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-heading">
<i class="fa fa-arrows-h fa-fw"></i>
<i class="fa fa-fw fa-exchange" title="Transfer"></i>
Income vs. expense
</div>
<?php

View File

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

View File

@@ -7,10 +7,18 @@
<div class="panel-heading">
<i class="fa {{$subTitleIcon}}"></i> {{{$subTitle}}}
</div>
@include('list.journals-full')
@include('list.journals-full',['sorting' => false])
</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

View File

@@ -145,7 +145,7 @@
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<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>

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

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