Compare commits

..

91 Commits
3.3.1 ... 3.3.2

Author SHA1 Message Date
James Cole
6dd1b4537a Merge branch 'release/3.3.2' 2015-03-14 22:06:11 +01:00
James Cole
39ca9bea72 Update read me. 2015-03-14 22:06:01 +01:00
James Cole
999103277e Updated composer. 2015-03-14 22:05:36 +01:00
James Cole
3cc57ab89c Remove hash routine from user model. 2015-03-14 22:00:57 +01:00
James Cole
4e830079de New icon. 2015-03-14 17:31:17 +01:00
James Cole
ea7231e9fe Update mail config. 2015-03-14 17:25:35 +01:00
James Cole
ddfaca6d0c Cleanup and added some icons. 2015-03-14 17:21:43 +01:00
James Cole
3b11bd0593 Fixed some basic tests. 2015-03-13 08:44:07 +01:00
James Cole
3f89057528 Fixed a bug where the home budget chart would not actually list transactions without a budget. 2015-03-13 07:10:26 +01:00
James Cole
2f1073712f Merge pull request #63 from niekvanderkooy/fix-migrations
Fix migrations
2015-03-13 07:00:55 +01:00
James Cole
678e504c48 Fix charts to match report. 2015-03-10 20:26:52 +01:00
James Cole
e69545fd95 Include shared accounts. 2015-03-10 20:07:02 +01:00
James Cole
a7ed025cd6 Fine tune query. 2015-03-10 20:05:27 +01:00
James Cole
3d8e1f2484 Forgot the javascript. 2015-03-10 20:03:44 +01:00
James Cole
c38c9608da Fine tune query. 2015-03-10 20:01:49 +01:00
James Cole
2396b75e3c Fine tune income. 2015-03-10 20:00:43 +01:00
James Cole
e5c19b7562 Small fixes. 2015-03-10 19:58:05 +01:00
James Cole
114788567d More code for previous. 2015-03-10 19:57:20 +01:00
James Cole
17ae4b7d2a See if we can easily generate reports with shared accounts. 2015-03-10 19:51:24 +01:00
Niek van der Kooy
12d652bd0e We cannot entirely drop the foreign key constraint, since it is originally created (and dropped) in v3.2.1. 2015-03-10 18:25:23 +01:00
Niek van der Kooy
ff1bed97b8 We need to include models we use
Probably some left over L5 migration stuff, since you wouldn't notice it until reversing a migration
2015-03-10 18:24:28 +01:00
Niek van der Kooy
56490f0e84 Fix error when reverting changed migration.
When reverting changed migration, the down would fail because the foreign key constraint has to be dropped before you are able to rename the column.
2015-03-10 18:04:31 +01:00
James Cole
0b028a8923 Correctly order account names. 2015-03-10 17:26:31 +01:00
James Cole
43ac541cb8 Add reminders to top right menu. 2015-03-08 20:27:07 +01:00
James Cole
5218443678 Can now successfully act on reminders. 2015-03-08 20:20:58 +01:00
James Cole
d9028ed9b7 Small optimizations to the reminders 2015-03-08 09:00:04 +01:00
James Cole
9b9bd11ebb Small fix to table fix. 2015-03-07 19:42:45 +01:00
James Cole
07011ec5f7 Tiny chart improvement because I said so 2015-03-07 19:41:56 +01:00
James Cole
c3990ac32f Small updates to icons and code. 2015-03-07 12:34:03 +01:00
James Cole
081ff3ff55 View single reminder. 2015-03-07 09:29:43 +01:00
James Cole
93996ada96 Expanded reminders. 2015-03-07 09:21:06 +01:00
James Cole
a60671d4c1 First stuff for acting on reminders. 2015-03-06 20:33:23 +01:00
James Cole
3f716cc369 First new display of piggy bank reminders. 2015-03-06 17:23:29 +01:00
James Cole
9f23cffd1b Clean up code. 2015-03-06 15:26:21 +01:00
James Cole
07c416cfbf Fix set reminder. 2015-03-06 15:24:36 +01:00
James Cole
389fcc1c8d Update reminder in database. 2015-03-06 15:21:44 +01:00
James Cole
fa07811375 Add logging for debug purposes. 2015-03-06 15:18:07 +01:00
James Cole
fc91a50979 Reimplemented a routine that creates reminders. Cannot act on them yet. 2015-03-06 15:12:07 +01:00
James Cole
f9c518f321 Smaller font for boxes. 2015-03-06 10:10:07 +01:00
James Cole
015b439f0d New icons. 2015-03-06 08:30:15 +01:00
James Cole
0675622508 Experiment with boxes. 2015-03-06 08:20:27 +01:00
James Cole
e9969bdd5f Update the ranges to reflect the current dates. 2015-03-05 21:13:38 +01:00
James Cole
1139f950ed Fixed some views. 2015-03-04 21:11:47 +01:00
James Cole
284732c7a6 Better list for bills. 2015-03-04 20:58:57 +01:00
James Cole
0cb0720d8b No popup when the amount is zero. 2015-03-04 20:57:40 +01:00
James Cole
8ad1ede0c5 Popups in the reports as we've always promised. 2015-03-04 20:47:00 +01:00
James Cole
a349aac8a4 Fix balance thing. 2015-03-04 19:09:57 +01:00
James Cole
83b169c6ef Search related transfers, cleanup. 2015-03-04 16:04:17 +01:00
James Cole
49c37baac5 Update to related manager. 2015-03-04 15:30:44 +01:00
James Cole
8d62088576 Order bills. 2015-03-04 09:44:48 +01:00
James Cole
f5437a17f8 Lots of changes. 2015-03-04 09:42:47 +01:00
James Cole
92af4e5c96 Layout tweaks. 2015-03-04 05:12:05 +01:00
James Cole
0619adb0cd Support any bill in any range. 2015-03-03 17:40:17 +01:00
James Cole
65a5107854 Small layout updates. 2015-03-03 09:35:06 +01:00
James Cole
0fcc1e252b Mail user registration confirmation. 2015-03-03 09:29:02 +01:00
James Cole
4b7f817475 Removed panel for table. 2015-03-03 09:19:16 +01:00
James Cole
0439599971 Layout update. 2015-03-03 09:15:28 +01:00
James Cole
fa344d5308 Do not update when no event connected. 2015-03-03 08:46:14 +01:00
James Cole
f0316f09ed Do not over / undercorrect 2015-03-02 20:29:10 +01:00
James Cole
b1af6bab28 Cleaning up and bug fixing. 2015-03-02 20:05:28 +01:00
James Cole
ad2aebb54d Sort account list. 2015-03-02 15:46:31 +01:00
James Cole
23fc652092 Table border. 2015-03-02 15:45:24 +01:00
James Cole
f0a5756f25 Add a panel-body for the layout. 2015-03-02 15:45:00 +01:00
James Cole
14c7ad201a Paginated the account list. 2015-03-02 15:44:06 +01:00
James Cole
08ca3c89d3 Expanded account view. 2015-03-02 15:27:36 +01:00
James Cole
edda470bf8 Add everything range 2015-03-02 13:19:13 +01:00
James Cole
7183d72e5c Update chart to include other budget repetitions. 2015-03-02 12:50:51 +01:00
James Cole
5e38ebfce5 Merge branch 'feature/date-range' into develop 2015-03-02 12:35:34 +01:00
James Cole
56e36847a5 New code for new date range thing. 2015-03-02 12:35:14 +01:00
James Cole
6dba916d02 New code & css for date range. 2015-03-02 12:04:40 +01:00
James Cole
fd57086ffd Better mostly report lists. 2015-03-02 11:54:20 +01:00
James Cole
d98d366eea Some small bug fixes in the account views. 2015-03-02 11:42:27 +01:00
James Cole
b261b0b447 Shared expense should of course be shared asset. 2015-03-02 11:27:46 +01:00
James Cole
c6042a9053 Try to fix modal dialog for piggy banks. 2015-03-01 19:47:42 +01:00
James Cole
ae1245abec Fixed update transactions. 2015-03-01 19:31:00 +01:00
James Cole
e26d2376fc Fixed piggy bank delete routine. 2015-03-01 19:26:30 +01:00
James Cole
bb36ad64a7 Moved the form. 2015-03-01 13:39:16 +01:00
James Cole
262d4f92d4 Fixed a bug where consecutive payments from one source would not be summed up. 2015-03-01 10:55:20 +01:00
James Cole
675530458c Account show didn't show anything. 2015-03-01 10:44:10 +01:00
James Cole
19e34b460f Update event. 2015-03-01 08:34:59 +01:00
James Cole
dddb8cdbc0 This loops. 2015-03-01 08:25:12 +01:00
James Cole
0d0df5f143 Scan bills when editing and creating journals. 2015-03-01 08:15:24 +01:00
James Cole
d1cf683f57 Some layout and content updates 2015-03-01 07:50:26 +01:00
James Cole
d099c33e5b No more code climate for now. 2015-02-27 21:40:49 +01:00
James Cole
21fb41545b Now with son_path variable. 2015-02-27 21:24:14 +01:00
James Cole
4c56814785 Do code coverage from phpunit. 2015-02-27 21:15:21 +01:00
James Cole
cffe05e22b Add coveralls to composer.json 2015-02-27 21:01:18 +01:00
James Cole
4237850299 Restoring code coverage 2015-02-27 20:52:49 +01:00
James Cole
b5fc36a8e1 Clean up test stuff. 2015-02-27 20:46:39 +01:00
James Cole
4586c1ef52 Removed all test cases. 2015-02-27 20:45:17 +01:00
James Cole
d6cf7c4872 Merge branch 'release/3.3.1' into develop 2015-02-27 20:42:12 +01:00
167 changed files with 5431 additions and 6191 deletions

View File

@@ -1,3 +1,3 @@
src_dir: .
coverage_clover: tests/_output/coverage.xml
json_path: tests/_output/coveralls-upload.json
coverage_clover: storage/coverage/clover.xml
json_path: storage/coverage/coveralls-upload.json

1
.gitignore vendored
View File

@@ -25,3 +25,4 @@ c3.php
db.sqlite-journal
tests/_output/*
.env
clover.xml

View File

@@ -15,12 +15,7 @@ install:
- mv -v .env.testing .env
script:
- ./tests/_data/db.sh
- php vendor/bin/codecept build
- php vendor/bin/codecept run --coverage --coverage-xml --no-exit
- phpunit
after_script:
- cp -v tests/_output/coverage.xml build/logs/clover.xml
- php vendor/bin/coveralls
- vendor/bin/test-reporter --stdout > codeclimate.json
- "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"

View File

@@ -1,4 +1,4 @@
Firefly III (v3.3.1)
Firefly III (v3.3.2)
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)

View File

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

View File

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

View File

@@ -0,0 +1,85 @@
<?php namespace FireflyIII\Handlers\Events;
use Auth;
use FireflyIII\Events\JournalCreated;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Log;
/**
* Class ConnectJournalToPiggyBank
*
* @package FireflyIII\Handlers\Events
*/
class ConnectJournalToPiggyBank
{
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event when journal is saved.
*
* @param JournalCreated $event
*
* @return void
*/
public function handle(JournalCreated $event)
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId);
/** @var PiggyBank $piggyBank */
$piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank) || $journal->transactionType->type != 'Transfer') {
return;
}
Log::debug('Found a piggy bank');
$amount = 0;
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id === $piggyBank->account_id) {
// this transaction is the relevant one.
$amount = floatval($transaction->amount);
}
}
Log::debug('Amount: ' . $amount);
if ($amount == 0) {
return;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return;
}
Log::debug('Found rep! ' . $repetition->id);
$repetition->currentamount += $amount;
$repetition->save();
PiggyBankEvent::create(
[
'piggy_bank_id' => $piggyBank->id,
'transaction_journal_id' => $journal->id,
'date' => $journal->date,
'amount' => $amount
]
);
}
}

View File

@@ -0,0 +1,53 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalSaved;
use Log;
use App;
/**
* Class RescanJournal
*
* @package FireflyIII\Handlers\Events
*/
class RescanJournal
{
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalSaved $event
*
* @return void
*/
public function handle(JournalSaved $event)
{
$journal = $event->journal;
Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')');
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$list = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get();
Log::debug('Found ' . $list->count() . ' bills to check.');
/** @var Bill $bill */
foreach ($list as $bill) {
Log::debug('Now calling bill #' . $bill->id . ' (' . $bill->name . ')');
$repository->scan($bill, $journal);
}
Log::debug('Done!');
}
}

View File

@@ -0,0 +1,64 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalSaved;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
class UpdateJournalConnection
{
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalSaved $event
*
* @return void
*/
public function handle(JournalSaved $event)
{
$journal = $event->journal;
// get the event connected to this journal:
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
if(is_null($event)) {
return;
}
$piggyBank = $event->piggyBank()->first();
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return;
}
$amount = 0;
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id === $piggyBank->account_id) {
// this transaction is the relevant one.
$amount = floatval($transaction->amount);
}
}
// update current repetition:
$diff = $amount - $event->amount;
$repetition->currentamount += $diff;
$repetition->save();
$event->amount = $amount;
$event->save();
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace FireflyIII\Helpers\Reminders;
use Amount;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
use Navigation;
/**
* Class ReminderHelper
*
* @package FireflyIII\Helpers\Reminders
*/
class ReminderHelper implements ReminderHelperInterface
{
/**
* @param PiggyBank $piggyBank
* @param Carbon $start
* @param Carbon $end
*
* @return Reminder
*/
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end)
{
$reminder = Auth::user()->reminders()
->where('remindersable_id', $piggyBank->id)
->onDates($start, $end)
->first();
if (is_null($reminder)) {
if (!is_null($piggyBank->targetdate)) {
// get ranges again, but now for the start date
$ranges = $this->getReminderRanges($piggyBank, $start);
$currentRep = $piggyBank->currentRelevantRep();
$left = $piggyBank->targetamount - $currentRep->currentamount;
$perReminder = $left / count($ranges);
} else {
$perReminder = null;
$ranges = [];
$left = 0;
}
$metaData = [
'perReminder' => $perReminder,
'rangesCount' => count($ranges),
'ranges' => $ranges,
'leftToSave' => $left,
];
// create one:
$reminder = new Reminder;
$reminder->user()->associate(Auth::user());
$reminder->startdate = $start;
$reminder->enddate = $end;
$reminder->active = true;
$reminder->metadata = $metaData;
$reminder->notnow = false;
$reminder->remindersable()->associate($piggyBank);
$reminder->save();
return $reminder;
} else {
return $reminder;
}
}
/**
* This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has
* any reminders. For example:
*
* [12 mar - 15 mar]
* [15 mar - 18 mar]
*
* etcetera.
*
* Array is filled with tiny arrays with Carbon objects in them.
*
* @param PiggyBank $piggyBank
* @param Carbon $date ;
*
* @return array
*/
public function getReminderRanges(PiggyBank $piggyBank, Carbon $date = null)
{
$ranges = [];
if (is_null($date)) {
$date = new Carbon;
}
if ($piggyBank->remind_me === false) {
return $ranges;
}
if (!is_null($piggyBank->targetdate)) {
// count back until now.
// echo 'Count back!<br>';
$start = $piggyBank->targetdate;
$end = $piggyBank->startdate;
while ($start > $end) {
$currentEnd = clone $start;
$start = Navigation::subtractPeriod($start, $piggyBank->reminder, 1);
$currentStart = clone $start;
$ranges[] = ['start' => clone $currentStart, 'end' => clone $currentEnd];
}
} else {
$start = clone $piggyBank->startdate;
while ($start < $date) {
$currentStart = clone $start;
$start = Navigation::addPeriod($start, $piggyBank->reminder, 0);
$currentEnd = clone $start;
$ranges[] = ['start' => clone $currentStart, 'end' => clone $currentEnd];
}
}
return $ranges;
}
/**
* Takes a reminder, finds the piggy bank and tells you what to do now.
* Aka how much money to put in.
*
*
* @param Reminder $reminder
*
* @return string
*/
public function getReminderText(Reminder $reminder)
{
/** @var PiggyBank $piggyBank */
$piggyBank = $reminder->remindersable;
if(is_null($piggyBank)) {
return 'Piggy bank no longer exists.';
}
if (is_null($piggyBank->targetdate)) {
return 'Add money to this piggy bank to reach your target of ' . Amount::format($piggyBank->targetamount);
}
return 'Add ' . Amount::format($reminder->metadata->perReminder) . ' to fill this piggy bank on ' . $piggyBank->targetdate->format('jS F Y');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace FireflyIII\Helpers\Reminders;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\PiggyBank;
use Carbon\Carbon;
/**
* Interface ReminderHelperInterface
*
* @package FireflyIII\Helpers\Reminders
*/
interface ReminderHelperInterface {
/**
* Takes a reminder, finds the piggy bank and tells you what to do now.
* Aka how much money to put in.
*
* @param Reminder $reminder
*
* @return string
*/
public function getReminderText(Reminder $reminder);
/**
* This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has
* any reminders. For example:
*
* [12 mar - 15 mar]
* [15 mar - 18 mar]
*
* etcetera.
*
* Array is filled with tiny arrays with Carbon objects in them.
*
* @param PiggyBank $piggyBank
*
* @return array
*/
public function getReminderRanges(PiggyBank $piggyBank);
/**
* @param PiggyBank $piggyBank
* @param Carbon $start
* @param Carbon $end
*
* @return Reminder
*/
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end);
}

View File

@@ -7,6 +7,7 @@ use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
/**
* Class ReportHelper
@@ -35,19 +36,6 @@ class ReportHelper implements ReportHelperInterface
}
/**
* @return Carbon
*/
public function firstDate()
{
$journal = Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first();
if ($journal) {
return $journal->date;
}
return Carbon::now();
}
/**
* This method gets some kind of list for a monthly overview.
*
@@ -62,7 +50,7 @@ class ReportHelper implements ReportHelperInterface
$end = clone $date;
$end->endOfMonth();
// all budgets
$set = \Auth::user()->budgets()
$set = Auth::user()->budgets()
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
@@ -99,7 +87,8 @@ class ReportHelper implements ReportHelperInterface
$end = Carbon::now();
$months = [];
while ($start <= $end) {
$months[] = [
$year = $start->format('Y');
$months[$year][] = [
'formatted' => $start->format('F Y'),
'month' => intval($start->format('m')),
'year' => intval($start->format('Y')),
@@ -130,43 +119,47 @@ class ReportHelper implements ReportHelperInterface
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date)
public function yearBalanceReport(Carbon $date, $showSharedReports = false)
{
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
$sharedCollection = \Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedExpense'))
->get(['accounts.id']);
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
if ($showSharedReports === false) {
$sharedCollection = \Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedAsset'))
->get(['accounts.id']);
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
}
}
$accounts = \Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*'])
->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
return null;
}
);
return null;
}
);
$report = [];
$start->startOfYear()->subDay();
$end->endOfYear();
foreach ($accounts as $account) {
$report[] = [
'start' => \Steam::balance($account, $start),
'end' => \Steam::balance($account, $end),
'start' => Steam::balance($account, $start),
'end' => Steam::balance($account, $end),
'account' => $account,
'shared' => $account->accountRole == 'sharedExpense'
'shared' => $account->accountRole == 'sharedAsset'
];
}

View File

@@ -14,32 +14,6 @@ interface ReportHelperInterface
{
/**
* @return Carbon
*/
public function firstDate();
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function yearBalanceReport(Carbon $date);
/**
* This methods fails to take in account transfers FROM shared accounts.
*
@@ -59,4 +33,26 @@ interface ReportHelperInterface
* @return Collection
*/
public function getBudgetsForMonth(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false);
}

View File

@@ -1,10 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 22/02/15
* Time: 18:30
*/
namespace FireflyIII\Helpers\Report;
@@ -29,26 +23,33 @@ class ReportQuery implements ReportQueryInterface
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @param bool $showSharedReports
*
* @return Collection
*/
public function accountList()
public function accountList($showSharedReports = false)
{
return Auth::user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedExpense"');
$query->orWhereNull('account_meta.data');
}
)
->get(['accounts.*']);
$query = Auth::user()->accounts();
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedAsset"');
$query->orWhereNull('account_meta.data');
}
);
}
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->orderBy('accounts.name', 'ASC');
return $query->get(['accounts.*']);
}
/**
@@ -82,19 +83,37 @@ class ReportQuery implements ReportQueryInterface
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
->before($end)->after($start)
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', \Auth::user()->id)
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->first(
->get(
[
DB::Raw('SUM(`transactions`.`amount`) as `amount`')
'transaction_journals.*',
'transactions.amount'
]
);
return $set;
}
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end)
{
$set = $this->balancedTransactionsList($account, $start, $end);
$sum = 0;
if (!is_null($set)) {
$sum = floatval($set->amount);
foreach ($set as $entry) {
$sum += floatval($entry->amount);
}
return $sum;
@@ -105,25 +124,31 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end)
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false)
{
$set = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account'])
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedExpense"');
$query->orWhereNull('account_meta.data');
}
)
->get(['accounts.*']);
$query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account']);
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->orderBy('accounts.name', 'ASC')
->where(
function (Builder $query) use ($showSharedReports) {
$query->where('account_meta.data', '!=', '"sharedAsset"');
$query->orWhereNull('account_meta.data');
}
);
}
$set = $query->get(['accounts.*']);
$set->each(
function (Account $account) use ($start, $end) {
/** @noinspection PhpParamsInspection */
@@ -167,7 +192,40 @@ class ReportQuery implements ReportQueryInterface
return $set;
}
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->whereNull('budgets.id')
->orderBy('transaction_journals.date', 'ASC')
->get(['budgets.name', 'transactions.amount', 'transaction_journals.*']);
return $set;
}
/**
@@ -177,62 +235,73 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end)
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false)
{
return TransactionJournal::
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date')
->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
't_to.amount', 'transaction_journals.date', 't_from.account_id as account_id',
'ac_from.name as name']
);
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
if ($showSharedReports === false) {
// only get deposits not to a shared account
// and transfers to a shared account.
$query->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any deposit is fine.
$query->where('transaction_types.type', 'Deposit');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date');
return $query->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
DB::Raw('SUM(`t_to`.`amount`) as `amount`'),
'transaction_journals.date',
't_from.account_id as account_id',
'ac_from.name as name']
);
}
/**
@@ -240,33 +309,37 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end)
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false)
{
return Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('account_meta.data', '!=', '"sharedExpense"')
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC')
->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
$query = Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
}
/**
@@ -275,35 +348,38 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end)
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false)
{
return Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('account_meta.data', '!=', '"sharedExpense"')
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('amount')
->get(['categories.id', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]);
$query = Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('amount');
return $query->get(['categories.id', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]);
}
@@ -315,57 +391,65 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end)
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
return TransactionJournal::
leftJoin(
$query = TransactionJournal::leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)
->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('amount', 'DESC')
->get(['t_to.account_id as id', 'ac_to.name as name', DB::Raw('SUM(t_to.amount) as `amount`')]);
)->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts
// and all transfers to a shared account
$query->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal');
}
$query->before($end)
->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('amount', 'DESC');
return $query->get(['t_to.account_id as id', 'ac_to.name as name', DB::Raw('SUM(t_to.amount) as `amount`')]);
}
/**
@@ -373,55 +457,65 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end)
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
return TransactionJournal::
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount')
->get(['t_from.account_id as account_id', 'ac_from.name as name', DB::Raw('SUM(t_from.amount) as `amount`')]);
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account
// or where its a transfer and its from a shared account (both count as incomes)
$query->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any deposit goes:
$query->where('transaction_types.type', 'Deposit');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount');
return $query->get(['t_from.account_id as account_id', 'ac_from.name as name', DB::Raw('SUM(t_from.amount) as `amount`')]);
}
/**
@@ -450,7 +544,7 @@ class ReportQuery implements ReportQueryInterface
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '"sharedExpense"')
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
@@ -492,7 +586,7 @@ class ReportQuery implements ReportQueryInterface
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->where('account_meta.data', '"sharedExpense"')
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')

View File

@@ -17,19 +17,69 @@ interface ReportQueryInterface
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @param bool $showSharedReports
*
* @return Collection
*/
public function accountList();
public function accountList($showSharedReports = false);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end);
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end);
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end);
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end);
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end);
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
@@ -38,20 +88,22 @@ interface ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end);
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end);
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
@@ -59,10 +111,11 @@ interface ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end);
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
@@ -72,20 +125,22 @@ interface ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end);
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end);
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
@@ -108,27 +163,4 @@ interface ReportQueryInterface
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end);
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end);
}

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Http\Controllers;
use App;
use Auth;
use Carbon\Carbon;
use Config;
@@ -8,9 +7,11 @@ use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Input;
use Redirect;
use Session;
use Steam;
use View;
/**
@@ -74,6 +75,12 @@ class AccountController extends Controller
return Redirect::route('accounts.index', $typeName);
}
/**
* @param Account $account
* @param AccountRepositoryInterface $repository
*
* @return View
*/
public function edit(Account $account, AccountRepositoryInterface $repository)
{
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
@@ -110,29 +117,59 @@ class AccountController extends Controller
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what);
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$accounts = Auth::user()->accounts()->accountTypeIn($types)->get(['accounts.*']);
$size = 50;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$offset = ($page - 1) * $size;
// move to repository:
$set = Auth::user()->accounts()->with(
['accountmeta' => function ($query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn($types)->take($size)->offset($offset)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$total = Auth::user()->accounts()->accountTypeIn($types)->count();
// last activity:
$start = clone Session::get('start');
$start->subDay();
$set->each(
function (Account $account) use ($start) {
$lastTransaction = $account->transactions()->leftJoin(
'transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->orderBy('transaction_journals.date', 'DESC')->first(['transactions.*', 'transaction_journals.date']);
if ($lastTransaction) {
$account->lastActivityDate = $lastTransaction->transactionjournal->date;
} else {
$account->lastActivityDate = null;
}
$account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, Session::get('end'));
}
);
$accounts = new LengthAwarePaginator($set, $total, $size, $page);
$accounts->setPath(route('accounts.index', $what));
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
}
/**
* @param Account $account
* @param string $range
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\View\View
* @return View
*/
public function show(Account $account, $range = 'session')
public function show(Account $account, AccountRepositoryInterface $repository)
{
/** @var \FireflyIII\Repositories\Account\AccountRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type);
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page, $range);
$journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
return view('accounts.show', compact('account', 'what', 'range', 'subTitleIcon', 'journals', 'subTitle'));
return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle'));
}
/**
@@ -158,6 +195,10 @@ 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.index', $request->input('what'));
}
@@ -186,6 +227,10 @@ class AccountController extends Controller
Session::flash('success', 'Account "' . $account->name . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('accounts.edit', $account->id);
}
return Redirect::route('accounts.index', $what);
}

View File

@@ -4,6 +4,9 @@ use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;
use Mail;
use Session;
/**
* Class AuthController
@@ -43,4 +46,40 @@ class AuthController extends Controller
$this->middleware('guest', ['except' => 'getLogout']);
}
/**
* Handle a registration request for the application.
*
* @param Request $request
*
* @return \Illuminate\Http\Response
*/
public function postRegister(Request $request)
{
$validator = $this->registrar->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$this->auth->login($this->registrar->create($request->all()));
// get the email address
$email = $this->auth->user()->email;
// send email.
Mail::send(
'emails.registered', [], function ($message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III!');
}
);
// set flash message
Session::flash('success', 'You have registered successfully!');
return redirect($this->redirectPath());
}
}

View File

@@ -8,6 +8,7 @@ use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input;
use Redirect;
use Session;
use URL;
@@ -80,7 +81,7 @@ class BillController extends Controller
*/
public function index(BillRepositoryInterface $repository)
{
$bills = Auth::user()->bills()->get();
$bills = Auth::user()->bills()->orderBy('name', 'ASC')->get();
$bills->each(
function (Bill $bill) use ($repository) {
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
@@ -108,7 +109,9 @@ class BillController extends Controller
return Redirect::intended('/');
}
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(['transaction_journal_id']);
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(
['transaction_journal_id']
);
$ids = [];
/** @var Transaction $entry */
@@ -116,7 +119,7 @@ class BillController extends Controller
$ids[] = intval($entry->transaction_journal_id);
}
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id',$ids)->get();
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$repository->scan($bill, $journal);
@@ -168,6 +171,10 @@ class BillController extends Controller
$bill = $repository->store($billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
if (intval(Input::get('create_another')) === 1) {
return Redirect::route('bills.create')->withInput();
}
return Redirect::route('bills.index');
}
@@ -195,6 +202,10 @@ class BillController extends Controller
$bill = $repository->update($bill, $billData);
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('bills.edit', $bill->id);
}
Session::flash('success', 'Bill "' . e($bill->name) . '" updated.');
return Redirect::route('bills.index');

View File

@@ -7,10 +7,10 @@ use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Input;
use Redirect;
use Session;
use View;
use Input;
/**
@@ -38,44 +38,6 @@ class CategoryController extends Controller
return view('categories.create')->with('subTitle', 'Create a new category');
}
/**
* @param Category $category
*
* @return $this
*/
public function show(Category $category, CategoryRepositoryInterface $repository)
{
$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(['transaction_journals.*']);
$count = $category->transactionJournals()->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page);
return view('categories.show', compact('category', 'journals', 'hideCategory'));
}
/**
* @return \Illuminate\View\View
*/
public function noCategory()
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth());
$list = Auth::user()
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category in ' . $start->format('F Y');
return view('categories.noCategory', compact('list', 'subTitle'));
}
/**
* @param Category $category
*
@@ -122,11 +84,61 @@ class CategoryController extends Controller
*/
public function index()
{
$categories = Auth::user()->categories()->get();
$categories = Auth::user()->categories()->orderBy('name', 'ASC')->get();
$categories->each(
function (Category $category) {
$latest = $category->transactionjournals()->orderBy('date', 'DESC')->first();
if ($latest) {
$category->lastActivity = $latest->date;
}
}
);
return view('categories.index', compact('categories'));
}
/**
* @return \Illuminate\View\View
*/
public function noCategory()
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth());
$list = Auth::user()
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
return view('categories.noCategory', compact('list', 'subTitle'));
}
/**
* @param Category $category
*
* @return $this
*/
public function show(Category $category, CategoryRepositoryInterface $repository)
{
$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(
['transaction_journals.*']
);
$count = $category->transactionJournals()->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page);
return view('categories.show', compact('category', 'journals', 'hideCategory'));
}
/**
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository

View File

@@ -1,14 +1,16 @@
<?php namespace FireflyIII\Http\Controllers;
use Cache;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\CurrencyFormRequest;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use Input;
use Preferences;
use Redirect;
use Session;
use View;
use Cache;
/**
* Class CurrencyController
@@ -145,6 +147,10 @@ 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.index');
@@ -163,7 +169,12 @@ class CurrencyController extends Controller
$currency->name = $request->get('name');
$currency->save();
Session::flash('success', 'Currency "' . e($currency->namename) . '" updated.');
Session::flash('success', 'Currency "' . e($currency->name) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('currency.edit', $currency->id);
}
return Redirect::route('currency.index');

View File

@@ -3,7 +3,7 @@
use App;
use Auth;
use Carbon\Carbon;
use Crypt;
use DB;
use Exception;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
@@ -13,17 +13,19 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
use DB;
use Steam;
/**
@@ -41,27 +43,14 @@ class GoogleChartController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountBalanceChart(Account $account, $view = 'session', GChart $chart)
public function accountBalanceChart(Account $account, GChart $chart)
{
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$count = $account->transactions()->count();
if ($view == 'all' && $count > 0) {
$first = $account->transactions()->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->orderBy(
'date', 'ASC'
)->first(['transaction_journals.date']);
$last = $account->transactions()->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->orderBy(
'date', 'DESC'
)->first(['transaction_journals.date']);
$start = new Carbon($first->date);
$end = new Carbon($last->date);
}
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
while ($end >= $current) {
@@ -89,9 +78,9 @@ class GoogleChartController extends Controller
$end = Session::get('end', Carbon::now()->endOfMonth());
if ($frontPage->data == []) {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
$accounts = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->get(['accounts.*']);
$accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
}
$index = 1;
/** @var Account $account */
@@ -177,27 +166,35 @@ class GoogleChartController extends Controller
/** @var Budget $budget */
foreach ($budgets as $budget) {
/** @var \LimitRepetition $repetition */
$repetition = LimitRepetition::
/** @var Collection $repetitions */
$repetitions = LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
if (is_null($repetition)) { // use the session start and end for our search query
$searchStart = $start;
$searchEnd = $end;
$limit = 0; // the limit is zero:
->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'))
->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->get(['limit_repetitions.*']);
// no results? search entire range for expenses and list those.
if ($repetitions->count() == 0) {
$expenses = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name, 0, $expenses);
}
} else {
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
$limit = floatval($repetition->amount); // the limit is the repetitions limit:
// add with foreach:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses
=
floatval($budget->transactionjournals()->before($repetition->enddate)->after($repetition->startdate)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name . ' (' . $repetition->startdate->format('j M Y') . ')', floatval($repetition->amount), $expenses);
}
}
}
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name, $limit, $expenses);
}
}
$noBudgetSet = Auth::user()
@@ -209,7 +206,8 @@ class GoogleChartController extends Controller
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'));
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'))
->whereNotNull('budget_transaction_journal.budget_id');
}
)
->before($end)
@@ -315,49 +313,45 @@ class GoogleChartController extends Controller
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billsOverview(GChart $chart)
public function billsOverview(GChart $chart, BillRepositoryInterface $repository)
{
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
$bills = Auth::user()->bills()->where('active', 1)->get();
$set = Bill::
leftJoin(
'transaction_journals', function (JoinClause $join) use ($start, $end) {
$join->on('bills.id', '=', 'transaction_journals.bill_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'));
}
)
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '>', 0);
}
)
->where('active', 1)
->groupBy('bills.id')
->get(
['bills.id', 'bills.name', 'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_journals.id as journalId',
\DB::Raw('SUM(`bills`.`amount_min` + `bills`.`amount_max`) / 2 as `averageAmount`'),
'transactions.amount AS actualAmount']
);
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count == 0) {
$unpaid['items'][] = $bill->name . ' (' . $range['start']->format('jS M Y') . ')';
$unpaid['amount'] += ($bill->amount_max + $bill->amount_min / 2);
} else {
$journal = $bill->transactionjournals()->with('transactions')->before($range['end'])->after($range['start'])->first();
$paid['items'][] = $journal->description;
$amount = 0;
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0) {
$amount = floatval($t->amount);
}
}
$paid['amount'] += $amount;
}
foreach ($set as $entry) {
if (intval($entry->journalId) == 0) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += floatval($entry->averageAmount);
} else {
$description = intval($entry->encrypted) == 1 ? Crypt::decrypt($entry->description) : $entry->description;
$paid['items'][] = $description;
$paid['amount'] += floatval($entry->actualAmount);
}
}
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();
@@ -463,36 +457,32 @@ class GoogleChartController extends Controller
/**
*
* @param Category $category
* @param $year
* @param Category $category
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoriesAndSpending(Category $category, $year, GChart $chart)
public function categoryOverviewChart(Category $category, GChart $chart)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
// oldest transaction in category:
/** @var TransactionJournal $first */
$first = $category->transactionjournals()->orderBy('date', 'ASC')->first();
$start = $first->date;
/** @var Preference $range */
$range = Preferences::get('viewRange', '1M');
// jump to start of week / month / year / etc (TODO).
$start = Navigation::startOfPeriod($start, $range->data);
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
$end = new Carbon;
while ($start <= $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
$spent = floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
$budgeted = null;
$currentEnd = Navigation::endOfPeriod($start, $range->data);
$spent = floatval($category->transactionjournals()->before($currentEnd)->after($start)->lessThan(0)->sum('amount')) * -1;
$chart->addRow(clone $start, $spent);
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
$start = Navigation::addPeriod($start, $range->data, 0);
}
@@ -503,6 +493,35 @@ class GoogleChartController extends Controller
}
/**
*
* @param Category $category
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoryPeriodChart(Category $category, GChart $chart)
{
// oldest transaction in category:
/** @var TransactionJournal $first */
$start = Session::get('start');
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = Session::get('end');
while ($start <= $end) {
$spent = floatval($category->transactionjournals()->onDate($start)->lessThan(0)->sum('amount')) * -1;
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param PiggyBank $piggyBank
*
@@ -542,6 +561,9 @@ class GoogleChartController extends Controller
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
// get report query interface.
$end = clone $start;
@@ -550,14 +572,14 @@ class GoogleChartController extends Controller
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$income = $query->incomeByPeriod($start, $currentEnd);
$income = $query->incomeByPeriod($start, $currentEnd, $showSharedReports);
$incomeSum = 0;
foreach ($income as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expense = $query->journalsByExpenseAccount($start, $currentEnd);
$expense = $query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports);
$expenseSum = 0;
foreach ($expense as $entry) {
$expenseSum += floatval($entry->amount);
@@ -591,6 +613,9 @@ class GoogleChartController extends Controller
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$income = 0;
$expense = 0;
$count = 0;
@@ -601,14 +626,14 @@ class GoogleChartController extends Controller
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$incomeResult = $query->incomeByPeriod($start, $currentEnd);
$incomeResult = $query->incomeByPeriod($start, $currentEnd, $showSharedReports);
$incomeSum = 0;
foreach ($incomeResult as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expenseResult = $query->journalsByExpenseAccount($start, $currentEnd);
$expenseResult = $query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports);
$expenseSum = 0;
foreach ($expenseResult as $entry) {
$expenseSum += floatval($entry->amount);

View File

@@ -1,21 +1,19 @@
<?php namespace FireflyIII\Http\Controllers;
use Cache;
use ErrorException;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use League\CommonMark\CommonMarkConverter;
use Route;
use Response;
use Cache;
use Route;
/**
* Class HelpController
*
* @package FireflyIII\Http\Controllers
*/
class HelpController extends Controller {
class HelpController extends Controller
{
/**
* @param $route
@@ -83,7 +81,7 @@ class HelpController extends Controller {
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>There is no help for this route.</p>';
}
$converter = new CommonMarkConverter();
$converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']);
return $content;

View File

@@ -3,11 +3,11 @@
use Auth;
use Cache;
use Carbon\Carbon;
use Navigation;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Preferences;
use Redirect;
use Session;
use URL;
/**
* Class HomeController
@@ -24,6 +24,21 @@ class HomeController extends Controller
{
}
public function dateRange()
{
$start = new Carbon(Input::get('start'));
$end = new Carbon(Input::get('end'));
$diff = $start->diffInDays($end);
if ($diff > 50) {
Session::flash('warning', $diff . ' days of data may take a while to load.');
}
Session::put('start', $start);
Session::put('end', $end);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
@@ -37,9 +52,10 @@ class HomeController extends Controller
/**
* @return \Illuminate\View\View
*/
public function index()
public function index(AccountRepositoryInterface $repository)
{
$count = Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->count();
$count = $repository->countAssetAccounts();
$title = 'Firefly';
$subTitle = 'What\'s playing?';
$mainTitleIcon = 'fa-fire';
@@ -47,25 +63,10 @@ class HomeController extends Controller
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
if ($frontPage->data == []) {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->get(['accounts.*']);
}
$accounts = $repository->getFrontpageAccounts($frontPage);
foreach ($accounts as $account) {
$set = Auth::user()
->transactionjournals()
->with(['transactions', 'transactioncurrency', 'transactiontype'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id)
->where('date', '>=', $start->format('Y-m-d'))
->where('date', '<=', $end->format('Y-m-d'))
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->take(10)
->get(['transaction_journals.*']);
$set = $repository->getFrontpageTransactions($account, $start, $end);
if (count($set) > 0) {
$transactions[] = [$set, $account];
}
@@ -76,49 +77,5 @@ class HomeController extends Controller
return view('index', compact('count', 'title', 'subTitle', 'mainTitleIcon', 'transactions'));
}
/**
* @param string $range
*
* @return mixed
*/
public function rangeJump($range)
{
$valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
if (in_array($range, $valid)) {
Preferences::set('viewRange', $range);
Session::forget('range');
}
return Redirect::to(URL::previous());
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function sessionNext()
{
$range = Session::get('range');
$start = Session::get('start');
Session::put('start', Navigation::jumpToNext($range, clone $start));
return Redirect::to(URL::previous());
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function sessionPrev()
{
$range = Session::get('range');
$start = Session::get('start');
Session::put('start', Navigation::jumpToPrevious($range, clone $start));
return Redirect::to(URL::previous());
}
}

View File

@@ -1,20 +1,113 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Response;
use Amount;
use Auth;
use DB;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input;
use Preferences;
use Response;
use Session;
/**
* Class JsonController
*
* @package FireflyIII\Http\Controllers
*/
class JsonController extends Controller {
class JsonController extends Controller
{
/**
*
*/
public function box(BillRepositoryInterface $repository)
{
$amount = 0;
$start = Session::get('start');
$end = Session::get('end');
$box = 'empty';
switch (Input::get('box')) {
case 'in':
$box = Input::get('box');
$in = Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->before($end)
->after($start)
->transactionTypes(['Deposit'])
->where('transactions.amount', '>', 0)
->first([DB::Raw('SUM(transactions.amount) as `amount`')]);
if (!is_null($in)) {
$amount = floatval($in->amount);
}
break;
case 'out':
$box = Input::get('box');
$in = Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->before($end)
->after($start)
->transactionTypes(['Withdrawal'])
->where('transactions.amount', '>', 0)
->first([DB::Raw('SUM(transactions.amount) as `amount`')]);
if (!is_null($in)) {
$amount = floatval($in->amount);
}
break;
case 'bills-unpaid':
$box = 'bills-unpaid';
$bills = Auth::user()->bills()->where('active', 1)->get();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count == 0) {
$amount += floatval($bill->amount_max + $bill->amount_min / 2);
}
}
}
break;
case 'bills-paid':
$box = 'bills-paid';
// these two functions are the same as the chart TODO
$bills = Auth::user()->bills()->where('active', 1)->get();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count != 0) {
$journal = $bill->transactionjournals()->with('transactions')->before($range['end'])->after($range['start'])->first();
$paid['items'][] = $journal->description;
$currentAmount = 0;
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0) {
$currentAmount = floatval($t->amount);
}
}
$amount += $currentAmount;
}
}
}
}
return Response::json(['box' => $box, 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
}
/**
* Returns a list of categories.
*
@@ -22,15 +115,13 @@ class JsonController extends Controller {
*/
public function categories()
{
$list = Auth::user()->categories()->orderBy('name','ASC')->get();
$return = [];
$list = Auth::user()->categories()->orderBy('name', 'ASC')->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
}
return Response::json($return);
}
/**
@@ -40,8 +131,8 @@ class JsonController extends Controller {
*/
public function expenseAccounts()
{
$list = Auth::user()->accounts()->accountTypeIn(['Expense account', 'Beneficiary account'])->get();
$return = [];
$list = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Expense account', 'Beneficiary account'])->get();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
}
@@ -55,8 +146,8 @@ class JsonController extends Controller {
*/
public function revenueAccounts()
{
$list = Auth::user()->accounts()->accountTypeIn(['Revenue account'])->get();
$return = [];
$list = Auth::user()->accounts()->accountTypeIn(['Revenue account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
}
@@ -65,4 +156,27 @@ class JsonController extends Controller {
}
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
return Response::json(['value' => $pref->data]);
}
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function setSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
$new = !$pref->data;
Preferences::set('showSharedReports', $new);
return Response::json(['value' => $new]);
}
}

View File

@@ -9,6 +9,7 @@ use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\PiggyBankFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Support\Collection;
@@ -62,7 +63,9 @@ class PiggyBankController extends Controller
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
$subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus';
@@ -78,7 +81,7 @@ class PiggyBankController extends Controller
{
$subTitle = 'Delete "' . e($piggyBank->name) . '"';
return view('piggy_banks.delete', compact('piggyBank', 'subTitle'));
return view('piggy-banks.delete', compact('piggyBank', 'subTitle'));
}
/**
@@ -90,9 +93,9 @@ class PiggyBankController extends Controller
{
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$this->_repository->destroy($piggyBank);
$piggyBank->delete();
return Redirect::route('piggy_banks.index');
return Redirect::route('piggy-banks.index');
}
/**
@@ -106,7 +109,9 @@ class PiggyBankController extends Controller
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
$subTitle = 'Edit piggy bank "' . e($piggyBank->name) . '"';
$subTitleIcon = 'fa-pencil';
@@ -124,7 +129,7 @@ class PiggyBankController extends Controller
'targetamount' => $piggyBank->targetamount,
'targetdate' => $targetDate,
'reminder' => $piggyBank->reminder,
'remind_me' => intval($piggyBank->remind_me) == 1 || !is_null($piggyBank->reminder) ? true : false
'remind_me' => intval($piggyBank->remind_me) == 1 && !is_null($piggyBank->reminder) ? true : false
];
Session::flash('preFilled', $preFilled);
@@ -191,6 +196,9 @@ class PiggyBankController extends Controller
$repetition->currentamount += $amount;
$repetition->save();
// create event.
PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount, 'piggy_bank_id' => $piggyBank->id]);
/*
* Create event!
*/
@@ -220,6 +228,8 @@ class PiggyBankController extends Controller
$repetition->currentamount -= $amount;
$repetition->save();
PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount * -1, 'piggy_bank_id' => $piggyBank->id]);
/*
* Create event!
*/
@@ -278,12 +288,18 @@ class PiggyBankController extends Controller
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'reminder' => $request->get('reminder'),
'remind_me' => $request->get('remind_me'),
];
$piggyBank = $repository->store($piggyBankData);
Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('create_another')) === 1) {
return Redirect::route('piggy-banks.create')->withInput();
}
return Redirect::route('piggy-banks.index');
}
@@ -299,21 +315,28 @@ class PiggyBankController extends Controller
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'reminder' => $request->get('reminder'),
'remind_me' => $request->get('remind_me')
];
$piggyBank = $repository->update($piggyBank, $piggyBankData);
Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('piggy-banks.edit', $piggyBank->id);
}
return Redirect::route('piggy-banks.index');
}
}

View File

@@ -1,21 +1,20 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use View;
use Auth;
use Preferences;
use FireflyIII\Http\Requests;
use Input;
use Session;
use Preferences;
use Redirect;
use Session;
use View;
/**
* Class PreferencesController
*
* @package FireflyIII\Http\Controllers
*/
class PreferencesController extends Controller {
class PreferencesController extends Controller
{
/**
*
@@ -32,7 +31,7 @@ class PreferencesController extends Controller {
*/
public function index()
{
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$viewRange = Preferences::get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
$frontPage = Preferences::get('frontPageAccounts', []);

View File

@@ -1,15 +1,16 @@
<?php namespace FireflyIII\Http\Controllers;
use Amount;
use Auth;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Response;
use Input;
use Redirect;
use Response;
use URL;
/**
* Class RelatedController
@@ -37,11 +38,12 @@ class RelatedController extends Controller
}
}
}
$unique = array_unique($ids);
$unique = array_unique($ids);
$journals = new Collection;
if (count($unique) > 0) {
$set = Auth::user()->transactionjournals()->whereIn('id', $unique)->get();
$set->each(
$journals = Auth::user()->transactionjournals()->whereIn('id', $unique)->get();
$journals->each(
function (TransactionJournal $journal) {
/** @var Transaction $t */
foreach ($journal->transactions()->get() as $t) {
@@ -52,11 +54,38 @@ class RelatedController extends Controller
}
);
return Response::json($set->toArray());
} else {
return Response::json((new Collection)->toArray());
}
$parent = $journal;
return view('related.alreadyRelated', compact('parent', 'journals'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function getRemoveRelation(TransactionJournal $parentJournal, TransactionJournal $childJournal)
{
$groups = $parentJournal->transactiongroups()->get();
/** @var TransactionGroup $group */
foreach ($groups as $group) {
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id == $childJournal->id) {
// remove from group:
$group->transactionjournals()->detach($childJournal);
}
}
if ($group->transactionjournals()->count() == 1) {
$group->delete();
}
}
return Redirect::to(URL::previous());
}
/**
@@ -137,20 +166,12 @@ class RelatedController extends Controller
{
$search = e(trim(Input::get('searchValue')));
$parent = $journal;
$result = $repository->searchRelated($search, $journal);
$result->each(
function (TransactionJournal $journal) {
/** @var Transaction $t */
foreach ($journal->transactions()->get() as $t) {
if ($t->amount > 0) {
$journal->amount = $t->amount;
}
}
}
);
$journals = $repository->searchRelated($search, $journal);
return view('related.searchResult', compact('journals', 'search', 'parent'));
return Response::json($result->toArray());
}
}

View File

@@ -0,0 +1,128 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use FireflyIII\Helpers\Reminders\ReminderHelperInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Reminder;
use Redirect;
use Session;
use URL;
/**
* Class ReminderController
*
* @package FireflyIII\Http\Controllers
*/
class ReminderController extends Controller
{
/**
* @param Reminder $reminder
*/
public function act(Reminder $reminder)
{
$data = [
'description' => 'Money for piggy bank "' . $reminder->remindersable->name . '"',
'amount' => round($reminder->metadata->perReminder, 2),
'account_to_id' => $reminder->remindersable->account_id,
'piggy_bank_id' => $reminder->remindersable_id,
'reminder_id' => $reminder->id,
];
Session::flash('_old_input', $data);
return Redirect::route('transactions.create', 'transfer');
}
/**
* @param Reminder $reminder
*/
public function dismiss(Reminder $reminder)
{
$reminder->notnow = true;
$reminder->save();
return Redirect::to(URL::previous());
}
/**
*
*/
public function index(ReminderHelperInterface $helper)
{
$reminders = Auth::user()->reminders()->get();
$reminders->each(
function (Reminder $reminder) use ($helper) {
$reminder->description = $helper->getReminderText($reminder);
}
);
$today = new Carbon;
// active reminders:
$active = $reminders->filter(
function (Reminder $reminder) use ($today) {
if ($reminder->notnow === false && $reminder->active === true && $reminder->startdate <= $today && $reminder->enddate >= $today) {
return $reminder;
}
}
);
// expired reminders:
$expired = $reminders->filter(
function (Reminder $reminder) use ($today) {
if ($reminder->notnow === false && $reminder->active === true && $reminder->startdate > $today || $reminder->enddate < $today) {
return $reminder;
}
}
);
// inactive reminders
$inactive = $reminders->filter(
function (Reminder $reminder) use ($today) {
if ($reminder->active === false) {
return $reminder;
}
}
);
// dismissed reminders
$dismissed = $reminders->filter(
function (Reminder $reminder) use ($today) {
if ($reminder->notnow === true) {
return $reminder;
}
}
);
$title = 'Reminders';
$mainTitleIcon = 'fa-clock-o';
return view('reminders.index', compact('dismissed', 'expired', 'inactive', 'active', 'title', 'mainTitleIcon'));
}
/**
* @param Reminder $reminder
*/
public function show(Reminder $reminder)
{
$title = 'Reminder';
$mainTitleIcon = 'fa-clock-o';
if ($reminder->notnow === true) {
$subTitle = 'Dismissed reminder';
} else {
$subTitle = 'Reminder';
}
$subTitle .= ' for piggy bank "' . $reminder->remindersable->name . '"';
return view('reminders.show', compact('reminder', 'title', 'subTitle', 'mainTitleIcon'));
}
}

View File

@@ -36,7 +36,9 @@ class RepeatedExpenseController extends Controller
public function create()
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
return view('repeatedExpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with(
'subTitleIcon', 'fa-plus'
@@ -79,7 +81,9 @@ class RepeatedExpenseController extends Controller
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']));
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
$subTitle = 'Edit repeated expense "' . e($repeatedExpense->name) . '"';
$subTitleIcon = 'fa-pencil';
@@ -183,7 +187,7 @@ class RepeatedExpenseController extends Controller
'rep_length' => $request->get('rep_length'),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
'remind_me' => intval($request->get('remind_me')) == 1 ? true : false ,
'remind_me' => intval($request->get('remind_me')) == 1 ? true : false,
'reminder' => $request->get('reminder'),
];

View File

@@ -7,7 +7,10 @@ use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Preferences;
use Session;
use Steam;
use View;
@@ -49,17 +52,22 @@ class ReportController extends Controller
$end->endOfMonth();
$start->subDay();
// shared accounts preference:
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$dayEarly = clone $date;
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$dayEarly = $dayEarly->subDay();
$accounts = $query->getAllAccounts($start, $end);
$accounts = $query->getAllAccounts($start, $end, $showSharedReports);
$start->addDay();
$accounts->each(
function (Account $account) use ($start, $end, $query) {
$budgets = $query->getBudgetSummary($account, $start, $end);
$balancedAmount = $query->balancedTransactionsList($account, $start, $end);
$balancedAmount = $query->balancedTransactionsSum($account, $start, $end);
$array = [];
foreach ($budgets as $budget) {
$id = intval($budget->id);
@@ -87,24 +95,27 @@ class ReportController extends Controller
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
// find transactions to shared asset accounts, which are without a budget by default:
// which is only relevant when shared asset accounts are hidden.
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
/**
* End getBudgetsForMonth DONE
*/
return view('reports.budget', compact('subTitle', 'subTitleIcon', 'date', 'accounts', 'budgets', 'dayEarly'));
return view('reports.budget', compact('subTitle', 'year', 'month', 'subTitleIcon', 'date', 'accounts', 'budgets', 'dayEarly'));
}
@@ -115,7 +126,7 @@ class ReportController extends Controller
*/
public function index(ReportHelperInterface $helper)
{
$start = $helper->firstDate();
$start = Session::get('first');
$months = $helper->listOfMonths($start);
$years = $helper->listOfYears($start);
$title = 'Reports';
@@ -124,6 +135,87 @@ class ReportController extends Controller
return view('reports.index', compact('years', 'months', 'title', 'mainTitleIcon'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query)
{
try {
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date');
}
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
* @param ReportQueryInterface $query
*
* @return View
*/
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query)
{
try {
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date');
}
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$set = $query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter(
function (TransactionJournal $journal) {
$count = $journal->transactiongroups()->where('relation', 'balance')->count();
if ($count == 0) {
return $journal;
}
}
);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalNoBudget(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query)
{
try {
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date');
}
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param string $year
* @param string $month
@@ -142,6 +234,9 @@ class ReportController extends Controller
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
/**
*
@@ -157,14 +252,14 @@ class ReportController extends Controller
/**
* Start getIncomeForMonth DONE
*/
$income = $query->incomeByPeriod($start, $end);
$income = $query->incomeByPeriod($start, $end, $showSharedReports);
/**
* End getIncomeForMonth DONE
*/
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $query->journalsByExpenseAccount($start, $end);
$set = $query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
@@ -182,7 +277,7 @@ class ReportController extends Controller
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
@@ -190,9 +285,11 @@ class ReportController extends Controller
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
/**
@@ -206,9 +303,13 @@ class ReportController extends Controller
$categories = Steam::makeArray($result);
// all transfers
$result = $query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
if ($showSharedReports === false) {
$result = $query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
} else {
$merged = $categories;
}
// sort.
$sorted = Steam::sortNegativeArray($merged);
@@ -221,7 +322,7 @@ class ReportController extends Controller
/**
* Start getAccountsForMonth
*/
$list = $query->accountList();
$list = $query->accountList($showSharedReports);
$accounts = [];
/** @var Account $account */
foreach ($list as $account) {
@@ -262,16 +363,19 @@ class ReportController extends Controller
} catch (Exception $e) {
return view('error')->with('message', 'Invalid date.');
}
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $helper->yearBalanceReport($date);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end);
$balances = $helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end, $showSharedReports);
//$groupedExpenses = $helper-> expensesGroupedByAccount($date, $end, 15);

View File

@@ -1,10 +1,7 @@
<?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\Search\SearchInterface;
use Illuminate\Http\Request;
use Input;
/**
@@ -12,7 +9,8 @@ use Input;
*
* @package FireflyIII\Http\Controllers
*/
class SearchController extends Controller {
class SearchController extends Controller
{
/**
* Results always come in the form of an array [results, count, fullCount]
*/

View File

@@ -3,13 +3,14 @@
use Auth;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\JournalSaved;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Redirect;
use Session;
@@ -41,7 +42,9 @@ class TransactionController extends Controller
public function create($what = 'deposit')
{
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->where('active', 1)->orderBy('name', 'DESC')->get(['accounts.*'])
Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->orderBy('name', 'ASC')->where(
'active', 1
)->orderBy('name', 'DESC')->get(['accounts.*'])
);
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)';
@@ -118,7 +121,9 @@ class TransactionController extends Controller
{
$what = strtolower($journal->transactiontype->type);
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->where('active', 1)->orderBy('name', 'DESC')->get(['accounts.*'])
Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->where('active', 1)->orderBy(
'name', 'DESC'
)->get(['accounts.*'])
);
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)';
@@ -143,7 +148,7 @@ class TransactionController extends Controller
}
if ($journal->piggyBankEvents()->count() > 0) {
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->first()->piggy_bank_id;
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id;
}
$preFilled['amount'] = 0;
@@ -227,25 +232,15 @@ class TransactionController extends Controller
$t->after = $t->before + $t->amount;
}
);
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $loopJournal */
foreach ($group->transactionjournals()->get() as $loopJournal) {
if ($loopJournal->id != $journal->id) {
$members->push($loopJournal);
}
}
}
return view('transactions.show', compact('journal', 'members'))->with(
return view('transactions.show', compact('journal'))->with(
'subTitle', e($journal->transactiontype->type) . ' "' . e($journal->description) . '"'
);
}
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
@@ -264,8 +259,21 @@ class TransactionController extends Controller
$journal = $repository->store($journalData);
event(new JournalSaved($journal));
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
if (intval($request->get('reminder_id')) > 0) {
$reminder = Auth::user()->reminders()->find($request->get('reminder_id'));
$reminder->active = 0;
$reminder->save();
}
Session::flash('success', 'New transaction "' . $journal->description . '" stored!');
if (intval(Input::get('create_another')) === 1) {
return Redirect::route('transactions.create', $request->input('what'))->withInput();
}
return Redirect::route('transactions.index', $request->input('what'));
}
@@ -281,6 +289,7 @@ class TransactionController extends Controller
public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository)
{
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
@@ -298,8 +307,16 @@ class TransactionController extends Controller
];
$repository->update($journal, $journalData);
event(new JournalSaved($journal));
// update, get events by date and sort DESC
Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('transactions.edit', $journal->id);
}
return Redirect::route('transactions.index', $journalData['what']);
}

View File

@@ -36,6 +36,7 @@ class Kernel extends HttpKernel
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated',
'range' => 'FireflyIII\Http\Middleware\Range',
'reminders' => 'FireflyIII\Http\Middleware\Reminders',
];
}

View File

@@ -9,6 +9,7 @@ use Illuminate\Contracts\Auth\Guard;
use Navigation;
use Preferences;
use Session;
use View;
/**
* Class SessionFilter
@@ -46,25 +47,36 @@ class Range
public function handle($request, Closure $theNext)
{
if ($this->auth->check()) {
// user's view range comes from preferences, gets set in session:
/** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M');
// ignore preference. set the range to be the current month:
if (!Session::has('start') && !Session::has('end')) {
// the start and end date are checked and stored:
$start = Session::has('start') ? Session::get('start') : new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start);
$period = Navigation::periodName($viewRange->data, $start);
$prev = Navigation::jumpToPrevious($viewRange->data, clone $start);
$next = Navigation::jumpToNext($viewRange->data, clone $start);
/** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M');
$start = Session::has('start') ? Session::get('start') : new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start);
Session::put('start', $start);
Session::put('end', $end);
}
if (!Session::has('first')) {
$journal = $this->auth->user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
if ($journal) {
Session::put('first', $journal->date);
} else {
Session::put('first', Carbon::now());
}
}
// set current / next / prev month.
$current = Carbon::now()->format('F Y');
$next = Carbon::now()->endOfMonth()->addDay()->format('F Y');
$prev = Carbon::now()->startOfMonth()->subDay()->format('F Y');
View::share('currentMonthName', $current);
View::share('previousMonthName', $prev);
View::share('nextMonthName', $next);
Session::put('range', $viewRange->data);
Session::put('start', $start);
Session::put('end', $end);
Session::put('period', $period);
Session::put('prev', Navigation::periodName($viewRange->data, $prev));
Session::put('next', Navigation::periodName($viewRange->data, $next));
}

View File

@@ -0,0 +1,91 @@
<?php
namespace FireflyIII\Http\Middleware;
use App;
use Carbon\Carbon;
use Closure;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
use Illuminate\Contracts\Auth\Guard;
use View;
/**
* Class Reminders
*
* @package FireflyIII\Http\Middleware
*/
class Reminders
{
/**
* 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()) {
// do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon;
/** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */
$helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface');
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$ranges = $helper->getReminderRanges($piggyBank);
foreach ($ranges as $range) {
if ($today < $range['end'] && $today > $range['start']) {
// create a reminder here!
$helper->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
}
// delete invalid reminders
$reminders = $this->auth->user()->reminders()->get();
foreach($reminders as $reminder) {
if(is_null($reminder->remindersable)) {
$reminder->delete();
}
}
// get and list active reminders:
$reminders = $this->auth->user()->reminders()->today()->get();
$reminders->each(
function (Reminder $reminder) use ($helper) {
$reminder->description = $helper->getReminderText($reminder);
}
);
View::share('reminders', $reminders);
}
return $next($request);
}
}

View File

@@ -1,10 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 12:29
*/
namespace FireflyIII\Http\Requests;
@@ -33,8 +27,8 @@ class BillFormRequest extends Request
public function rules()
{
$nameRule = 'required|between:1,255|uniqueForUser:bills,name';
if(intval(Input::get('id')) > 0) {
$nameRule .= ','.intval(Input::get('id'));
if (intval(Input::get('id')) > 0) {
$nameRule = 'required|between:1,255';
}
$rules = [

View File

@@ -1,10 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 12:29
*/
namespace FireflyIII\Http\Requests;

View File

@@ -35,6 +35,7 @@ class JournalFormRequest extends Request
'what' => 'required|in:withdrawal,deposit,transfer|exists:transaction_types,type',
'amount' => 'numeric|required|min:0.01',
'date' => 'required|date',
'reminder_id' => 'numeric|exists:reminders,id',
'amount_currency_id' => 'required|exists:transaction_currencies,id',
];

View File

@@ -51,7 +51,7 @@ class PiggyBankFormRequest extends Request
'rep_times' => 'integer|min:0|max:99',
'reminder' => 'in:day,week,quarter,month,year',
'reminder_skip' => 'integer|min:0|max:99',
'remind_me' => 'boolean',
'remind_me' => 'boolean|piggyBankReminder',
'order' => 'integer|min:1',
];

View File

@@ -7,6 +7,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Category;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
@@ -239,8 +240,17 @@ Breadcrumbs::register(
// reminders
Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
'reminders.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reminders', route('reminders.index'));
}
);
// reminders
Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
$breadcrumbs->parent('reminders.index');
$breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id));
}

View File

@@ -4,9 +4,10 @@ use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\PiggyBank;
// models
@@ -89,6 +90,16 @@ Route::bind(
}
);
Route::bind(
'reminder', function ($value, $route) {
if (Auth::check()) {
return Reminder::where('id', $value)->where('user_id', Auth::user()->id)->first();
}
return null;
}
);
Route::bind(
'limitrepetition', function ($value, $route) {
if (Auth::check()) {
@@ -135,7 +146,7 @@ Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'r
Route::controllers(
[
'auth' => 'Auth\AuthController',
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]
);
@@ -145,12 +156,10 @@ Route::controllers(
* Home Controller
*/
Route::group(
['middleware' => ['auth', 'range']], function () {
['middleware' => ['auth', 'range', 'reminders']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::get('/prev', ['uses' => 'HomeController@sessionPrev', 'as' => 'sessionPrev']);
Route::get('/next', ['uses' => 'HomeController@sessionNext', 'as' => 'sessionNext']);
Route::get('/jump/{range}', ['uses' => 'HomeController@rangeJump', 'as' => 'rangeJump']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']);
/**
* Account Controller
@@ -232,11 +241,12 @@ Route::group(
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/category/{category}/spending/{year}', ['uses' => 'GoogleChartController@categoriesAndSpending']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']);
Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']);
Route::get('/chart/category/{category}/period', ['uses' => 'GoogleChartController@categoryPeriodChart']);
Route::get('/chart/category/{category}/overview', ['uses' => 'GoogleChartController@categoryOverviewChart']);
/**
* Help Controller
@@ -249,14 +259,17 @@ Route::group(
Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']);
Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']);
Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']);
Route::get('/json/box', ['uses' => 'JsonController@box', 'as' => 'json.box']);
Route::get('/json/show-shared-reports', 'JsonController@showSharedReports');
Route::get('/json/show-shared-reports/set', 'JsonController@setSharedReports');
/**
* Piggy Bank Controller
*/
Route::get('/piggy-banks', ['uses' => 'PiggyBankController@index', 'as' => 'piggy-banks.index']);
Route::get('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@add']); # add money
Route::get('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@remove']); #remove money
Route::get('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@add', 'as' => 'piggy-banks.addMoney']); # add money
Route::get('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@remove', 'as' => 'piggy-banks.removeMoney']); #remove money
Route::get('/piggy-banks/create', ['uses' => 'PiggyBankController@create', 'as' => 'piggy-banks.create']);
Route::get('/piggy-banks/edit/{piggyBank}', ['uses' => 'PiggyBankController@edit', 'as' => 'piggy-banks.edit']);
Route::get('/piggy-banks/delete/{piggyBank}', ['uses' => 'PiggyBankController@delete', 'as' => 'piggy-banks.delete']);
@@ -278,7 +291,7 @@ Route::group(
*/
Route::get('/profile', ['uses' => 'ProfileController@index', 'as' => 'profile']);
Route::get('/profile/change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword','as' => 'change-password-post']);
Route::post('/profile/change-password', ['uses' => 'ProfileController@postChangePassword', 'as' => 'change-password-post']);
/**
* Related transactions controller
@@ -286,9 +299,19 @@ Route::group(
Route::get('/related/alreadyRelated/{tj}', ['uses' => 'RelatedController@alreadyRelated', 'as' => 'related.alreadyRelated']);
Route::post('/related/relate/{tj}/{tjSecond}', ['uses' => 'RelatedController@relate', 'as' => 'related.relate']);
Route::post('/related/removeRelation/{tj}/{tjSecond}', ['uses' => 'RelatedController@removeRelation', 'as' => 'related.removeRelation']);
Route::get('/related/remove/{tj}/{tjSecond}', ['uses' => 'RelatedController@getRemoveRelation', 'as' => 'related.getRemoveRelation']);
Route::get('/related/related/{tj}', ['uses' => 'RelatedController@related', 'as' => 'related.related']);
Route::post('/related/search/{tj}', ['uses' => 'RelatedController@search', 'as' => 'related.search']);
/**
* Reminder Controller
*/
Route::get('/reminders', ['uses' => 'ReminderController@index', 'as' => 'reminders.index']);
Route::get('/reminder/dismiss/{reminder}', ['uses' => 'ReminderController@dismiss', 'as' => 'reminders.dismiss']);
Route::get('/reminder/act/{reminder}', ['uses' => 'ReminderController@act', 'as' => 'reminders.act']);
Route::get('/reminder/{reminder}', ['uses' => 'ReminderController@show', 'as' => 'reminders.show']);
/**
* Repeated Expenses Controller
*/
@@ -309,6 +332,16 @@ Route::group(
Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']);
Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']);
// pop ups for budget report:
Route::get('/reports/modal/{account}/{year}/{month}/no-budget', ['uses' => 'ReportController@modalNoBudget', 'as' => 'reports.no-budget']);
Route::get(
'/reports/modal/{account}/{year}/{month}/balanced-transfers',
['uses' => 'ReportController@modalBalancedTransfers', 'as' => 'reports.balanced-transfers']
);
Route::get(
'/reports/modal/{account}/{year}/{month}/left-unbalanced', ['uses' => 'ReportController@modalLeftUnbalanced', 'as' => 'reports.left-unbalanced']
);
/**
* Search Controller
*/

View File

@@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait;
use Crypt;
/**
* Class Account
*
@@ -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,100|uniqueForUser:accounts,name',
'name' => 'required|between:1,1024|uniqueForUser:accounts,name',
'active' => 'required|boolean'
];
@@ -41,6 +41,22 @@ class Account extends Model
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if ($this->encrypted) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@@ -14,7 +14,7 @@ class PiggyBank extends Model
{
use SoftDeletes;
protected $fillable = ['repeats', 'name', 'account_id','rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder',];
protected $fillable = ['repeats', 'name', 'account_id','rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder','remind_me'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
@@ -24,6 +24,15 @@ 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
*

View File

@@ -10,6 +10,8 @@ use Illuminate\Database\Eloquent\Model;
class PiggyBankEvent extends Model
{
protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'amount'];
/**
* @return array
*/

View File

@@ -1,7 +1,8 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Carbon\Carbon;
/**
* Class PiggyBankRepetition
*
@@ -26,4 +27,25 @@ class PiggyBankRepetition extends Model
return $this->belongsTo('FireflyIII\Models\PiggyBank');
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeRelevantOnDate(EloquentBuilder $query, Carbon $date)
{
return $query->where(
function($q) use ($date) {
$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->orWhereNull('targetdate');
});
}
}

View File

@@ -1,5 +1,7 @@
<?php namespace FireflyIII\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
/**
@@ -10,6 +12,19 @@ use Illuminate\Database\Eloquent\Model;
class Reminder extends Model
{
protected $fillable = ['user_id', 'startdate', 'metadata', 'enddate', 'active', 'notnow', 'remindersable_id', 'remindersable_type',];
/**
* @param $value
*
* @return int
*/
public function getActiveAttribute($value)
{
return intval($value) == 1;
}
/**
* @return array
*/
@@ -18,6 +33,26 @@ class Reminder extends Model
return ['created_at', 'updated_at', 'startdate', 'enddate'];
}
/**
* @param $value
*
* @return mixed
*/
public function getMetadataAttribute($value)
{
return json_decode($value);
}
/**
* @param $value
*
* @return bool
*/
public function getNotnowAttribute($value)
{
return intval($value) == 1;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
@@ -26,6 +61,34 @@ class Reminder extends Model
return $this->morphTo();
}
/**
* @param EloquentBuilder $query
* @param Carbon $start
* @param Carbon $end
*
* @return $this
*/
public function scopeOnDates(EloquentBuilder $query, Carbon $start, Carbon $end)
{
return $query->where('reminders.startdate', '=', $start->format('Y-m-d 00:00:00'))->where('reminders.enddate', '=', $end->format('Y-m-d 00:00:00'));
}
public function scopeToday(EloquentBuilder $query)
{
$today = new Carbon;
return $query->where('startdate', '<=', $today->format('Y-m-d 00:00:00'))->where('enddate', '>=', $today->format('Y-m-d 00:00:00'))->where('active', 1)
->where('notnow', 0);
}
/**
* @param $value
*/
public function setMetadataAttribute($value)
{
$this->attributes['metadata'] = json_encode($value);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@@ -1,17 +1,19 @@
<?php namespace FireflyIII\Providers;
use App;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\Facades\Navigation;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Log;
/**
* Class EventServiceProvider
@@ -28,12 +30,14 @@ class EventServiceProvider extends ServiceProvider
*/
protected $listen
= [
'event.name' => [
'EventListener',
],
'App\Events\JournalDeleted' => [
'App\Handlers\Events\JournalDeletedHandler@handle',
'FireflyIII\Events\JournalSaved' => [
'FireflyIII\Handlers\Events\RescanJournal',
'FireflyIII\Handlers\Events\UpdateJournalConnection',
],
'FireflyIII\Events\JournalCreated' => [
'FireflyIII\Handlers\Events\ConnectJournalToPiggyBank',
]
];
/**
@@ -57,6 +61,8 @@ class EventServiceProvider extends ServiceProvider
}
);
Account::deleted(
function (Account $account) {
@@ -68,14 +74,16 @@ class EventServiceProvider extends ServiceProvider
}
);
PiggyBank::created(function(PiggyBank $piggyBank) {
PiggyBank::created(
function (PiggyBank $piggyBank) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($piggyBank);
$repetition->startdate = $piggyBank->startdate;
$repetition->targetdate = $piggyBank->targetdate;
$repetition->startdate = is_null($piggyBank->startdate) ? null : $piggyBank->startdate;
$repetition->targetdate = is_null($piggyBank->targetdate) ? null : $piggyBank->targetdate;
$repetition->currentamount = 0;
$repetition->save();
});
}
);
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {

View File

@@ -64,6 +64,8 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
$this->app->bind('FireflyIII\Helpers\Reminders\ReminderHelperInterface', 'FireflyIII\Helpers\Reminders\ReminderHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery');

View File

@@ -10,10 +10,12 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Session;
@@ -25,6 +27,14 @@ use Session;
class AccountRepository implements AccountRepositoryInterface
{
/**
* @return int
*/
public function countAssetAccounts()
{
return Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->count();
}
/**
* @param Account $account
*
@@ -37,16 +47,53 @@ class AccountRepository implements AccountRepositoryInterface
return true;
}
/**
* @param Preference $preference
*
* @return Collection
*/
public function getFrontpageAccounts(Preference $preference)
{
if ($preference->data == []) {
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $preference->data)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
}
return $accounts;
}
/**
* @param Account $account
* @param int $page
* @param string $range
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getJournals(Account $account, $page, $range = 'session')
public function getFrontpageTransactions(Account $account, Carbon $start, Carbon $end)
{
$offset = $page * 50;
return Auth::user()
->transactionjournals()
->with(['transactions', 'transactioncurrency', 'transactiontype'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id)
->before($end)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->take(10)
->get(['transaction_journals.*']);
}
/**
* @param Account $account
* @param int $page
*
* @return mixed
*/
public function getJournals(Account $account, $page)
{
$offset = ($page - 1) * 50;
$query = Auth::user()
->transactionJournals()
->withRelevantData()
@@ -54,10 +101,8 @@ class AccountRepository implements AccountRepositoryInterface
->where('transactions.account_id', $account->id)
->orderBy('date', 'DESC');
if ($range == 'session') {
$query->before(Session::get('end', Carbon::now()->startOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
}
$query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
$count = $query->count();
$set = $query->take(50)->offset($offset)->get(['transaction_journals.*']);
$paginator = new LengthAwarePaginator($set, $count, 50, $page);
@@ -69,6 +114,23 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @param Account $account
*
* @return float
*/
public function leftOnAccount(Account $account)
{
$balance = \Steam::balance($account);
/** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;
}
return $balance;
}
/**
* @param Account $account
*
@@ -123,13 +185,7 @@ class AccountRepository implements AccountRepositoryInterface
$account->save();
// update meta data:
/** @var AccountMeta $meta */
foreach ($account->accountMeta()->get() as $meta) {
if ($meta->name == 'accountRole') {
$meta->data = $data['accountRole'];
$meta->save();
}
}
$this->_updateMetadata($account, $data);
$openingBalance = $this->openingBalanceTransaction($account);
@@ -285,6 +341,40 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @param Account $account
* @param array $data
*/
protected function _updateMetadata(Account $account, array $data)
{
$metaEntries = $account->accountMeta()->get();
$updated = false;
/** @var AccountMeta $entry */
foreach ($metaEntries as $entry) {
if ($entry->name == 'accountRole') {
$entry->data = $data['accountRole'];
$updated = true;
$entry->save();
}
}
if ($updated === false) {
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => 'accountRole',
'data' => $data['accountRole']
]
);
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save();
}
}
/**
* @param Account $account
* @param TransactionJournal $journal
@@ -310,21 +400,4 @@ class AccountRepository implements AccountRepositoryInterface
return $journal;
}
/**
* @param Account $account
*
* @return float
*/
public function leftOnAccount(Account $account)
{
$balance = \Steam::balance($account);
/** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount;
}
return $balance;
}
}

View File

@@ -4,7 +4,9 @@ namespace FireflyIII\Repositories\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Preference;
use Illuminate\Support\Collection;
use Carbon\Carbon;
/**
* Interface AccountRepositoryInterface
*
@@ -19,14 +21,34 @@ interface AccountRepositoryInterface
*/
public function destroy(Account $account);
/**
* @return int
*/
public function countAssetAccounts();
/**
* @param Preference $preference
*
* @return Collection
*/
public function getFrontpageAccounts(Preference $preference);
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getFrontpageTransactions(Account $account, Carbon $start, Carbon $end);
/**
* @param Account $account
* @param int $page
* @param string $range
*
* @return mixed
*/
public function getJournals(Account $account, $page, $range = 'session');
public function getJournals(Account $account, $page);
/**
* @param Account $account

View File

@@ -1,18 +1,12 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 07:40
*/
namespace FireflyIII\Repositories\Bill;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Navigation;
use Log;
use Navigation;
/**
* Class BillRepository
@@ -21,6 +15,54 @@ use Log;
*/
class BillRepository implements BillRepositoryInterface
{
/**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
* and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and
* you give 1st and the 31st of that month as argument, you'll get one response, matching the range of your bill.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getRanges(Bill $bill, Carbon $start, Carbon $end)
{
$startOfBill = $bill->date;
$startOfBill = Navigation::startOfPeriod($startOfBill, $bill->repeat_freq);
// all periods of this bill up until the current period:
$billStarts = [];
while ($startOfBill < $end) {
$endOfBill = Navigation::endOfPeriod($startOfBill, $bill->repeat_freq);
$billStarts[] = [
'start' => clone $startOfBill,
'end' => clone $endOfBill,
];
// actually the next one:
$startOfBill = Navigation::addPeriod($startOfBill, $bill->repeat_freq, $bill->skip);
}
// for each
$validRanges = [];
foreach ($billStarts as $dateEntry) {
if ($dateEntry['end'] > $start && $dateEntry['start'] < $end) {
// count transactions for bill in this range (not relevant yet!):
// $count = $bill->transactionjournals()->before($dateEntry['end'])->after($dateEntry['start'])->count();
// if ($count == 0) {
$validRanges[] = $dateEntry;
// }
}
}
return $validRanges;
// echo $bill->name;
// var_dump($validRanges);
}
/**
* @param Bill $bill
*
@@ -28,6 +70,7 @@ class BillRepository implements BillRepositoryInterface
*/
public function nextExpectedMatch(Bill $bill)
{
$finalDate = null;
if ($bill->active == 0) {
return $finalDate;
@@ -113,7 +156,7 @@ class BillRepository implements BillRepositoryInterface
$wordMatch = true;
Log::debug('word match is true');
} else {
Log::debug('Count: ' . $count.', count(matches): ' . count($matches));
Log::debug('Count: ' . $count . ', count(matches): ' . count($matches));
}

View File

@@ -1,13 +1,8 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/02/15
* Time: 07:40
*/
namespace FireflyIII\Repositories\Bill;
use Carbon\Carbon;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
@@ -25,6 +20,19 @@ interface BillRepositoryInterface {
*/
public function nextExpectedMatch(Bill $bill);
/**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
* and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and
* you give 1st and the 31st of that month as argument, you'll get one response, matching the range of your bill.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getRanges(Bill $bill, Carbon $start, Carbon $end);
/**
* @param array $data
*

View File

@@ -267,13 +267,15 @@ class JournalRepository implements JournalRepositoryInterface
// update the from and to transaction.
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id === $from->id) {
if (floatval($transaction->amount) < 0) {
// this is the from transaction, negative amount:
$transaction->amount = $data['amount'] * -1;
$transaction->amount = $data['amount'] * -1;
$transaction->account_id = $from->id;
$transaction->save();
}
if ($transaction->account_id === $to->id) {
if (floatval($transaction->amount) > 0) {
$transaction->amount = $data['amount'];
$transaction->account_id = $to->id;
$transaction->save();
}
}

View File

@@ -2,8 +2,12 @@
namespace FireflyIII\Repositories\PiggyBank;
use Amount;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
use Illuminate\Support\Collection;
use Navigation;
@@ -15,49 +19,6 @@ use Navigation;
class PiggyBankRepository implements PiggyBankRepositoryInterface
{
/**
* @param array $data
*
* @return PiggyBank
*/
public function store(array $data)
{
$piggyBank = PiggyBank::create($data);
return $piggyBank;
}
/**
* @param PiggyBank $account
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data)
{
/**
'rep_length' => $request->get('rep_length'),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
'remind_me' => intval($request->get('remind_me')) == 1 ? true : false ,
'reminder' => $request->get('reminder'),
*/
$piggyBank->name = $data['name'];
$piggyBank->account_id = intval($data['account_id']);
$piggyBank->targetamount = floatval($data['targetamount']);
$piggyBank->targetdate = $data['targetdate'];
$piggyBank->reminder = $data['reminder'];
$piggyBank->rep_length = isset($data['rep_length']) ? $data['rep_length'] : null;
$piggyBank->rep_every =isset($data['rep_every']) ? $data['rep_every'] : null;
$piggyBank->rep_times = isset($data['rep_times']) ? $data['rep_times'] : null;
$piggyBank->remind_me = isset($data['remind_me']) ? $data['remind_me'] : null;
$piggyBank->save();
return $piggyBank;
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
@@ -126,4 +87,50 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $part;
}
/**
* @param array $data
*
* @return PiggyBank
*/
public function store(array $data)
{
$data['remind_me'] = isset($data['remind_me']) && $data['remind_me'] == '1' ? true : false;
$piggyBank = PiggyBank::create($data);
return $piggyBank;
}
/**
* @param PiggyBank $account
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data)
{
/**
* 'rep_length' => $request->get('rep_length'),
* 'rep_every' => intval($request->get('rep_every')),
* 'rep_times' => intval($request->get('rep_times')),
* 'remind_me' => intval($request->get('remind_me')) == 1 ? true : false ,
* 'reminder' => $request->get('reminder'),
*/
$piggyBank->name = $data['name'];
$piggyBank->account_id = intval($data['account_id']);
$piggyBank->targetamount = floatval($data['targetamount']);
$piggyBank->targetdate = $data['targetdate'];
$piggyBank->reminder = $data['reminder'];
$piggyBank->startdate = $data['startdate'];
$piggyBank->rep_length = isset($data['rep_length']) ? $data['rep_length'] : null;
$piggyBank->rep_every = isset($data['rep_every']) ? $data['rep_every'] : null;
$piggyBank->rep_times = isset($data['rep_times']) ? $data['rep_times'] : null;
$piggyBank->remind_me = isset($data['remind_me']) && $data['remind_me'] == '1' ? 1 : 0;
$piggyBank->save();
return $piggyBank;
}
}

View File

@@ -4,31 +4,19 @@ namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
use Illuminate\Support\Collection;
use Carbon\Carbon;
/**
* Interface PiggyBankRepositoryInterface
*
* @package FireflyIII\Repositories\PiggyBank
*/
interface PiggyBankRepositoryInterface {
interface PiggyBankRepositoryInterface
{
/**
* @param array $data
*
* @return PiggyBank
*/
public function store(array $data);
/**
* @param PiggyBank $account
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data);
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
@@ -48,4 +36,21 @@ interface PiggyBankRepositoryInterface {
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data);
/**
* @param array $data
*
* @return PiggyBank
*/
public function store(array $data);
/**
* @param PiggyBank $account
* @param array $data
*
* @return PiggyBank
*/
public function update(PiggyBank $piggyBank, array $data);
}

View File

@@ -8,6 +8,7 @@ use Illuminate\Support\MessageBag;
use Input;
use Session;
use View;
use Amount as Amt;
/**
* Class ExpandedForm
@@ -17,46 +18,6 @@ use View;
class ExpandedForm
{
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function integer($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = '1';
$html = \View::make('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function tags($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['data-role'] = 'tagsinput';
$html = \View::make('form.tags', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
* @param $name
* @param null $value
@@ -72,7 +33,7 @@ class ExpandedForm
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : \Amount::getDefaultCurrency();
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = TransactionCurrency::orderBy('code', 'ASC')->get();
$html = View::make('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
@@ -91,9 +52,19 @@ class ExpandedForm
if (isset($options['label'])) {
return $options['label'];
}
$labels = ['amount_min' => 'Amount (min)', 'amount_max' => 'Amount (max)', 'match' => 'Matches on', 'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from', 'account_to_id' => 'Account to', 'account_id' => 'Asset account', 'budget_id' => 'Budget'
, 'piggy_bank_id' => 'Piggy bank'];
$labels = [
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'piggy_bank_id' => 'Piggy bank'];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
@@ -157,7 +128,7 @@ class ExpandedForm
public function fillFieldValue($name, $value)
{
if (Session::has('preFilled')) {
$preFilled = \Session::get('preFilled');
$preFilled = Session::get('preFilled');
$value = isset($preFilled[$name]) && is_null($value) ? $preFilled[$name] : $value;
}
if (!is_null(Input::old($name))) {
@@ -181,7 +152,7 @@ class ExpandedForm
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amount::getDefaultCurrency();
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = TransactionCurrency::orderBy('code', 'ASC')->get();
$html = View::make('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
@@ -229,6 +200,26 @@ class ExpandedForm
return $html;
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function integer($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = '1';
$html = View::make('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
@@ -246,7 +237,7 @@ class ExpandedForm
$selectList[0] = '(none)';
}
$fields = ['title', 'name', 'description'];
/** @var \Eloquent $entry */
/** @var Eloquent $entry */
foreach ($set as $entry) {
$id = intval($entry->id);
$title = null;
@@ -270,9 +261,9 @@ class ExpandedForm
*/
public function optionsList($type, $name)
{
$previousValue = \Input::old('post_submit_action');
$previousValue = Input::old('post_submit_action');
$previousValue = is_null($previousValue) ? 'store' : $previousValue;
$html = \View::make('form.options', compact('type', 'name', 'previousValue'))->render();
$html = View::make('form.options', compact('type', 'name', 'previousValue'))->render();
return $html;
}
@@ -291,7 +282,26 @@ class ExpandedForm
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$selected = $this->fillFieldValue($name, $selected);
$html = \View::make('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
$html = View::make('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
return $html;
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function tags($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['data-role'] = 'tagsinput';
$html = View::make('form.tags', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}

View File

@@ -28,20 +28,27 @@ class Navigation
$add = ($skip + 1);
$functionMap = [
'1D' => 'addDays',
'daily' => 'addDays',
'1W' => 'addWeeks',
'weekly' => 'addWeeks',
'week' => 'addWeeks',
'1M' => 'addMonths',
'month' => 'addMonths',
'monthly' => 'addMonths',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYears',
'yearly' => 'addYears',
];
$modifierMap = [
'quarter' => 3,
'3M' => 3,
'quarterly' => 3,
'6M' => 6,
'half-year' => 6,
];
if (!isset($functionMap[$repeatFreq])) {
@@ -68,24 +75,31 @@ class Navigation
$currentEnd = clone $theCurrentEnd;
$functionMap = [
'1D' => 'addDay',
'daily' => 'addDay',
'1W' => 'addWeek',
'week' => 'addWeek',
'weekly' => 'addWeek',
'1M' => 'addMonth',
'month' => 'addMonth',
'monthly' => 'addMonth',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYear',
'yearly' => 'addYear',
];
$modifierMap = [
'quarter' => 3,
'3M' => 3,
'quarterly' => 3,
'half-year' => 6,
'6M' => 6,
];
$subDay = ['week', 'weekly', 'month', 'monthly', 'quarter', 'quarterly', 'half-year', 'year', 'yearly'];
$subDay = ['week', 'weekly', '1W', 'month', 'monthly', '1M', '3M', 'quarter', 'quarterly', '6M', 'half-year', 'year', 'yearly'];
if (!isset($functionMap[$repeatFreq])) {
throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq);
@@ -298,15 +312,19 @@ class Navigation
$date = clone $theDate;
$functionMap = [
'daily' => 'startOfDay',
'week' => 'startOfWeek',
'weekly' => 'startOfWeek',
'month' => 'startOfMonth',
'monthly' => 'startOfMonth',
'quarter' => 'firstOfQuarter',
'quartly' => 'firstOfQuarter',
'year' => 'startOfYear',
'yearly' => 'startOfYear',
'1D' => 'startOfDay',
'daily' => 'startOfDay',
'1W' => 'startOfWeek',
'week' => 'startOfWeek',
'weekly' => 'startOfWeek',
'month' => 'startOfMonth',
'1M' => 'startOfMonth',
'monthly' => 'startOfMonth',
'3M' => 'firstOfQuarter',
'quarter' => 'firstOfQuarter',
'quarterly' => 'firstOfQuarter',
'year' => 'startOfYear',
'yearly' => 'startOfYear',
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
@@ -314,7 +332,7 @@ class Navigation
return $date;
}
if ($repeatFreq == 'half-year') {
if ($repeatFreq == 'half-year' || $repeatFreq == '6M') {
$month = intval($date->format('m'));
$date->startOfYear();
if ($month >= 7) {
@@ -396,5 +414,47 @@ class Navigation
throw new FireflyException('updateStartDate cannot handle $range ' . $range);
}
/**
* @param Carbon $theDate
* @param $repeatFreq
* @param int $subtract
*
* @return Carbon
* @throws FireflyException
*/
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
$functionMap = [
'daily' => 'subDays',
'week' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function($subtract);
return $date;
}
if (isset($modifierMap[$repeatFreq])) {
$subtract = $subtract * $modifierMap[$repeatFreq];
$date->subMonths($subtract);
return $date;
}
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
}
}

View File

@@ -45,12 +45,12 @@ class Preferences
if (is_null($pref)) {
$pref = new Preference;
$pref->name = $name;
$pref->user()->associate(Auth::user());
}
$pref->data = $value;
$pref->save();
if (!is_null(Auth::user()->id)) {
$pref->user()->associate(Auth::user());
$pref->save();
}
return $pref;

View File

@@ -99,12 +99,4 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->hasMany('FireflyIII\Models\TransactionJournal');
}
/**
* @param $value
*/
public function setPasswordAttribute($value)
{
$this->attributes['password'] = \Hash::make($value);
}
}

View File

@@ -3,8 +3,10 @@
namespace FireflyIII\Validation;
use Auth;
use Carbon\Carbon;
use DB;
use Illuminate\Validation\Validator;
use Navigation;
/**
* Class FireflyValidator
@@ -32,6 +34,31 @@ class FireflyValidator extends Validator
}
public function validatePiggyBankReminder($attribute, $value, $parameters)
{
$array = $this->data;
// no reminder? dont care.
if (!isset($array['remind_me'])) {
return true;
}
// get or set start date & target date:
$startDate = isset($array['startdate']) ? new Carbon($array['startdate']) : new Carbon;
$targetDate = isset($array['targetdate']) && strlen($array['targetdate']) > 0 ? new Carbon($array['targetdate']) : null;
// target date is null? reminder period is always good.
if ($array['remind_me'] == '1' && is_null($targetDate)) {
return true;
}
$nextReminder = Navigation::addPeriod($startDate, $array['reminder'],0);
// reminder is beyond target?
if($nextReminder > $targetDate) {
return false;
}
return true;
}
/**
* @param $attribute
* @param $value
@@ -41,7 +68,11 @@ class FireflyValidator extends Validator
*/
public function validateUniqueForUser($attribute, $value, $parameters)
{
$count = DB::table($parameters[0])->where($parameters[1], $value)->count();
$query = DB::table($parameters[0])->where($parameters[1], $value);
if (isset($paramers[2])) {
$query->where('id', '!=', $parameters[2]);
}
$count = $query->count();
if ($count == 0) {
return true;
}

View File

@@ -1,18 +0,0 @@
actor: Tester
paths:
tests: tests
log: tests/_output
data: tests/_data
helpers: tests/_support
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
modules:
config:
coverage:
enabled: true
remote: false
whitelist:
include:
- app/*

View File

@@ -26,23 +26,14 @@
"watson/validating": "dev-master",
"doctrine/dbal": "~2.5",
"illuminate/html": "~5.0",
"barryvdh/laravel-ide-helper": "~2.0",
"league/commonmark": "0.7.*"
},
"require-dev": {
"barryvdh/laravel-debugbar": "@stable",
"barryvdh/laravel-ide-helper": "~2.0",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"codeception/codeception": "@stable",
"codeception/c3": "@stable",
"league/factory-muffin": "~2.1",
"codeception/phpbuiltinserver": "*",
"codeception/specify": "*",
"codeception/verify": "*",
"fzaninotto/faker": "1.*",
"codeclimate/php-test-reporter": "dev-master"
"satooshi/php-coveralls": "0.6.1"
},
"autoload": {
"classmap": [
@@ -60,14 +51,11 @@
"scripts": {
"post-install-cmd": [
"php artisan clear-compiled",
"php artisan optimize",
"Codeception\\c3\\Installer::copyC3ToRoot"
"php artisan optimize"
],
"post-update-cmd": [
"php artisan clear-compiled",
"php artisan optimize",
"Codeception\\c3\\Installer::copyC3ToRoot"
"php artisan optimize"
],
"post-create-project-cmd": [
"php -r \"copy('.env.example', '.env');\"",

1105
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,8 +18,8 @@ return [
],
'accountRoles' => [
'defaultExpense' => 'Default expense account',
'sharedExpense' => 'Shared expense account'
'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account'
],
'range_to_text' => [

View File

@@ -15,7 +15,7 @@ return [
|
*/
'driver' => 'smtp',
'driver' => env('EMAIL_DRIVER', 'smtp'),
/*
|--------------------------------------------------------------------------

View File

@@ -1,5 +1,7 @@
<?php
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Component;
use Illuminate\Database\Migrations\Migration;

View File

@@ -23,13 +23,17 @@ class ChangesForV322 extends Migration
// rename fields
Schema::table(
'piggy_bank_events', function (Blueprint $table) {
$table->dropForeign('piggy_bank_events_piggy_bank_id_foreign');
$table->renameColumn('piggy_bank_id', 'piggybank_id');
$table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade');
}
);
Schema::table(
'piggybank_repetitions', function (Blueprint $table) {
$table->dropForeign('piggy_bank_repetitions_piggy_bank_id_foreign');
$table->renameColumn('piggy_bank_id', 'piggybank_id');
$table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade');
}
);
@@ -55,11 +59,12 @@ class ChangesForV322 extends Migration
}
);
// drop foreign key from budget_limits:
// drop unique constraint from budget_limits:
Schema::table(
'budget_limits', function (Blueprint $table) {
$table->dropForeign('bid_foreign');
$table->dropUnique('unique_bl_combi');
$table->foreign('budget_id', 'bid_foreign')->references('id')->on('budgets')->onDelete('cascade');
}
);

View File

@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
/**
* Class ChangesForV332
*/
class ChangesForV332 extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table(
'accounts', function (Blueprint $table) {
$table->boolean('encrypted');
}
);
Schema::table(
'reminders', function (Blueprint $table) {
$table->text('metadata');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@@ -138,9 +138,9 @@ class TestDataSeeder extends Seeder
$acc_c = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Delete me', 'active' => 1]);
// create account meta:
$meta_a = AccountMeta::create(['account_id' => $acc_a->id, 'name' => 'accountRole', 'data' => 'defaultExpense']);
$meta_b = AccountMeta::create(['account_id' => $acc_b->id, 'name' => 'accountRole', 'data' => 'defaultExpense']);
$meta_c = AccountMeta::create(['account_id' => $acc_c->id, 'name' => 'accountRole', 'data' => 'defaultExpense']);
$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_c = AccountMeta::create(['account_id' => $acc_c->id, 'name' => 'accountRole', 'data' => 'defaultAsset']);
// var_dump($meta_a->toArray());
// var_dump($meta_b->toArray());
// var_dump($meta_c->toArray());
@@ -279,7 +279,6 @@ class TestDataSeeder extends Seeder
);
// and some events!
PiggyBankEvent::create(['piggy_bank_id' => $newCamera->id, 'date' => $this->som, 'amount' => 100]);
PiggyBankRepetition::create(['piggy_bank_id' => $newCamera->id, 'startdate' => $this->som, 'targetdate' => null, 'currentamount' => 100]);
$newClothes = PiggyBank::create(
@@ -301,27 +300,51 @@ class TestDataSeeder extends Seeder
);
PiggyBankEvent::create(['piggy_bank_id' => $newClothes->id, 'date' => $this->som, 'amount' => 100]);
PiggyBankRepetition::create(['piggy_bank_id' => $newClothes->id, 'startdate' => $this->som, 'targetdate' => $end, 'currentamount' => 100]);
// weekly reminder piggy bank
$weekly = PiggyBank::create(
[
'account_id' => $savings->id,
'name' => 'Weekly reminder for clothes',
'targetamount' => 2000,
'startdate' => $this->som,
'targetdate' => $next,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => 'week',
'reminder_skip' => 0,
'remind_me' => 1,
'order' => 0,
]
);
PiggyBankRepetition::create(['piggy_bank_id' => $weekly->id, 'startdate' => $this->som, 'targetdate' => $next, 'currentamount' => 0]);
/*
* New: create no less than eight piggy banks that
* create all sorts of reminders
*/
$list = ['week','quarter','month','year'];
$nextYear = clone $this->_startOfMonth;
$nextYear->addYear();
foreach($list as $entry) {
PiggyBank::create(
[
'account_id' => $savings->id,
'name' => $entry.' piggy bank with target date.',
'targetamount' => 1000,
'startdate' => $this->som,
'targetdate' => $nextYear,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => $entry,
'reminder_skip' => 0,
'remind_me' => 1,
'order' => 0,
]
);
PiggyBank::create(
[
'account_id' => $savings->id,
'name' => $entry.' piggy bank without target date.',
'targetamount' => 1000,
'startdate' => $this->som,
'targetdate' => null,
'repeats' => 0,
'rep_length' => null,
'rep_every' => 0,
'rep_times' => null,
'reminder' => $entry,
'reminder_skip' => 0,
'remind_me' => 1,
'order' => 0,
]
);
}
}
/**
@@ -331,21 +354,7 @@ class TestDataSeeder extends Seeder
{
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
// for weekly piggy bank (clothes)
$nextWeek = clone $this->_startOfMonth;
$piggyBank = PiggyBank::whereName('New clothes')->orderBy('id', 'DESC')->first();
$nextWeek->addWeek();
$week = $nextWeek->format('Y-m-d');
Reminder::create(
['user_id' => $user->id, 'startdate' => $this->som, 'enddate' => $week, 'active' => 1, 'notnow' => 0,
'remindersable_id' => $piggyBank->id, 'remindersable_type' => 'PiggyBank']
);
// a fake reminder::
Reminder::create(
['user_id' => $user->id, 'startdate' => $this->som, 'enddate' => $week, 'active' => 0, 'notnow' => 0, 'remindersable_id' => 40,
'remindersable_type' => 'Transaction']
);
}
/**
@@ -374,14 +383,6 @@ class TestDataSeeder extends Seeder
'order' => 0,
]
);
PiggyBankRepetition::create(['piggy_bank_id' => $recurring->id, 'startdate' => $this->som, 'targetdate' => $this->eom, 'currentamount' => 0]);
PiggyBankRepetition::create(
['piggy_bank_id' => $recurring->id, 'startdate' => $this->nsom, 'targetdate' => $this->neom, 'currentamount' => 0]
);
Reminder::create(
['user_id' => $user->id, 'startdate' => $this->som, 'enddate' => $this->neom, 'active' => 1, 'notnow' => 0,
'remindersable_id' => $recurring->id, 'remindersable_type' => 'PiggyBank']
);
}
/**

View File

@@ -11,12 +11,26 @@
syntaxCheck="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<!-- code coverage -->
<!--
<logging>
<log type="coverage-clover" target="./storage/coverage/clover.xml" charset="UTF-8" />
</logging>
-->
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
</php>
</phpunit>
</phpunit>

View File

@@ -0,0 +1,319 @@
/*!
* Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
*
* Copyright 2013-2015 Dan Grossman ( http://www.dangrossman.info )
* Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
*
* Built for http://www.improvely.com
*/
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
float: left;
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar,
.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar {
float: right;
margin: 4px;
}
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
float: none;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
}
.daterangepicker .ranges .range_inputs>div {
float: left;
}
.daterangepicker .ranges .range_inputs>div:nth-child(2) {
padding-left: 11px;
}
.daterangepicker .calendar {
display: none;
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
min-width: 32px;
}
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
display: block;
font-size: 11px;
font-weight: normal;
height: 20px;
line-height: 20px;
margin-bottom: 2px;
text-shadow: #fff 1px 1px 0px;
text-transform: uppercase;
width: 74px;
}
.daterangepicker .ranges input {
font-size: 11px;
}
.daterangepicker .ranges .input-mini {
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
display: block;
font-size: 11px;
height: 30px;
line-height: 30px;
vertical-align: middle;
margin: 0 0 10px 0;
padding: 0 6px;
width: 74px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}
.daterangepicker .calendar-date {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 8px auto 0 auto;
line-height: 30px;
}
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.openscenter:before {
position: absolute;
top: -7px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.openscenter:after {
position: absolute;
top: -6px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
}
.daterangepicker td.off {
color: #999;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.daterangepicker td.start-date {
-webkit-border-radius: 4px 0 0 4px;
-moz-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: #3071a9;
color: #fff;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

2
public/css/firefly.css Normal file
View File

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

View File

@@ -298,6 +298,9 @@ table.dataTable thead .sorting:after {
.huge {
font-size: 40px;
}
.large {
font-size: 30px;
}
.panel-green {
border-color: #5cb85c;

View File

@@ -1,7 +1,7 @@
$(function () {
if (typeof(googleLineChart) === "function" && typeof accountID !== 'undefined' && typeof view !== 'undefined') {
googleLineChart('chart/account/' + accountID + '/' + view, 'overview-chart');
if (typeof(googleLineChart) === "function" && typeof accountID !== 'undefined') {
googleLineChart('chart/account/' + accountID, 'overview-chart');
}
});

View File

@@ -1,7 +1,8 @@
$(function () {
if (typeof componentID !== 'undefined' && typeof repetitionID === 'undefined') {
googleColumnChart('chart/category/' + componentID + '/spending/' + year, 'componentOverview');
if (typeof categoryID !== 'undefined') {
googleColumnChart('chart/category/' + categoryID + '/overview', 'componentOverview');
googleColumnChart('chart/category/' + categoryID + '/period', 'periodOverview');
}

1280
public/js/daterangepicker.js Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,46 @@
$(function () {
$('.currencySelect').click(currencySelect)
$('.currencySelect').click(currencySelect);
ranges = {};
ranges[currentMonthName] = [moment().startOf('month'), moment().endOf('month')];
ranges[previousMonthName] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
ranges[nextMonthName] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
ranges['Everything'] = [firstDate, moment()];
$('#daterange').daterangepicker(
{
//View::share('currentMonthName', $current);
//View::share('previousMonthName', $prev);
//View::share('nextMonthName', $next);
ranges: ranges
,
opens: 'left',
format: 'DD-MM-YYYY',
startDate: start,
endDate: end
},
function (start, end, label) {
// send post.
$.post(dateRangeURL, {
start: start.format('YYYY-MM-DD'),
end: end.format('YYYY-MM-DD'),
label: label,
_token: token
}).success(function () {
window.location.reload(true);
}).fail(function () {
alert('Could not change date range');
});
//alert('A date range was chosen: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
}
);
});
@@ -21,4 +61,5 @@ function currencySelect(e) {
return false;
}
}

View File

@@ -3,7 +3,22 @@ google.setOnLoadCallback(drawChart);
function drawChart() {
googleLineChart('chart/home/account', 'accounts-chart');
googleBarChart('chart/home/budgets','budgets-chart');
googleColumnChart('chart/home/categories','categories-chart');
googlePieChart('chart/home/bills','bills-chart')
googleBarChart('chart/home/budgets', 'budgets-chart');
googleColumnChart('chart/home/categories', 'categories-chart');
googlePieChart('chart/home/bills', 'bills-chart');
getBoxAmounts();
}
function getBoxAmounts() {
var boxes = ['in', 'out','bills-unpaid','bills-paid'];
for (x in boxes) {
var box = boxes[x];
$.getJSON('/json/box', {box: box}).success(function (data) {
if(data.amount_raw != 0) {
$('#box-' + data.box).html(data.amount);
}
}).fail(function () {
console.log('Failed to get box!')
});
}
}

7
public/js/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -9,14 +9,14 @@ $(function () {
function addMoney(e) {
var pigID = parseInt($(e.target).data('id'));
$('#moneyManagementModal').empty().load('piggy-banks/add/' + pigID).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).modal('show');
$('#moneyManagementModal').empty().load('piggy-banks/remove/' + pigID, function() {$('#moneyManagementModal').modal('show');});
return false;
}

View File

@@ -1,23 +1,40 @@
$(document).ready(function () {
$('.relateTransaction').click(relateTransaction);
$('.unrelate-checkbox').click(unrelateTransaction);
$('.relateTransaction').click(relateTransactionDialog);
//$('.unrelate-checkbox').click(unrelateTransaction);
});
function unrelateTransaction(e) {
var target = $(e.target);
var id = target.data('id');
var relatedTo = target.data('relatedto');
var parent = target.data('parent');
$.post('related/removeRelation/' + id + '/' + relatedTo, {_token:token}).success(function (data) {
if(typeof id == "undefined" && typeof parent == "undefined") {
target = target.parent();
id = target.data('id');
parent = target.data('parent');
}
console.log('unlink ' + id + ' from ' + parent);
$.post('related/removeRelation/' + id + '/' + parent, {_token: token}).success(function (data) {
target.parent().parent().remove();
}).fail(function () {
alert('Could not!');
});
return false;
//$.post('related/removeRelation/' + id + '/' + relatedTo, {_token: token}).success(function (data) {
// target.parent().parent().remove();
//}).fail(function () {
// alert('Could not!');
//});
}
function relateTransaction(e) {
function relateTransactionDialog(e) {
var target = $(e.target);
var ID = target.data('id');
@@ -41,21 +58,12 @@ function relateTransaction(e) {
function searchRelatedTransactions(e, ID) {
var searchValue = $('#relatedSearchValue').val();
if (searchValue != '') {
$.post('related/search/' + ID, {searchValue: searchValue,_token:token}).success(function (data) {
// post each result to some div.
$('#relatedSearchResults').empty();
$.each(data, function (i, row) {
var tr = $('<tr>');
var checkBox = $('<td>').append($('<input>').attr('type', 'checkbox').data('relateto', ID).data('id', row.id).click(doRelateNewTransaction));
var description = $('<td>').text(row.description);
var amount = $('<td>').html(row.amount);
tr.append(checkBox).append(description).append(amount);
$('#relatedSearchResults').append(tr);
//$('#relatedSearchResults').append($('<div>').text(row.id));
});
$.post('related/search/' + ID, {searchValue: searchValue, _token: token}).success(function (data) {
// post the results to some div.
$('#relatedSearchResultsTitle').show();
$('#relatedSearchResults').empty().html(data);
// remove any clicks.
$('.relate').unbind('click').on('click', doRelateNewTransaction);
}).fail(function () {
alert('Could not search. Sorry.');
@@ -69,38 +77,35 @@ function doRelateNewTransaction(e) {
// remove the row from the table:
var target = $(e.target);
var id = target.data('id');
var relateToId = target.data('relateto');
if (!target.checked) {
var relateID = target.data('id');
$.post('related/relate/' + id + '/' + relateToId,{_token:token}).success(function (data) {
// success!
target.parent().parent().remove();
getAlreadyRelatedTransactions(null, relateToId);
}).fail(function () {
// could not relate.
alert('Error!');
});
var parent = target.data('parent');
} else {
alert('remove again!');
if (typeof id == "undefined" && typeof parent == "undefined") {
target = target.parent();
console.log(target);
id = target.data('id');
parent = target.data('parent');
}
console.log('Relate ' + id + ' to ' + parent);
$.post('related/relate/' + parent + '/' + id, {_token: token}).success(function (data) {
// success! remove entry:
target.parent().parent().remove();
// get related stuff (again).
getAlreadyRelatedTransactions(null, parent);
}).fail(function () {
// could not relate.
alert('Could not relate this transaction to the intended target.');
});
return false;
}
function getAlreadyRelatedTransactions(e, ID) {
//#alreadyRelated
$.get('related/alreadyRelated/' + ID).success(function (data) {
$('#alreadyRelated').empty();
$.each(data, function (i, row) {
var tr = $('<tr>');
$('#alreadyRelated').empty().html(data);
// some event triggers.
$('.unrelate').unbind('click').on('click', unrelateTransaction);
var checkBox = $('<td>').append($('<input>').attr('type', 'checkbox').data('relateto', ID).data('id', row.id).click(doRelateNewTransaction));
var description = $('<td>').text(row.description);
var amount = $('<td>').html(row.amount);
tr.append(checkBox).append(description).append(amount);
$('#alreadyRelated').append(tr);
//$('#relatedSearchResults').append($('<div>').text(row.id));
});
}).fail(function () {
alert('Cannot get related stuff.');
});

View File

@@ -6,4 +6,55 @@ if (typeof(google) != 'undefined') {
googleStackedColumnChart('chart/budgets/spending/' + year, 'budgets');
}
}
$(function () {
$('.openModal').on('click', openModal);
includeSharedToggle();
$('#includeShared').click(includeSharedSet);
});
function openModal(e) {
"use strict";
var target = $(e.target).parent();
var URL = target.attr('href');
$.get(URL).success(function (data) {
$('#defaultModal').empty().html(data).modal('show');
}).fail(function () {
alert('Could not load data.');
});
return false;
}
function includeSharedToggle() {
// get setting from JSON.
$.getJSON('json/show-shared-reports').success(function (data) {
console.log('GO');
if (data.value == true) {
// show shared data, update button:
//<i class="state-icon glyphicon glyphicon-check"></i>
$('#includeShared').empty().addClass('btn-info').append($('<i>').addClass('state-icon glyphicon glyphicon-check')).append(' Include shared asset accounts').show();
console.log('true');
} else {
$('#includeShared').empty().removeClass('btn-info').append($('<i>').addClass('state-icon glyphicon glyphicon-unchecked')).append(' Include shared asset accounts').show();
console.log('false');
}
}).fail(function () {
console.log('fail');
});
}
function includeSharedSet() {
// get setting from JSON.
$.getJSON('json/show-shared-reports/set').success(function (data) {
console.log('Value is now: ' + data.value);
includeSharedToggle();
}).fail(function () {
console.log('fail');
});
return false;
}

View File

@@ -20,7 +20,8 @@ return [
"alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
"alpha_num" => "The :attribute may only contain letters and numbers.",
"array" => "The :attribute must be an array.",
"unique_for_user" => "There already is an entry with this :attribute.",
"unique_for_user" => "There already is an entry with this :attribute.",
'piggy_bank_reminder' => 'The target date is too close to today to allow reminders.',
"before" => "The :attribute must be a date before :date.",
"between" => [
"numeric" => "The :attribute must be between :min and :max.",

View File

@@ -22,7 +22,9 @@
</div>
<div class="panel-body">
@include('list.accounts')
</div>
</div>
</div>
</div>

View File

@@ -2,7 +2,7 @@
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) !!}
<div class="row">
<div class="col-lg-8 col-md-6 col-sm-12">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}}
@@ -27,24 +27,6 @@
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 col-sm-12">
<!-- time based navigation -->
@include('partials.date_nav')
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-clock-o fa-fw"></i> View options for {{{$account->name}}}
</div>
<div class="panel-body">
<p>
@if($range == 'all')
<a href="{{route('accounts.show',$account->id)}}/session" class="btn btn-default">Stick to date-range</a>
@else
<a href="{{route('accounts.show',$account->id)}}/all" class="btn btn-default">Show all transactions</a>
@endif
</p>
</div>
</div>
</div>
</div>
@@ -66,7 +48,6 @@
@section('scripts')
<script type="text/javascript">
var accountID = {{{$account->id}}};
var view = '{{{$range}}}';
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script>
<!-- load the libraries and scripts necessary for Google Charts: -->

View File

@@ -1,62 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link href="/css/app.css" rel="stylesheet">
<!-- Fonts -->
<link href='//fonts.googleapis.com/css?family=Roboto:400,300' rel='stylesheet' type='text/css'>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Laravel</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
@if (Auth::guest())
<li><a href="/auth/login">Login</a></li>
<li><a href="/auth/register">Register</a></li>
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ Auth::user()->name }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="/auth/logout">Logout</a></li>
</ul>
</li>
@endif
</ul>
</div>
</div>
</nav>
@yield('content')
<!-- Scripts -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>
</body>
</html>

View File

@@ -20,10 +20,8 @@
</div>
</div>
</div>
<div class="panel-body">
@include('list.bills')
</div>
</div>
</div>
</div>
@stop

View File

@@ -5,7 +5,7 @@
<div class="modal-content">
<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" id="myModalLabel">Update (expected) income for {{Session::get('start', \Carbon\Carbon::now()->startOfMonth())->format('F Y')}}</h4>
<h4 class="modal-title" id="myModalLabel">Update (expected) available amount for {{Session::get('start', \Carbon\Carbon::now()->startOfMonth())->format('F Y')}}</h4>
</div>
<div class="modal-body">
<div class="input-group">

View File

@@ -5,15 +5,16 @@
<div class="col-lg-9 col-sm-8 col-md-8">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-calendar fa-fw"></i>
{{Session::get('start', \Carbon\Carbon::now()->startOfMonth())->format('F Y')}}
</div>
<div class="panel-body">
<div class="row">
<div class="col-lg-6 col-md-4 col-sm-3">
<small>Budgeted: <span id="budgetedAmount" data-value="300">{{Amount::format(300)}}</span></small>
<small>Budgeted: <span id="budgetedAmount" data-value="300"></span></small>
</div>
<div class="col-lg-6 col-md-4 col-sm-3" style="text-align:right;">
<small>Income {{Session::get('start', \Carbon\Carbon::now()->startOfMonth())->format('F Y')}}:
<small>Available in {{Session::get('start', \Carbon\Carbon::now()->startOfMonth())->format('F Y')}}:
<a href="#" class="updateIncome"><span id="totalAmount" data-value="{{$amount}}">{!! Amount::format($amount) !!}</span></a></small>
</div>
</div>
@@ -49,11 +50,10 @@
</div>
</div>
<div class="col-lg-3 col-sm-4 col-md-4">
<!-- time based navigation -->
@include('partials.date_nav')
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-tags"></i>
Transactions without a budget
</div>
<div class="panel-body">
@@ -71,6 +71,7 @@
<div class="col-lg-3 col-sm-4 col-md-6" style="height:180px;">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-tasks"></i>
@if(isset($budget->currentRep))
<a href="{{route('budgets.show',[$budget->id,$budget->currentRep->id])}}" id="budget-link-{{$budget->id}}">{{{$budget->name}}}</a>
@else
@@ -135,10 +136,11 @@
<div class="col-lg-3 col-sm-4 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-plus-circle"></i>
Create budget
</div>
<div class="panel-body">
<a href="{{route('budgets.create')}}" class="btn btn-success">Create new budget</a>
<a href="{{route('budgets.create')}}" class="btn btn-success"><i class="fa fa-fw fa-plus"></i> Create new budget</a>
</div>
</div>
</div>

View File

@@ -1,12 +1,6 @@
@extends('layouts.default')
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!}
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-12 ">
<!-- time based navigation -->
@include('partials.date_nav')
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">

View File

@@ -20,6 +20,10 @@
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-5">
@if(count($limits) == 1)
<p class="small text-center"><a href="{{route('budgets.show',$budget->id)}}">Show everything</a></p>
@endif
@foreach($limits as $limit)
@foreach($limit->limitrepetitions as $rep)
<div class="panel panel-default">
@@ -64,6 +68,9 @@
@endforeach
@endforeach
@if(count($limits) == 1)
<p class="small text-center"><a href="{{route('budgets.show',$budget->id)}}">Show everything</a></p>
@endif
</div>
</div>

View File

@@ -1,12 +1,6 @@
@extends('layouts.default')
@section('content')
{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) }}
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-12 ">
<!-- time based navigation -->
@include('partials.date_nav')
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">

View File

@@ -2,18 +2,35 @@
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $category) !!}
<div class="row">
<div class="col-lg-9 col-md-9 col-sm-7">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-calendar fa-fw"></i>
Overview
</div>
<div class="panel-body">
<div id="periodOverview"></div>
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-calendar-o fa-fw"></i>
Overview
</div>
<div class="panel-body">
<div id="componentOverview"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-repeat fa-fw"></i>
Transactions
</div>
<div class="panel-body">
@@ -21,16 +38,12 @@
</div>
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-5">
(TODO)
</div>
</div>
@stop
@section('scripts')
<script type="text/javascript">
var componentID = {{$category->id}};
var year = {{Session::get('start',\Carbon\Carbon::now()->startOfMonth())->format('Y')}};
var categoryID = {{$category->id}};
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script>
<!-- load the libraries and scripts necessary for Google Charts: -->

View File

@@ -3,7 +3,7 @@
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!}
<div class="row">
<div class="col-lg-6 col-sm-12 col-md-12">
<div class="col-lg-12 col-sm-12 col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
Currencies
@@ -14,23 +14,34 @@
</p>
<ul>
@if(count($currencies) > 0)
<table class="table table-striped table-bordered">
<tr>
<th>&nbsp;</th>
<th colspan="2">Currency</th>
</tr>
@foreach($currencies as $currency)
<li>
<a href="{{route('currency.edit',$currency->id)}}"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{{route('currency.delete',$currency->id)}}"><i class="fa fa-fw fa-trash"></i></a>
{{{$currency->name}}} ({{{$currency->code}}}) ({{{$currency->symbol}}})
<tr>
<td>
<div class="btn-group btn-group-sm">
<a class="btn btn-default" href="{{route('currency.edit',$currency->id)}}"><i class="fa fa-fw fa-pencil"></i></a>
<a class="btn btn-default" href="{{route('currency.delete',$currency->id)}}"><i class="fa fa-fw fa-trash"></i></a>
</div>
</td>
<td>{{{$currency->name}}} ({{{$currency->code}}}) ({{{$currency->symbol}}})</td>
<td>
@if($currency->id == $defaultCurrency->id)
<span class="label label-success">default</span>
@else
<span class="label label-default"><a style="color:#fff" href="{{route('currency.default',$currency->id)}}">make default</a></span>
<a class="btn btn-info" href="{{route('currency.default',$currency->id)}}">make default</a>
@endif
</li>
</td>
</tr>
@endforeach
</table>
@endif
<li><a href="{{route('currency.create')}}"><i class="fa fa-fw fa-plus-circle"></i> Add another currency</a></li>
</ul>
<p><a class="btn btn-success" href="{{route('currency.create')}}"><i class="fa fa-fw fa-plus-circle"></i> Add another currency</a></p>
</div>
</div>
</div>

View File

@@ -0,0 +1,5 @@
Hey there!
Welkome to Firefly III. Your registration has made it, and this email is here to confirm it.
Thanks for using Firefly!

View File

@@ -17,6 +17,10 @@
</div>
@else
<!-- fancy new boxes -->
@include('partials.boxes')
<div class="row">
@@ -61,8 +65,6 @@
</div>
<div class="col-lg-4 col-md-6 col-sm-12">
<!-- time based navigation -->
@include('partials.date_nav')
<!-- REMINDERS -->
<div class="panel panel-default">

View File

@@ -16,15 +16,17 @@
</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" type="text/css" media="all" />
<!-- <link rel="stylesheet" href="css/metisMenu.min.css" type="text/css" media="all" /> -->
<!-- new css for SB admin -->
<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" />
<link rel="stylesheet" href="css/firefly.css" type="text/css" media="all" />
@yield('styles')
<!--[if lt IE 9]>
@@ -107,24 +109,35 @@
<!-- modal to relate transactions to each other -->
<div class="modal fade" id="relationModal">
</div>
<!-- default modal -->
<div class="modal fade" id="defaultModal">
</div>
<script type="text/javascript" src="js/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<!-- <script type="text/javascript" src="js/jquery.metisMenu.min.js"></script> -->
<!-- <script type="text/javascript" src="js/sb-admin-2.js"></script>-->
<!-- new js for sb admin -->
<script type="text/javascript" src="js/metisMenu.min.js"></script>
<script type="text/javascript" src="js/sb-admin-2.js"></script>
<script type="text/javascript" src="js/help.js"></script>
<!-- date range stuff -->
<script type="text/javascript" src="js/moment.min.js"></script>
<script type="text/javascript" src="js/daterangepicker.js"></script>
<script type="text/javascript">
var start = "{{Session::get('start')->format('d-m-Y')}}";
var end = "{{Session::get('end')->format('d-m-Y')}}";
var titleString = "{{Session::get('start')->format('j M Y')}} - {{Session::get('end')->format('j M Y')}}";
var dateRangeURL = "{{route('daterange')}}";
var token = "{{csrf_token()}}";
var firstDate = moment("{{Session::get('first')->format('Y-m-d')}}");
var currentMonthName = "{{$currentMonthName}}";
var previousMonthName = "{{$previousMonthName}}";
var nextMonthName = "{{$nextMonthName}}";
$('#daterange span').text(titleString);
</script>
<script type="text/javascript" src="js/firefly.js"></script>
@yield('scripts')
</body>

View File

@@ -8,8 +8,10 @@
<title>Firefly III</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" type="text/css" media="all" />
<!-- <link rel="stylesheet" href="css/metisMenu.min.css" type="text/css" media="all" /> -->
<!-- new css for SB admin -->
<link rel="stylesheet" href="css/metisMenu.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="css/sb.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" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
@@ -44,5 +46,9 @@
</div>
<script type="text/javascript" src="js/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<!-- new js for sb admin -->
<script type="text/javascript" src="js/metisMenu.min.js"></script>
<script type="text/javascript" src="js/sb-admin-2.js"></script>
</body>
</html>

View File

@@ -1,11 +1,17 @@
<table class="table table-striped">
@if(is_object($accounts) && method_exists($accounts, 'render'))
{!! $accounts->render() !!}
@endif
<table class="table table-striped table-bordered">
<tr>
<th>&nbsp;</th>
<th>Name</th>
@if(isset($what) && $what == 'asset')
<th>Role</th>
@endif
<th>Current balance</th>
<th>Active</th>
<th>Last activity</th>
<th>Balance difference between {{Session::get('start')->format('jS F Y')}} and {{Session::get('end')->format('jS F Y')}}</th>
</tr>
@foreach($accounts as $account)
<tr>
@@ -16,7 +22,15 @@
</div>
</td>
<td><a href="{{route('accounts.show',$account->id)}}">{{{$account->name}}}</a></td>
<td>{{{$account->accountRole}}}</td>
@if(isset($what) && $what == 'asset')
<td>
@foreach($account->accountmeta as $entry)
@if($entry->name == 'accountRole')
{{Config::get('firefly.accountRoles.'.$entry->data)}}
@endif
@endforeach
</td>
@endif
<td>{!! Amount::format(Steam::balance($account)) !!}</td>
<td>
@if($account->active)
@@ -32,7 +46,14 @@
<em>Never</em>
@endif
</td>
<td>
{!! Amount::format($account->endBalance - $account->startBalance) !!}
</td>
</tr>
@endforeach
</table>
@if(is_object($accounts) && method_exists($accounts, 'render'))
{!! $accounts->render() !!}
@endif

View File

@@ -3,7 +3,7 @@
<th>&nbsp;</th>
<th>Name</th>
<th>Matches on</th>
<th>Matching amount</th>
<th colspan="2">Matching amount</th>
<th>Last seen match</th>
<th>Next expected match</th>
<th>Is active</th>
@@ -28,7 +28,8 @@
</td>
<td>
{!! Amount::format($entry->amount_min) !!}
&mdash;
</td>
<td>
{!! Amount::format($entry->amount_max) !!}
</td>
<td>

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