Merge branch 'release/3.1.3'

This commit is contained in:
Sander Dorigo 2014-10-13 19:13:44 +02:00
commit a787ff3f3c
29 changed files with 523 additions and 695 deletions

View File

@ -1,8 +1,10 @@
<?php <?php
use Carbon\Carbon; use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Firefly\Helper\Controllers\ChartInterface; use Firefly\Helper\Controllers\ChartInterface;
use Firefly\Storage\Account\AccountRepositoryInterface; use Firefly\Storage\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
/** /**
* Class ChartController * Class ChartController
@ -18,7 +20,7 @@ class ChartController extends BaseController
/** /**
* @param ChartInterface $chart * @param ChartInterface $chart
* @param AccountRepositoryInterface $accounts * @param AccountRepositoryInterface $accounts
*/ */
public function __construct(ChartInterface $chart, AccountRepositoryInterface $accounts) public function __construct(ChartInterface $chart, AccountRepositoryInterface $accounts)
@ -57,13 +59,13 @@ class ChartController extends BaseController
$return = [ $return = [
'chart_title' => 'Overview for budget ' . $budget->name, 'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' => 'All envelopes', 'subtitle' => 'All envelopes',
'series' => [ 'series' => [
[ [
'type' => 'line', 'type' => 'line',
'yAxis' => 1, 'yAxis' => 1,
'name' => 'Amount in envelope', 'name' => 'Amount in envelope',
'data' => $envelope 'data' => $envelope
], ],
[ [
'type' => 'column', 'type' => 'column',
@ -71,10 +73,10 @@ class ChartController extends BaseController
'data' => $expense 'data' => $expense
], ],
[ [
'type' => 'line', 'type' => 'line',
'yAxis' => 1, 'yAxis' => 1,
'name' => 'Spent percentage for envelope', 'name' => 'Spent percentage for envelope',
'data' => $left 'data' => $left
] ]
@ -111,14 +113,14 @@ class ChartController extends BaseController
$return = [ $return = [
'chart_title' => 'Overview for budget ' . $budget->name, 'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' => 'subtitle' =>
'Between ' . $rep->startdate->format('M jS, Y') . ' and ' . $rep->enddate->format('M jS, Y'), 'Between ' . $rep->startdate->format('M jS, Y') . ' and ' . $rep->enddate->format('M jS, Y'),
'series' => [ 'series' => [
[ [
'type' => 'column', 'type' => 'column',
'name' => 'Expenses per day', 'name' => 'Expenses per day',
'yAxis' => 1, 'yAxis' => 1,
'data' => $expense 'data' => $expense
], ],
[ [
'type' => 'line', 'type' => 'line',
@ -175,8 +177,8 @@ class ChartController extends BaseController
} }
$return = [ $return = [
'chart_title' => 'Overview for ' . $budget->name, 'chart_title' => 'Overview for ' . $budget->name,
'subtitle' => 'Not organized by an envelope', 'subtitle' => 'Not organized by an envelope',
'series' => [ 'series' => [
[ [
'type' => 'column', 'type' => 'column',
'name' => 'Expenses per day', 'name' => 'Expenses per day',
@ -245,11 +247,11 @@ class ChartController extends BaseController
// create a serie for the repetition. // create a serie for the repetition.
$currentSerie = [ $currentSerie = [
'type' => 'spline', 'type' => 'spline',
'id' => 'rep-' . $repetition->id, 'id' => 'rep-' . $repetition->id,
'yAxis' => 1, 'yAxis' => 1,
'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(), 'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(),
'data' => [] 'data' => []
]; ];
$current = clone $repetition->startdate; $current = clone $repetition->startdate;
while ($current <= $repetition->enddate) { while ($current <= $repetition->enddate) {
@ -271,11 +273,11 @@ class ChartController extends BaseController
$return = [ $return = [
'chart_title' => 'Overview for budget ' . $budget->name, 'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' => 'subtitle' =>
'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format( 'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format(
'M jS, Y' 'M jS, Y'
), ),
'series' => $series 'series' => $series
]; ];
return Response::json($return); return Response::json($return);
@ -296,8 +298,8 @@ class ChartController extends BaseController
$serie = $this->_chart->categoryShowChart($category, $range, $start, $end); $serie = $this->_chart->categoryShowChart($category, $range, $start, $end);
$data = [ $data = [
'chart_title' => $category->name, 'chart_title' => $category->name,
'subtitle' => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>', 'subtitle' => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>',
'series' => $serie 'series' => $serie
]; ];
return Response::json($data); return Response::json($data);
@ -336,8 +338,8 @@ class ChartController extends BaseController
'<a href="' . route('accounts.index') . '">View more</a>'; '<a href="' . route('accounts.index') . '">View more</a>';
$data = [ $data = [
'chart_title' => count($accounts) == 1 ? $accounts[0]->name : 'All accounts', 'chart_title' => count($accounts) == 1 ? $accounts[0]->name : 'All accounts',
'subtitle' => $url, 'subtitle' => $url,
'series' => [] 'series' => []
]; ];
foreach ($accounts as $account) { foreach ($accounts as $account) {
@ -431,11 +433,11 @@ class ChartController extends BaseController
if ($limit > 0 || $expenses > 0) { if ($limit > 0 || $expenses > 0) {
$data['labels'][] = $budget->name; $data['labels'][] = $budget->name;
$data['series'][0]['data'][] = [ $data['series'][0]['data'][] = [
'y' => $limit, 'y' => $limit,
'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter 'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter
]; ];
$data['series'][1]['data'][] = [ $data['series'][1]['data'][] = [
'y' => $expenses, 'y' => $expenses,
'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter 'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter
]; ];
} }
@ -444,26 +446,26 @@ class ChartController extends BaseController
$set = \Auth::user()->transactionjournals()->whereNotIn( $set = \Auth::user()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) { 'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals') $query->select('transaction_journals.id')->from('transaction_journals')
->leftJoin( ->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', 'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id' 'transaction_journals.id'
) )
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id') ->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget'); ->where('components.class', 'Budget');
} }
)->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->sum('amount'); )->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->sum('amount');
// This can be debugged by using get(['transaction_journals.*','transactions.amount']); // This can be debugged by using get(['transaction_journals.*','transactions.amount']);
$data['labels'][] = 'No budget'; $data['labels'][] = 'No budget';
$data['series'][0]['data'][] = [ $data['series'][0]['data'][] = [
'y' => 0, 'y' => 0,
'url' => route('budgets.nobudget','session') 'url' => route('budgets.nobudget', 'session')
]; ];
$data['series'][1]['data'][] = [ $data['series'][1]['data'][] = [
'y' => floatval($set) * -1, 'y' => floatval($set) * -1,
'url' => route('budgets.nobudget','session') 'url' => route('budgets.nobudget', 'session')
]; ];
return Response::json($data); return Response::json($data);
@ -482,4 +484,120 @@ class ChartController extends BaseController
} }
/**
* This method checks all recurring transactions, calculates the current "moment" and returns
* a list of yet to be paid (and paid) recurring transactions. This list can be used to show a beautiful chart
* to the end user who will love it and cherish it.
*
* @throws FireflyException
*/
public function homeRecurring()
{
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
/*
* Set of paid transaction journals.
* Set of unpaid recurring transactions.
*/
$paid = [];
$unpaid = [];
/*
* Loop the recurring transactions.
*/
/** @var \RecurringTransaction $recurring */
foreach (\Auth::user()->recurringtransactions()->get() as $recurring) {
/*
* Start another loop starting at the $date.
*/
$start = clone $recurring->date;
$end = Carbon::now();
/*
* The jump we make depends on the $repeat_freq
*/
$current = clone $start;
while ($current <= $end) {
/*
* Get end of period for $current:
*/
$currentEnd = clone $current;
$toolkit->endOfPeriod($currentEnd, $recurring->repeat_freq);
/*
* In the current session range?
*/
if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) {
/*
* Lets see if we've already spent money on this recurring transaction (it hath recurred).
*/
/** @var TransactionJournal $set */
$transaction = \Auth::user()->transactionjournals()->where('recurring_transaction_id', $recurring->id)->after($current)->before($currentEnd)->first();
if(is_null($transaction)) {
$unpaid[] = $recurring;
} else {
$paid[] = $transaction;
}
}
/*
* Add some time for the next loop!
*/
$toolkit->addPeriod($current, $recurring->repeat_freq, intval($recurring->skip));
}
}
/*
* Get some colors going.
*/
$unPaidColours = $toolkit->colorRange('AA4643', 'FFFFFF', count($unpaid) == 0 ? 1 : count($unpaid));
$paidColours = $toolkit->colorRange('4572A7', 'FFFFFF', count($paid) == 0 ? 1 : count($paid));
/*
* The chart serie:
*/
$serie = [
'type' => 'pie',
'name' => 'Amount',
'data' => []
];
/*
* Loop paid and unpaid to make two haves for a pie chart.
*/
/** @var TransactionJournal $entry */
foreach ($paid as $index => $entry) {
$transactions = $entry->transactions()->get();
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));
$serie['data'][] = [
'name' => 'Already paid "'.$entry->description.'"',
'y' => $amount,
'url' => route('transactions.show',$entry->id),
'objType' => 'paid',
'color' => $paidColours[$index]
];
}
/** @var RecurringTransaction $entry */
foreach ($unpaid as $index => $entry) {
$amount = (floatval($entry->amount_max) + floatval($entry->amount_min)) / 2;
$serie['data'][] = [
'name' => 'Still due: '.$entry->name,
'y' => $amount,
'url' => route('recurring.show',$entry->id),
'objType' => 'unpaid',
'color' => $unPaidColours[$index]
];
}
return Response::json([$serie]);
}
} }

View File

@ -1,7 +1,6 @@
<?php <?php
use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI; use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI; use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI; use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
/** /**
@ -14,20 +13,17 @@ class HomeController extends BaseController
protected $_accounts; protected $_accounts;
protected $_preferences; protected $_preferences;
protected $_journal; protected $_journal;
protected $_reminders;
/** /**
* @param ARI $accounts * @param ARI $accounts
* @param PHI $preferences * @param PHI $preferences
* @param TJRI $journal * @param TJRI $journal
* @param RRI $reminders
*/ */
public function __construct(ARI $accounts, PHI $preferences, TJRI $journal, RRI $reminders) public function __construct(ARI $accounts, PHI $preferences, TJRI $journal)
{ {
$this->_accounts = $accounts; $this->_accounts = $accounts;
$this->_preferences = $preferences; $this->_preferences = $preferences;
$this->_journal = $journal; $this->_journal = $journal;
$this->_reminders = $reminders;
} }
public function jobDev() public function jobDev()
@ -65,10 +61,9 @@ class HomeController extends BaseController
public function rangeJump($range) public function rangeJump($range)
{ {
$viewRange = $this->_preferences->get('viewRange', '1M'); $valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
$valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
if(in_array($range,$valid)) { if (in_array($range, $valid)) {
$this->_preferences->set('viewRange', $range); $this->_preferences->set('viewRange', $range);
Session::forget('range'); Session::forget('range');
} }

View File

@ -21,7 +21,7 @@ class RecurringController extends BaseController
public function __construct(RTR $repository, RI $helper) public function __construct(RTR $repository, RI $helper)
{ {
$this->_repository = $repository; $this->_repository = $repository;
$this->_helper = $helper; $this->_helper = $helper;
View::share('title', 'Recurring transactions'); View::share('title', 'Recurring transactions');
View::share('mainTitleIcon', 'fa-rotate-right'); View::share('mainTitleIcon', 'fa-rotate-right');
@ -102,6 +102,31 @@ class RecurringController extends BaseController
} }
/**
* @param RecurringTransaction $recurringTransaction
* @return mixed
*/
public function rescan(RecurringTransaction $recurringTransaction)
{
if (intval($recurringTransaction->active) == 0) {
Session::flash('warning', 'Inactive recurring transactions cannot be scanned.');
return Redirect::back();
}
// do something!
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $repo */
$repo = App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$set = $repo->get();
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
Event::fire('recurring.rescan', [$recurringTransaction, $journal]);
}
Session::flash('success', 'Rescanned everything.');
return Redirect::back();
}
public function store() public function store()
{ {
$data = Input::except(['_token', 'post_submit_action']); $data = Input::except(['_token', 'post_submit_action']);
@ -170,14 +195,14 @@ class RecurringController extends BaseController
Session::flash('error', 'Could not update recurring transaction: ' . $messageBag->first()); Session::flash('error', 'Could not update recurring transaction: ' . $messageBag->first());
return Redirect::route('transactions.edit', $recurringTransaction->id)->withInput() return Redirect::route('transactions.edit', $recurringTransaction->id)->withInput()
->withErrors($messageBag); ->withErrors($messageBag);
} }
break; break;
case 'validate_only': case 'validate_only':
$data = Input::all(); $data = Input::all();
$data['id'] = $recurringTransaction->id; $data['id'] = $recurringTransaction->id;
$messageBags = $this->_helper->validate($data); $messageBags = $this->_helper->validate($data);
Session::flash('warnings', $messageBags['warnings']); Session::flash('warnings', $messageBags['warnings']);

View File

@ -1,97 +1,10 @@
<?php <?php
use Carbon\Carbon;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
/** /**
* Class ReminderController * Class ReminderController
* *
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/ */
class ReminderController extends BaseController class ReminderController extends BaseController
{ {
protected $_repository; }
/**
* @param RRI $repository
*/
public function __construct(RRI $repository)
{
$this->_repository = $repository;
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\JsonResponse
*/
public function dismiss(\Reminder $reminder)
{
$reminder = $this->_repository->deactivate($reminder);
return Response::json($reminder->id);
}
/**
* Returns the reminders currently active for the modal dialog.
*/
public function modalDialog()
{
$today = new Carbon;
$reminders = $this->_repository->getPiggybankReminders();
/** @var \Reminder $reminder */
foreach ($reminders as $index => $reminder) {
if (\Session::has('dismissal-' . $reminder->id)) {
$time = \Session::get('dismissal-' . $reminder->id);
if ($time >= $today) {
unset($reminders[$index]);
}
}
}
return View::make('reminders.popup')->with('reminders', $reminders);
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\JsonResponse
*/
public function postpone(\Reminder $reminder)
{
$now = new Carbon;
$now->addDay();
Session::put('dismissal-' . $reminder->id, $now);
return Response::json($reminder->id);
}
/**
* @param Reminder $reminder
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function redirect(\Reminder $reminder)
{
if ($reminder instanceof PiggybankReminder) {
// fields to prefill:
$parameters = [
'account_to_id' => $reminder->piggybank->account->id,
'amount' => round($reminder->amountToSave(), 2),
'description' => 'Money for ' . $reminder->piggybank->name,
'piggybank_id' => $reminder->piggybank->id,
'reminder_id' => $reminder->id
];
return Redirect::to(
route('transactions.create', ['what' => 'transfer']) . '?' . http_build_query($parameters)
);
}
return View::make('error')->with('message', 'No such reminder.');
}
}

View File

@ -27,26 +27,11 @@ class CreateRemindersTable extends Migration
'reminders', function (Blueprint $table) { 'reminders', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->timestamps(); $table->timestamps();
$table->string('class', 40);
$table->integer('piggybank_id')->unsigned()->nullable();
$table->integer('recurring_transaction_id')->unsigned()->nullable();
$table->integer('user_id')->unsigned(); $table->integer('user_id')->unsigned();
$table->date('startdate'); $table->date('startdate');
$table->date('enddate')->nullable(); $table->date('enddate')->nullable();
$table->boolean('active'); $table->boolean('active');
// connect reminders to piggy banks.
$table->foreign('piggybank_id')
->references('id')->on('piggybanks')
->onDelete('set null');
// connect reminders to recurring transactions.
$table->foreign('recurring_transaction_id')
->references('id')->on('recurring_transactions')
->onDelete('set null');
// connect reminders to users // connect reminders to users
$table->foreign('user_id') $table->foreign('user_id')
->references('id')->on('users') ->references('id')->on('users')

View File

@ -10,6 +10,7 @@ App::before(
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface'); $toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->getDateRange(); $toolkit->getDateRange();
$toolkit->checkImportJobs(); $toolkit->checkImportJobs();
Event::fire('recurring.verify');
} }
} }

View File

@ -355,7 +355,7 @@ class Json implements JsonInterface
* Loop set and create entries to return. * Loop set and create entries to return.
*/ */
foreach ($set as $entry) { foreach ($set as $entry) {
$data['data'][] = [ $set = [
'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)], 'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)],
'match' => explode(' ', $entry->match), 'match' => explode(' ', $entry->match),
@ -370,6 +370,11 @@ class Json implements JsonInterface
'delete' => route('recurring.delete', $entry->id) 'delete' => route('recurring.delete', $entry->id)
] ]
]; ];
if (intval($entry->skip) > 0) {
$set['repeat_freq'] = $entry->repeat_freq . ' (skip ' . $entry->skip . ')';
}
$data['data'][] = $set;
} }
return $data; return $data;

View File

@ -381,4 +381,127 @@ class Toolkit implements ToolkitInterface
} }
return $selectList; return $selectList;
} }
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5)
{
if (strlen($start) != 6) {
throw new FireflyException('Start, ' . e($start) . ' should be a six character HTML colour.');
}
if (strlen($end) != 6) {
throw new FireflyException('End, ' . e($end) . ' should be a six character HTML colour.');
}
if ($steps < 1) {
throw new FireflyException('Steps must be > 1');
}
$start = '#' . $start;
$end = '#' . $end;
/*
* Split html colours.
*/
list($rs, $gs, $bs) = sscanf($start, "#%02x%02x%02x");
list($re, $ge, $be) = sscanf($end, "#%02x%02x%02x");
$stepr = ($re - $rs) / $steps;
$stepg = ($ge - $gs) / $steps;
$stepb = ($be - $bs) / $steps;
$return = [];
for ($i = 0; $i <= $steps; $i++) {
$cr = $rs + ($stepr * $i);
$cg = $gs + ($stepg * $i);
$cb = $bs + ($stepb * $i);
$return[] = $this->rgb2html($cr, $cg, $cb);
}
return $return;
}
protected function rgb2html($r, $g = -1, $b = -1)
{
$r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r));
$g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g));
$b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b));
$color = (strlen($r) < 2 ? '0' : '') . $r;
$color .= (strlen($g) < 2 ? '0' : '') . $g;
$color .= (strlen($b) < 2 ? '0' : '') . $b;
return '#' . $color;
}
/**
* @param Carbon $currentEnd
* @param $repeatFreq
* @throws FireflyException
*/
public function endOfPeriod(Carbon $currentEnd, $repeatFreq)
{
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->addDay();
break;
case 'weekly':
$currentEnd->addWeek()->subDay();
break;
case 'monthly':
$currentEnd->addMonth()->subDay();
break;
case 'quarterly':
$currentEnd->addMonths(3)->subDay();
break;
case 'half-year':
$currentEnd->addMonths(6)->subDay();
break;
case 'yearly':
$currentEnd->addYear()->subDay();
break;
}
}
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
* @throws FireflyException
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip)
{
$add = ($skip + 1);
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$date->addDays($add);
break;
case 'weekly':
$date->addWeeks($add);
break;
case 'monthly':
$date->addMonths($add);
break;
case 'quarterly':
$months = $add * 3;
$date->addMonths($months);
break;
case 'half-year':
$months = $add * 6;
$date->addMonths($months);
break;
case 'yearly':
$date->addYears($add);
break;
}
return $date;
}
} }

View File

@ -2,6 +2,7 @@
namespace Firefly\Helper\Toolkit; namespace Firefly\Helper\Toolkit;
use Carbon\Carbon;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@ -21,7 +22,7 @@ interface ToolkitInterface
* Takes any collection and tries to make a sensible select list compatible array of it. * Takes any collection and tries to make a sensible select list compatible array of it.
* *
* @param Collection $set * @param Collection $set
* @param null $titleField * @param null $titleField
* *
* @return mixed * @return mixed
*/ */
@ -33,4 +34,19 @@ interface ToolkitInterface
public function checkImportJobs(); public function checkImportJobs();
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5);
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip);
} }

View File

@ -31,17 +31,4 @@ class EloquentReminderRepository implements ReminderRepositoryInterface
{ {
$this->_user = \Auth::user(); $this->_user = \Auth::user();
} }
/**
* @param \Reminder $reminder
*
* @return mixed|void
*/
public function deactivate(\Reminder $reminder)
{
$reminder->active = 0;
$reminder->save();
return $reminder;
}
} }

View File

@ -10,14 +10,6 @@ namespace Firefly\Storage\Reminder;
interface ReminderRepositoryInterface interface ReminderRepositoryInterface
{ {
/**
* @param \Reminder $reminder
*
* @return mixed
*/
public function deactivate(\Reminder $reminder);
/** /**
* @param \User $user * @param \User $user
* @return mixed * @return mixed

View File

@ -5,6 +5,7 @@ namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job; use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
/** /**
@ -24,6 +25,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$this->_user = \Auth::user(); $this->_user = \Auth::user();
} }
/**
* Get them ALL
*
* @return Collection
*/
public function get() {
return $this->_user->transactionjournals()->with('transactions')->get();
}
/** /**
* @param Job $job * @param Job $job
* @param array $payload * @param array $payload

View File

@ -4,6 +4,7 @@ namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job; use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
/** /**
* Interface TransactionJournalRepositoryInterface * Interface TransactionJournalRepositoryInterface
@ -20,6 +21,13 @@ interface TransactionJournalRepositoryInterface
*/ */
public function importTransaction(Job $job, array $payload); public function importTransaction(Job $job, array $payload);
/**
* Get them ALL
*
* @return Collection
*/
public function get();
/** /**
* @param Job $job * @param Job $job
* @param array $payload * @param array $payload

View File

@ -40,7 +40,7 @@ class EloquentJournalTrigger
*/ */
$count = 0; $count = 0;
foreach ($matches as $word) { foreach ($matches as $word) {
if (!(strpos($description, $word) === false)) { if (!(strpos($description, strtolower($word)) === false)) {
$count++; $count++;
\Log::debug('Recurring transaction #' . $recurring->id . ': word "' . $word . '" found in "' . $description . '".'); \Log::debug('Recurring transaction #' . $recurring->id . ': word "' . $word . '" found in "' . $description . '".');
} }

View File

@ -107,159 +107,6 @@ class EloquentPiggybankTrigger
return true; return true;
} }
/**
* Whenever a repetition is made, the decision is there to make reminders for it. Or not.
* Some combinations are "invalid" or impossible and will never trigger reminders. Others do.
*
* The numbers below refer to a small list I made in a text-file (it no longer exists) which contained the eight
* binary combinations that can be made of three properties each piggy bank has (among others):
*
* - Whether or not it has a start date.
* - Whether or not it has an end date.
* - Whether or not the piggy bank repeats itself.
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param \PiggybankRepetition $repetition
*
* @return null
*/
public function createdRepetition(\PiggybankRepetition $repetition)
{
\Log::debug('TRIGGER on createdRepetition() for repetition #' . $repetition->id);
$piggyBank = $repetition->piggybank;
// first, exclude all combinations that will not generate (valid) reminders
// no reminders needed (duh)
if (is_null(($piggyBank->reminder))) {
\Log::debug('No reminders because no reminder needed.');
return null;
}
// no start, no target, no repeat (#1):
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 0) {
\Log::debug('No reminders because no start, no target, no repeat (#1)');
return null;
}
// no start, but repeats (#5):
if (is_null($piggyBank->startdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, but repeats (#5)');
return null;
}
// no start, no end, but repeats (#6)
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, no end, but repeats (#6)');
return null;
}
// no end, but repeats (#7)
if (is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no end, but repeats (#7)');
return null;
}
\Log::debug('Will continue...');
/*
* #2, #3, #4 and #8 are valid combo's.
*
* We add two years to the end when the repetition has no target date; we "pretend" there is a target date.
*
*/
if (is_null($repetition->targetdate)) {
$end = new Carbon;
$end->addYears(2);
} else {
$end = $repetition->targetdate;
}
/*
* If there is no start date, the start dat becomes right now.
*/
if (is_null($repetition->startdate)) {
$start = new Carbon;
} else {
$start = $repetition->startdate;
}
/*
* Firefly checks every period X between $start and $end and if necessary creates a reminder. Firefly
* only creates reminders if the $current date is after today. Piggy banks may have their start in the past.
*
* This loop will jump a month when the reminder is set monthly, a week when it's set weekly, etcetera.
*/
$current = $start;
$today = new Carbon;
$today->startOfDay();
while ($current <= $end) {
\Log::debug('Looping reminder dates; now at ' . $current);
/*
* Piggy bank reminders start X days before the actual date of the event.
*/
$reminderStart = clone $current;
switch ($piggyBank->reminder) {
case 'day':
$reminderStart->subDay();
break;
case 'week':
$reminderStart->subDays(4);
break;
case 'month':
$reminderStart->subDays(21);
break;
case 'year':
$reminderStart->subMonths(9);
break;
}
/*
* If the date is past today we create a reminder, otherwise we don't. The end date is the date
* the reminder is due; after that it is invalid.
*/
if ($current >= $today) {
$reminder = new \PiggybankReminder;
$reminder->piggybank()->associate($piggyBank);
$reminder->user()->associate(\Auth::user());
$reminder->startdate = $reminderStart;
$reminder->enddate = $current;
$reminder->active = 1;
\Log::debug('Will create a reminder. Is it valid?');
\Log::debug($reminder->validate());
try {
$reminder->save();
} catch (QueryException $e) {
\Log::error('Could not save reminder: ' . $e->getMessage());
}
} else {
\Log::debug('Current is before today, will not make a reminder.');
}
/*
* Here Firefly jumps ahead to the next reminder period.
*/
switch ($piggyBank->reminder) {
case 'day':
$current->addDays($piggyBank->reminder_skip);
break;
case 'week':
$current->addWeeks($piggyBank->reminder_skip);
break;
case 'month':
$current->addMonths($piggyBank->reminder_skip);
break;
case 'year':
$current->addYears($piggyBank->reminder_skip);
break;
}
}
}
/** /**
* @param \Piggybank $piggyBank * @param \Piggybank $piggyBank
* *
@ -267,13 +114,6 @@ class EloquentPiggybankTrigger
*/ */
public function destroy(\Piggybank $piggyBank) public function destroy(\Piggybank $piggyBank)
{ {
$reminders = $piggyBank->piggybankreminders()->get();
/** @var \PiggybankReminder $reminder */
foreach ($reminders as $reminder) {
$reminder->delete();
}
return true; return true;
} }
@ -356,9 +196,6 @@ class EloquentPiggybankTrigger
'piggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies' 'piggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies'
); );
$events->listen(
'piggybanks.repetition', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createdRepetition'
);
} }
/** /**

View File

@ -3,6 +3,7 @@
namespace Firefly\Trigger\Recurring; namespace Firefly\Trigger\Recurring;
use Carbon\Carbon; use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Events\Dispatcher; use Illuminate\Events\Dispatcher;
/** /**
@ -28,8 +29,50 @@ class EloquentRecurringTrigger
} }
public function createReminders() /**
* @param \RecurringTransaction $recurring
* @param \TransactionJournal $journal
*/
public function rescan(\RecurringTransaction $recurring, \TransactionJournal $journal)
{ {
/*
* Match words.
*/
$wordMatch = false;
$matches = explode(' ', $recurring->match);
$description = strtolower($journal->description);
$count = 0;
foreach ($matches as $word) {
if (!(strpos($description, strtolower($word)) === false)) {
$count++;
}
}
if ($count > 0) {
$wordMatch = true;
}
/*
* Match amount.
*/
$transactions = $journal->transactions()->get();
$amountMatch = false;
if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));
$min = floatval($recurring->amount_min);
$max = floatval($recurring->amount_max);
if ($amount >= $min && $amount <= $max) {
$amountMatch = true;
}
}
/*
* If both, update!
*/
if ($wordMatch && $amountMatch) {
$journal->recurringTransaction()->associate($recurring);
$journal->save();
}
} }
/** /**
@ -39,10 +82,7 @@ class EloquentRecurringTrigger
*/ */
public function subscribe(Dispatcher $events) public function subscribe(Dispatcher $events)
{ {
// $events->listen('recurring.destroy', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@destroy'); $events->listen('recurring.rescan', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@rescan');
// $events->listen('recurring.store', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@store');
// $events->listen('recurring.update', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@update');
// $events->listen('recurring.check', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@createReminders');
} }
/** /**

View File

@ -104,68 +104,6 @@ class Piggybank extends Ardent
return ['created_at', 'updated_at', 'targetdate', 'startdate']; return ['created_at', 'updated_at', 'targetdate', 'startdate'];
} }
/**
* Firefly shouldn't create piggybank repetions that completely
* lie in the future, so we should be able to safely grab the "latest"
* one and use that to calculate when the user will be reminded.
*/
public function nextReminderDate()
{
if (is_null($this->reminder)) {
return null;
}
/** @var \PiggybankRepetition $rep */
$rep = $this->currentRelevantRep();
$today = new Carbon;
if ($rep && is_null($rep->startdate)) {
switch ($this->reminder) {
case 'day':
return $today;
break;
case 'week':
return $today->endOfWeek();
break;
case 'month':
return $today->endOfMonth();
break;
case 'year':
return $today->endOfYear();
break;
}
return null;
}
if ($rep && !is_null($rep->startdate)) {
// start with the start date
// when its bigger than today, return it:
$start = clone $rep->startdate;
while ($start <= $today) {
switch ($this->reminder) {
default:
return null;
break;
case 'day':
$start->addDay();
break;
case 'week':
$start->addWeek();
break;
case 'month':
$start->addMonth();
break;
case 'year':
$start->addYear();
break;
}
}
return $start;
}
return new Carbon;
}
/** /**
* Grabs the PiggyBankRepetition that's currently relevant / active * Grabs the PiggyBankRepetition that's currently relevant / active
* *
@ -222,14 +160,6 @@ class Piggybank extends Ardent
return $this->hasMany('PiggybankEvent'); return $this->hasMany('PiggybankEvent');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggybankreminders()
{
return $this->hasMany('PiggybankReminder');
}
/** /**
* Same but for specific date. * Same but for specific date.
* *

View File

@ -1,105 +0,0 @@
<?php
use Carbon\Carbon;
/**
* PiggybankReminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $class
* @property integer $piggybank_id
* @property integer $recurring_transaction_id
* @property integer $user_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property boolean $active
* @property-read \Piggybank $piggybank
* @property-read \RecurringTransaction $recurringTransaction
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder wherePiggybankId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereRecurringTransactionId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\PiggybankReminder whereActive($value)
* @method static \Reminder validOn($date)
* @method static \Reminder validOnOrAfter($date)
*/
class PiggybankReminder extends Reminder
{
protected $isSubclass = true;
/**
* This method will render a string telling you something about what to save or something.
* @return string
*/
public function render()
{
/** @var \Piggybank $piggyBank */
$piggyBank = $this->piggybank;
$fullText
= 'In order to save enough money for <a href="' . route('piggybanks.show', $piggyBank->id) . '">"' . e(
$piggyBank->name
) . '"</a> you';
$fullText .= ' should save at least ' . mf($this->amountToSave(), false) . ' this ' . $piggyBank->reminder
. ', before ' . $this->enddate->format('M jS, Y');
return $fullText;
}
/**
* @return float
* @throws Firefly\Exception\FireflyException
*/
public function amountToSave()
{
/** @var \Piggybank $piggyBank */
$piggyBank = $this->piggybank;
/** @var \PiggybankRepetition $repetition */
$repetition = $piggyBank->currentRelevantRep();
// if the target date of the repetition is zero, we use the created_at date of the repetition
// and add two years; it's the same routine used elsewhere.
if (is_null($repetition->targetdate)) {
$targetdate = clone $repetition->created_at;
$targetdate->addYears(2);
} else {
$targetdate = $repetition->targetdate;
}
$today = new Carbon;
$diff = $today->diff($targetdate);
$left = $piggyBank->targetamount - $repetition->currentamount;
// to prevent devide by zero:
$piggyBank->reminder_skip = $piggyBank->reminder_skip < 1 ? 1 : $piggyBank->reminder_skip;
$toSave = 0;
switch ($piggyBank->reminder) {
case 'day':
$toSave = $left;// / ($diff->days / $piggyBank->reminder_skip);
break;
case 'week':
$weeks = ceil($diff->days / 7);
$toSave = $left / ($weeks / $piggyBank->reminder_skip);
break;
case 'month':
$toSave = $left / ($diff->m / $piggyBank->reminder_skip);
break;
case 'year':
throw new \Firefly\Exception\FireflyException('No impl year reminder/ PiggyBankReminder Render');
break;
}
return floatval($toSave);
}
}

View File

@ -62,15 +62,6 @@ class RecurringTransaction extends Ardent
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function reminders()
{
return $this->hasMany('RecurringTransactionReminder');
}
/** /**
* @return Carbon * @return Carbon
*/ */

View File

@ -1,42 +0,0 @@
<?php
/**
* RecurringTransactionReminder
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $class
* @property integer $piggybank_id
* @property integer $recurring_transaction_id
* @property integer $user_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property boolean $active
* @property-read \Piggybank $piggybank
* @property-read \RecurringTransaction $recurringTransaction
* @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder wherePiggybankId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereRecurringTransactionId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\RecurringTransactionReminder whereActive($value)
* @method static \Reminder validOn($date)
* @method static \Reminder validOnOrAfter($date)
*/
class RecurringTransactionReminder extends Reminder
{
protected $isSubclass = true;
public function render()
{
return '123';
}
}

View File

@ -2,6 +2,8 @@
use Carbon\Carbon; use Carbon\Carbon;
use Firefly\Database\SingleTableInheritanceEntity; use Firefly\Database\SingleTableInheritanceEntity;
use LaravelBook\Ardent\Ardent;
/** /**
* Reminder * Reminder
@ -10,33 +12,24 @@ use Firefly\Database\SingleTableInheritanceEntity;
* @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at * @property \Carbon\Carbon $updated_at
* @property string $class * @property string $class
* @property integer $piggybank_id
* @property integer $recurring_transaction_id
* @property integer $user_id * @property integer $user_id
* @property \Carbon\Carbon $startdate * @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate * @property \Carbon\Carbon $enddate
* @property boolean $active * @property boolean $active
* @property-read \Piggybank $piggybank
* @property-read \RecurringTransaction $recurringTransaction
* @property-read \User $user * @property-read \User $user
* @method static \Illuminate\Database\Query\Builder|\Reminder whereId($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereUpdatedAt($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereClass($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereClass($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder wherePiggybankId($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereRecurringTransactionId($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereUserId($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereStartdate($value) * @method static \Illuminate\Database\Query\Builder|\Reminder whereActive($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\Reminder whereActive($value)
* @method static \Reminder validOn($date)
* @method static \Reminder validOnOrAfter($date)
*/ */
class Reminder extends SingleTableInheritanceEntity class Reminder extends Ardent
{ {
protected $table = 'reminders'; protected $table = 'reminders';
protected $subclassField = 'class';
/** /**
@ -47,49 +40,6 @@ class Reminder extends SingleTableInheritanceEntity
return ['created_at', 'updated_at', 'startdate', 'enddate']; return ['created_at', 'updated_at', 'startdate', 'enddate'];
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function piggybank()
{
return $this->belongsTo('Piggybank');
}
public function recurringTransaction()
{
return $this->belongsTo('RecurringTransaction');
}
public function render()
{
return '';
}
public function scopeValidOn($query, Carbon $date)
{
return $query->where('startdate', '<=', $date->format('Y-m-d'))->where('enddate', '>=', $date->format('Y-m-d'))
->where('active', 1);
}
public function scopeValidOnOrAfter($query, Carbon $date)
{
return $query->where(
function ($q) use ($date) {
$q->where('startdate', '<=', $date->format('Y-m-d'))->where(
'enddate', '>=', $date->format('Y-m-d')
);
$q->orWhere(
function ($q) use ($date) {
$q->where('startdate', '>=', $date);
$q->where('enddate', '>=', $date);
}
);
}
)->where('active', 1);
}
/** /**
* User * User
* *

View File

@ -215,6 +215,30 @@ use LaravelBook\Ardent\Builder;
* 'Budget[] $budgets * 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\ * @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories * 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Budget[] $budgets
* @property-read \Illuminate\Database\Eloquent\Collection|\
* 'Category[] $categories
*/ */
class TransactionJournal extends Ardent class TransactionJournal extends Ardent
{ {

View File

@ -78,22 +78,6 @@ class User extends Ardent implements UserInterface, RemindableInterface
return $this->hasMany('Budget'); return $this->hasMany('Budget');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function reminders()
{
return $this->hasMany('Reminder');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggybankreminders()
{
return $this->hasMany('PiggybankReminder');
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@ -170,6 +170,7 @@ Route::group(['before' => 'auth'], function () {
Route::get('/chart/home/budgets', ['uses' => 'ChartController@homeBudgets', 'as' => 'chart.budgets']); Route::get('/chart/home/budgets', ['uses' => 'ChartController@homeBudgets', 'as' => 'chart.budgets']);
Route::get('/chart/home/info/{accountnameA}/{day}/{month}/{year}', ['uses' => 'ChartController@homeAccountInfo', 'as' => 'chart.info']); Route::get('/chart/home/info/{accountnameA}/{day}/{month}/{year}', ['uses' => 'ChartController@homeAccountInfo', 'as' => 'chart.info']);
Route::get('/chart/categories/show/{category}', ['uses' => 'ChartController@categoryShowChart','as' => 'chart.showcategory']); Route::get('/chart/categories/show/{category}', ['uses' => 'ChartController@categoryShowChart','as' => 'chart.showcategory']);
Route::get('/chart/home/recurring', ['uses' => 'ChartController@homeRecurring', 'as' => 'chart.recurring']);
// (new charts for budgets) // (new charts for budgets)
Route::get('/chart/budget/{budget}/default', ['uses' => 'ChartController@budgetDefault', 'as' => 'chart.budget.default']); Route::get('/chart/budget/{budget}/default', ['uses' => 'ChartController@budgetDefault', 'as' => 'chart.budget.default']);
Route::get('chart/budget/{budget}/no_envelope', ['uses' => 'ChartController@budgetNoLimits', 'as' => 'chart.budget.nolimit']); Route::get('chart/budget/{budget}/no_envelope', ['uses' => 'ChartController@budgetNoLimits', 'as' => 'chart.budget.nolimit']);
@ -219,16 +220,11 @@ Route::group(['before' => 'auth'], function () {
// recurring transactions controller // recurring transactions controller
Route::get('/recurring',['uses' => 'RecurringController@index', 'as' => 'recurring.index']); Route::get('/recurring',['uses' => 'RecurringController@index', 'as' => 'recurring.index']);
Route::get('/recurring/show/{recurring}',['uses' => 'RecurringController@show', 'as' => 'recurring.show']); Route::get('/recurring/show/{recurring}',['uses' => 'RecurringController@show', 'as' => 'recurring.show']);
Route::get('/recurring/rescan/{recurring}',['uses' => 'RecurringController@rescan', 'as' => 'recurring.rescan']);
Route::get('/recurring/create',['uses' => 'RecurringController@create', 'as' => 'recurring.create']); Route::get('/recurring/create',['uses' => 'RecurringController@create', 'as' => 'recurring.create']);
Route::get('/recurring/edit/{recurring}',['uses' => 'RecurringController@edit','as' => 'recurring.edit']); Route::get('/recurring/edit/{recurring}',['uses' => 'RecurringController@edit','as' => 'recurring.edit']);
Route::get('/recurring/delete/{recurring}',['uses' => 'RecurringController@delete','as' => 'recurring.delete']); Route::get('/recurring/delete/{recurring}',['uses' => 'RecurringController@delete','as' => 'recurring.delete']);
// reminder controller
Route::get('/reminders/dialog',['uses' => 'ReminderController@modalDialog']);
Route::post('/reminders/postpone/{reminder}',['uses' => 'ReminderController@postpone']);
Route::post('/reminders/dismiss/{reminder}',['uses' => 'ReminderController@dismiss']);
Route::get('/reminders/redirect/{reminder}',['uses' => 'ReminderController@redirect']);
// report controller: // report controller:
Route::get('/reports',['uses' => 'ReportController@index','as' => 'reports.index']); Route::get('/reports',['uses' => 'ReportController@index','as' => 'reports.index']);

View File

@ -20,7 +20,7 @@
</p> </p>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<h2><a href="{{route('accounts.create')}}">Start from scratch</a></h2> <h2><a href="{{route('accounts.create','asset')}}">Start from scratch</a></h2>
<p> <p>
Use this option if you are new to Firefly (III). Use this option if you are new to Firefly (III).
@ -29,9 +29,10 @@
@else @else
<!-- ACCOUNTS -->
<div class="row"> <div class="row">
<div class="col-lg-8 col-md-12 col-sm-12"> <div class="col-lg-8 col-md-12 col-sm-12">
<!-- ACCOUNTS -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-credit-card fa-fw"></i> <a href="#">Your accounts</a> <i class="fa fa-credit-card fa-fw"></i> <a href="#">Your accounts</a>
@ -40,6 +41,7 @@
<div id="accounts-chart" style="height:300px;"></div> <div id="accounts-chart" style="height:300px;"></div>
</div> </div>
</div> </div>
<!-- BUDGETS -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-tasks fa-fw"></i> <a href="{{route('budgets.index.date')}}">Budgets and spending</a> <i class="fa fa-tasks fa-fw"></i> <a href="{{route('budgets.index.date')}}">Budgets and spending</a>
@ -48,6 +50,7 @@
<div id="budgets"></div> <div id="budgets"></div>
</div> </div>
</div> </div>
<!-- CATEGORIES -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-bar-chart fa-fw"></i> <a href="{{route('categories.index')}}">Categories</a> <i class="fa fa-bar-chart fa-fw"></i> <a href="{{route('categories.index')}}">Categories</a>
@ -55,8 +58,15 @@
<div class="panel-body"> <div class="panel-body">
<div id="categories"></div> <div id="categories"></div>
</div> </div>
</div> </div>
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-line-chart"></i> Savings
</div>
<div class="panel-body">
Bla bla
</div>
</div>
@ -65,6 +75,16 @@
<!-- time based navigation --> <!-- time based navigation -->
@include('partials.date_nav') @include('partials.date_nav')
<!-- REMINDERS -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-line-chart"></i> Recurring transactions
</div>
<div class="panel-body">
<div id="recurring"></div>
</div>
</div>
<!-- TRANSACTIONS --> <!-- TRANSACTIONS -->
@foreach($transactions as $data) @foreach($transactions as $data)
<div class="panel panel-default"> <div class="panel panel-default">

View File

@ -57,7 +57,7 @@
Target date: {{$piggyBank->targetdate->format('M jS, Y')}}<br /> Target date: {{$piggyBank->targetdate->format('M jS, Y')}}<br />
@endif @endif
@if(!is_null($piggyBank->reminder)) @if(!is_null($piggyBank->reminder))
Next reminder: {{$piggyBank->nextReminderDate()->format('M jS, Y')}} ({{$piggyBank->reminder}}) Next reminder: TODO
@endif @endif
</p> </p>
@ -119,7 +119,7 @@
@if(!is_null($repeated->reminder)) @if(!is_null($repeated->reminder))
<small> <small>
Next reminder: {{$repeated->nextReminderDate()->format('M jS, Y')}} ({{$piggyBank->reminder}}) Next reminder: TODO
</small> </small>
@endif @endif

View File

@ -1,7 +1,7 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
<div class="row"> <div class="row">
<div class="col-lg-6 col-sm-6 col-md-12"> <div class="col-lg-6 col-sm-12 col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa fa-rotate-right"></i> {{{$recurring->name}}} <i class="fa fa-rotate-right"></i> {{{$recurring->name}}}
@ -53,6 +53,18 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-6 col-sm-12 col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
More
</div>
<div class="panel-body">
<p>
<a href="{{route('recurring.rescan',$recurring->id)}}" class="btn btn-default">Rescan old transactions</a>
</p>
</div>
</div>
</div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -1,24 +0,0 @@
<div class="modal-header">
<button type="button" class="close"
data-dismiss="modal"><span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span></button>
<h4 class="modal-title">Reminders</h4>
</div>
<div class="modal-body">
<table class="table">
@foreach($reminders as $reminder)
<tr class="reminder-row-{{$reminder->id}}">
<td>{{$reminder->render()}}</td>
<td style="width:50%;">
<a href="#" data-id="{{$reminder->id}}" class="dismiss-24 btn btn-danger btn-sm">Postpone (24hrs)</a>
<a href="#" data-id="{{$reminder->id}}" class="dismiss-forever btn btn-danger btn-sm">Dismiss (forever)</a>
<a href="#" data-id="{{$reminder->id}}" class="do-it btn btn-success btn-sm">I want to do this</a>
</td>
</tr>
@endforeach
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>

View File

@ -17,7 +17,7 @@ $(function () {
allowDecimals: false, allowDecimals: false,
labels: { labels: {
formatter: function () { formatter: function () {
if(this.value >= 1000 || this.value <= -1000) { if (this.value >= 1000 || this.value <= -1000) {
return '\u20AC ' + (this.value / 1000) + 'k'; return '\u20AC ' + (this.value / 1000) + 'k';
} }
return '\u20AC ' + this.value; return '\u20AC ' + this.value;
@ -36,10 +36,10 @@ $(function () {
text: null text: null
} }
}, },
legend: {enabled:false}, legend: {enabled: false},
tooltip: { tooltip: {
formatter: function () { formatter: function () {
return this.series.name + ': \u20AC ' + Highcharts.numberFormat(this.y,2); return this.series.name + ': \u20AC ' + Highcharts.numberFormat(this.y, 2);
} }
}, },
plotOptions: { plotOptions: {
@ -101,7 +101,7 @@ $(function () {
}, },
labels: { labels: {
formatter: function () { formatter: function () {
if(this.value >= 1000 || this.value <= -1000) { if (this.value >= 1000 || this.value <= -1000) {
return '\u20AC ' + (this.value / 1000) + 'k'; return '\u20AC ' + (this.value / 1000) + 'k';
} }
return '\u20AC ' + this.value; return '\u20AC ' + this.value;
@ -165,17 +165,17 @@ $(function () {
}, },
tooltip: { tooltip: {
formatter: function () { formatter: function () {
return this.series.name + ': \u20AC ' + Highcharts.numberFormat(this.y,2); return this.series.name + ': \u20AC ' + Highcharts.numberFormat(this.y, 2);
} }
}, },
yAxis: { yAxis: {
min: 0, min: 0,
title: {text:null}, title: {text: null},
labels: { labels: {
overflow: 'justify', overflow: 'justify',
formatter: function () { formatter: function () {
if(this.value >= 1000 || this.value <= -1000) { if (this.value >= 1000 || this.value <= -1000) {
return '\u20AC ' + (this.value / 1000) + 'k'; return '\u20AC ' + (this.value / 1000) + 'k';
} }
return '\u20AC ' + this.value; return '\u20AC ' + this.value;
@ -188,7 +188,7 @@ $(function () {
cursor: 'pointer', cursor: 'pointer',
events: { events: {
click: function (e) { click: function (e) {
if(e.point.url != null) { if (e.point.url != null) {
window.location = e.point.url; window.location = e.point.url;
} }
} }
@ -202,7 +202,7 @@ $(function () {
} }
}, },
legend: { legend: {
enabled: false, enabled: false
}, },
credits: { credits: {
enabled: false enabled: false
@ -211,5 +211,42 @@ $(function () {
}); });
}); });
$.getJSON('chart/home/recurring').success(function (data) {
$('#recurring').highcharts({
title: {
text: null
},
credits: {
enabled: false
},
tooltip: {
formatter: function () {
if (this.point.objType == 'paid') {
return this.key + ': \u20AC ' + Highcharts.numberFormat(this.y, 2);
} else {
return this.key + ': ~\u20AC ' + Highcharts.numberFormat(this.y, 2);
}
}
},
plotOptions: {
pie: {
events: {
click: function (e) {
if (e.point.url != null) {
window.location = e.point.url;
}
}
},
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: false
}
}
},
series: data
});
});
}); });