mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-18 10:16:49 +00:00
Compare commits
291 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5a45b25614 | ||
|
da3dc599f9 | ||
|
f013b435ab | ||
|
c5dee29e4b | ||
|
633ee02f13 | ||
|
6b750c909a | ||
|
5f8b6640a9 | ||
|
dd42d8437c | ||
|
67a178591d | ||
|
f5e5659c1f | ||
|
8b0f0fb615 | ||
|
209116e766 | ||
|
79392ab656 | ||
|
3ca1207231 | ||
|
cec1b147f2 | ||
|
46cfcfa3e7 | ||
|
b833e8dfa2 | ||
|
77b843efd8 | ||
|
db72ad7c60 | ||
|
eadc630fcb | ||
|
170c1793cc | ||
|
9f7c6c2d0c | ||
|
72d054c55c | ||
|
524edfe7c2 | ||
|
c25c5623d2 | ||
|
4f38b77ef6 | ||
|
5862803434 | ||
|
5b3beded39 | ||
|
c61fb7a598 | ||
|
33d9148029 | ||
|
63969f5a33 | ||
|
edde18aeef | ||
|
657116d361 | ||
|
e16269daa8 | ||
|
c07591ff5c | ||
|
75a478ad54 | ||
|
8dae8b1a7f | ||
|
15fd8cf486 | ||
|
55333156ac | ||
|
8cdcba3231 | ||
|
8bab9e84e2 | ||
|
2faae83912 | ||
|
5a61a11a61 | ||
|
a6d71988f2 | ||
|
7069e242ae | ||
|
56ee830558 | ||
|
6dd12729e6 | ||
|
14a48303cb | ||
|
72cf6c9c0f | ||
|
144ee6b8ca | ||
|
8967d86da6 | ||
|
18c6edbb5d | ||
|
53de3c4717 | ||
|
ad577e4e81 | ||
|
44811a3e7c | ||
|
1ab3f05b3a | ||
|
5e76488ae7 | ||
|
32771fe7e1 | ||
|
9b40cc6881 | ||
|
2e35260bbb | ||
|
a067704277 | ||
|
de281818ac | ||
|
c49bfad38d | ||
|
c1ba591b26 | ||
|
719af38a61 | ||
|
ac61dfae6b | ||
|
813fb679a7 | ||
|
e7562781f7 | ||
|
56d36b7f53 | ||
|
53b3f7f821 | ||
|
08a53156bd | ||
|
8985cd6309 | ||
|
3833da7410 | ||
|
4210cd10db | ||
|
a7bd1c6892 | ||
|
52b0111afa | ||
|
7921d128e4 | ||
|
d7e838701a | ||
|
289bcb22aa | ||
|
3fe57b7983 | ||
|
32e92c2a16 | ||
|
1b3d208540 | ||
|
6a8bf0aa62 | ||
|
56715556ed | ||
|
838330b909 | ||
|
69553b138b | ||
|
36d7a02994 | ||
|
301528e2d2 | ||
|
0303b45707 | ||
|
ba722e8ed5 | ||
|
289e5a5442 | ||
|
fdad96e2bc | ||
|
af994e4dae | ||
|
006d68e279 | ||
|
29dc122ad3 | ||
|
cf4a8c6204 | ||
|
3c73fe92bf | ||
|
6637590797 | ||
|
b8bab11acd | ||
|
a2f600feac | ||
|
80dd62ef0a | ||
|
827b1c9cd8 | ||
|
2e4fcf803d | ||
|
d00d95fc6f | ||
|
3e3ab9bd25 | ||
|
6eecc7722d | ||
|
ada4aaf69a | ||
|
93244c1f78 | ||
|
be056cea6b | ||
|
659ca8be14 | ||
|
ea9af8366d | ||
|
80edd47d36 | ||
|
d7746b3649 | ||
|
c4c4fbc34c | ||
|
59f57c96e9 | ||
|
a2f852fecf | ||
|
ad114ed329 | ||
|
c4c3d0f07f | ||
|
6cf8102de5 | ||
|
e7e4aa2218 | ||
|
6d84f4b6c1 | ||
|
ce3e9ffd11 | ||
|
3ada260e0e | ||
|
8fdd0cb795 | ||
|
913e05a2e6 | ||
|
fa1f703ef6 | ||
|
4004c53e1b | ||
|
4838670649 | ||
|
a985e09282 | ||
|
9bd1503cb4 | ||
|
a2ccbf7844 | ||
|
61bbe8a905 | ||
|
59bc5d22d1 | ||
|
1423d5b314 | ||
|
152d0eb1d0 | ||
|
6426d1df06 | ||
|
9284eb3fe9 | ||
|
afdae8bc1e | ||
|
2a7085e593 | ||
|
2408fb3ed4 | ||
|
8316afb176 | ||
|
e59fd098a3 | ||
|
e044199693 | ||
|
8f8e29fc22 | ||
|
8de5384158 | ||
|
216c659335 | ||
|
041ca8a5d3 | ||
|
fe4f1b306d | ||
|
a0972d99fb | ||
|
e332bfef7c | ||
|
cba5e226d8 | ||
|
5aff0c4943 | ||
|
cb49c00f4d | ||
|
e26d797d57 | ||
|
938581527e | ||
|
c38ae09735 | ||
|
28c3cfe084 | ||
|
4a2823bcba | ||
|
18eba02026 | ||
|
d4690ce580 | ||
|
a785c450b1 | ||
|
7480dc4a19 | ||
|
ad01891a67 | ||
|
67fe35d564 | ||
|
7f19b6957a | ||
|
0a54caf202 | ||
|
4b4c1c7f8f | ||
|
d071f3947e | ||
|
b3d99cd210 | ||
|
90e696f82c | ||
|
958fcd1cfa | ||
|
8f57c7dcb3 | ||
|
77262f52a4 | ||
|
16bfbc8a12 | ||
|
1fd375b875 | ||
|
46131ad39d | ||
|
0b5c5b2ae9 | ||
|
55be174037 | ||
|
a17b7025f1 | ||
|
170cf7fd77 | ||
|
23cdb4d326 | ||
|
cbbe529572 | ||
|
0b382426e9 | ||
|
1cbbf9baa4 | ||
|
8d41ff7b79 | ||
|
e3b6057bf8 | ||
|
66a4042cad | ||
|
56c08d8302 | ||
|
d4e759754d | ||
|
a96e171cbf | ||
|
bd4a8c8397 | ||
|
04f71b3b43 | ||
|
d124de51db | ||
|
d87d12a0f5 | ||
|
f2b08346d0 | ||
|
d3682a6727 | ||
|
371bbd9508 | ||
|
a8a28f442f | ||
|
65ddd8a736 | ||
|
8bb27de233 | ||
|
37e2f097ba | ||
|
1966d87ce6 | ||
|
7b8c86e1e3 | ||
|
de634da513 | ||
|
96836e2d6c | ||
|
8a9d576f61 | ||
|
791d12fbb4 | ||
|
d1329be2fa | ||
|
3ed6561702 | ||
|
7a0587f433 | ||
|
0fe682bfe6 | ||
|
0f685e8789 | ||
|
420771c233 | ||
|
5e3e9271ca | ||
|
1e603c0833 | ||
|
03e1673e92 | ||
|
9f992f003d | ||
|
f50244a41f | ||
|
8b9607f9b5 | ||
|
af107ad5e8 | ||
|
8926d62165 | ||
|
45344ee347 | ||
|
baf9ebab15 | ||
|
b7dab817f2 | ||
|
fb2fa54480 | ||
|
2c966a1234 | ||
|
143ea69c0d | ||
|
8a02ead013 | ||
|
930d5ab941 | ||
|
e54a56d3a8 | ||
|
f5216c0d85 | ||
|
ebc77540b9 | ||
|
28d2583c10 | ||
|
b0988a7b00 | ||
|
2c4920db2d | ||
|
8321663815 | ||
|
ac0280d460 | ||
|
ba6f4268f0 | ||
|
0bd18f94ac | ||
|
66fb63661f | ||
|
d9b16beb0a | ||
|
6a01a9bdfd | ||
|
b81b34a706 | ||
|
3750a00b5f | ||
|
05ddcc169d | ||
|
f571a5f1bd | ||
|
48b786adea | ||
|
d691fa9b4d | ||
|
da50f9e419 | ||
|
103de5e18a | ||
|
dac6efd98b | ||
|
46aa7f81b2 | ||
|
0683c7cd67 | ||
|
50d7aa7b6a | ||
|
b781215d0a | ||
|
866bc2f3bd | ||
|
49d4705014 | ||
|
9163fcfccb | ||
|
7e10641461 | ||
|
cdc0e3cfd8 | ||
|
466e81d56a | ||
|
5953f691d1 | ||
|
e2790ca6c1 | ||
|
66dbd48b76 | ||
|
73cfbbd2ba | ||
|
38bc38bf26 | ||
|
e12d13c838 | ||
|
fcc3af6136 | ||
|
491298e1cb | ||
|
72b5895217 | ||
|
0a8f4017bd | ||
|
cb985f5897 | ||
|
968ec0853f | ||
|
0bde72d3df | ||
|
45293fbd42 | ||
|
72997065f0 | ||
|
a838dc163d | ||
|
3d15a4ca6d | ||
|
51c7d4fb1b | ||
|
fa586dba7e | ||
|
8ad40389f2 | ||
|
b3333cc2d3 | ||
|
3699a7ba9a | ||
|
204e521ba4 | ||
|
c9bab3e5c3 | ||
|
3d00e20238 | ||
|
c3958ed3c4 | ||
|
e5b88be5fa | ||
|
425552988a | ||
|
a81dd8abe5 | ||
|
737d15fa0e |
13
.env.example
13
.env.example
@@ -2,6 +2,7 @@ APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=homestead
|
||||
@@ -11,14 +12,22 @@ DB_PASSWORD=secret
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
|
||||
DEFAULT_CURRENCY=EUR
|
||||
DEFAULT_LANGUAGE=en_US
|
||||
|
||||
EMAIL_SMTP=
|
||||
EMAIL_DRIVER=smtp
|
||||
EMAIL_USERNAME=
|
||||
EMAIL_PASSWORD=
|
||||
ANALYTICS_ID=
|
||||
EMAIL_PRETEND=false
|
||||
|
||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||
|
||||
ANALYTICS_ID=
|
||||
RUNCLEANUP=true
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
SENDGRID_USERNAME=
|
||||
SENDGRID_PASSWORD=
|
||||
SENDGRID_PASSWORD=
|
||||
|
||||
BLOCKED_DOMAINS=
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,7 +5,7 @@ Thumbs.db
|
||||
.idea/
|
||||
tests/_output/*
|
||||
_ide_helper.php
|
||||
/build/logs/clover.xml
|
||||
/build/logs
|
||||
index.html*
|
||||
app/storage/firefly-export*
|
||||
.vagrant
|
||||
|
@@ -10,6 +10,9 @@ install:
|
||||
- composer update
|
||||
- php artisan env
|
||||
- mv -v .env.testing .env
|
||||
- touch storage/database/testing.db
|
||||
- php artisan migrate --env=testing
|
||||
- php artisan migrate --seed --env=testing
|
||||
|
||||
script:
|
||||
- phpunit
|
||||
|
29
README.md
29
README.md
@@ -19,7 +19,6 @@ their current cashflow. There are tons of ways to save and earn money.
|
||||
|
||||
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
|
||||
|
||||
|
||||
To get to know Firefly, and to see if it fits you, check out these resources:
|
||||
|
||||
- The screenshots below on this very page.
|
||||
@@ -61,19 +60,19 @@ Everything is organised:
|
||||
|
||||
_Please note that everything in these screenshots is fictional and may not be realistic._
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## Running and installing
|
||||
|
||||
@@ -82,7 +81,19 @@ If you're still interested please read [the installation guide](https://github.c
|
||||
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
|
||||
|
||||
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
|
||||
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one week.
|
||||
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one month. It's a trial.
|
||||
|
||||
## Security
|
||||
|
||||
You should always run Firefly III on a site with TLS enabled (https://). Please note that although some parts of the
|
||||
database are encrypted (transaction descriptions, names, etc.) some parts are _not_ (amounts, dates, etc). If you need
|
||||
more security, you must enable transparent database encryption or a comparable technology. Please remember that this
|
||||
is open source software under active development, and it is in no way guaranteed to be safe or secure.
|
||||
|
||||
## Translations
|
||||
|
||||
Firefly III is currently available in Dutch and English. Support for other languages is being worked on. I can use
|
||||
your help. Checkout [Crowdin](https://crowdin.com/project/firefly-iii) for more information.
|
||||
|
||||
## Credits
|
||||
|
||||
|
@@ -14,15 +14,6 @@ use Illuminate\Support\Collection;
|
||||
interface AccountChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
|
@@ -3,10 +3,8 @@
|
||||
namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
@@ -17,21 +15,6 @@ use Steam;
|
||||
class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
return $this->frontpage($accounts, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
@@ -42,10 +25,10 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$data = [
|
||||
'count' => 1,
|
||||
'labels' => [], 'datasets' => [[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []]]];
|
||||
'count' => 1,
|
||||
'labels' => [], 'datasets' => [[
|
||||
'label' => trans('firefly.spent'),
|
||||
'data' => []]]];
|
||||
|
||||
bcscale(2);
|
||||
$start->subDay();
|
||||
@@ -105,21 +88,21 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
$data = [
|
||||
$format = trans('config.month_and_day');
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
$current = clone $start;
|
||||
$current = clone $start;
|
||||
while ($current <= $end) {
|
||||
$data['labels'][] = $current->formatLocalized($format);
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$set = [
|
||||
$set = [
|
||||
'label' => $account->name,
|
||||
'fillColor' => 'rgba(220,220,220,0.2)',
|
||||
'strokeColor' => 'rgba(220,220,220,1)',
|
||||
@@ -129,9 +112,15 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
'pointHighlightStroke' => 'rgba(220,220,220,1)',
|
||||
'data' => [],
|
||||
];
|
||||
$current = clone $start;
|
||||
$current = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, $end);
|
||||
$previous = array_values($range)[0];
|
||||
while ($current <= $end) {
|
||||
$set['data'][] = Steam::balance($account, $current);
|
||||
$format = $current->format('Y-m-d');
|
||||
$balance = isset($range[$format]) ? $range[$format] : $previous;
|
||||
|
||||
$set['data'][] = $balance;
|
||||
$previous = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
$data['datasets'][] = $set;
|
||||
@@ -151,8 +140,7 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
|
||||
public function single(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
$format = trans('config.month_and_day');
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
|
@@ -33,7 +33,7 @@ class ChartJsBillChartGenerator implements BillChartGenerator
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($paid as $entry) { // loop paid and create single entry:
|
||||
$paidDescriptions[] = $entry->description;
|
||||
$paidAmount = bcadd($paidAmount, $entry->amount);
|
||||
$paidAmount = bcadd($paidAmount, $entry->amount_positive);
|
||||
}
|
||||
/** @var Bill $entry */
|
||||
foreach ($unpaid as $entry) { // loop unpaid:
|
||||
@@ -71,8 +71,7 @@ class ChartJsBillChartGenerator implements BillChartGenerator
|
||||
public function single(Bill $bill, Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 3,
|
||||
@@ -91,7 +90,7 @@ class ChartJsBillChartGenerator implements BillChartGenerator
|
||||
$data['labels'][] = $entry->date->formatLocalized($format);
|
||||
$minAmount[] = round($bill->amount_min, 2);
|
||||
$maxAmount[] = round($bill->amount_max, 2);
|
||||
$actualAmount[] = round($entry->amount, 2);
|
||||
$actualAmount[] = round(($entry->amount * -1), 2);
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
|
@@ -32,6 +32,13 @@ interface BudgetChartGenerator
|
||||
*/
|
||||
public function frontpage(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
|
@@ -24,7 +24,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
public function budget(Collection $entries, $dateFormat = 'month')
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
|
||||
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
|
||||
|
||||
$data = [
|
||||
@@ -33,7 +33,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
[
|
||||
'label' => 'Amount',
|
||||
'data' => [],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -115,8 +115,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
public function year(Collection $budgets, Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = trans('config.month');
|
||||
|
||||
$data = [
|
||||
'labels' => [],
|
||||
@@ -141,4 +140,37 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries)
|
||||
{
|
||||
// dataset:
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// get labels from one of the budgets (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
foreach ($first['budgeted'] as $year => $noInterest) {
|
||||
$data['labels'][] = strval($year);
|
||||
}
|
||||
|
||||
// then, loop all entries and create datasets:
|
||||
foreach ($entries as $entry) {
|
||||
$name = $entry['name'];
|
||||
$spent = $entry['spent'];
|
||||
$budgeted = $entry['budgeted'];
|
||||
$data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)];
|
||||
$data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)];
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,14 @@ interface CategoryChartGenerator
|
||||
*/
|
||||
public function all(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInPeriod(Collection $categories, Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
@@ -31,8 +39,14 @@ interface CategoryChartGenerator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function month(Collection $entries);
|
||||
public function multiYear(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
@@ -41,12 +55,4 @@ interface CategoryChartGenerator
|
||||
* @return array
|
||||
*/
|
||||
public function spentInYear(Collection $categories, Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInYear(Collection $categories, Collection $entries);
|
||||
}
|
||||
|
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,16 +15,12 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $dateFormat
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all(Collection $entries, $dateFormat = 'month')
|
||||
public function all(Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
@@ -44,15 +38,12 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized($format);
|
||||
$amount = round($entry[1], 2);
|
||||
if ($amount > 0) {
|
||||
$data['datasets'][0]['data'][] = null;
|
||||
$data['datasets'][1]['data'][] = $amount;
|
||||
} else {
|
||||
$data['datasets'][0]['data'][] = $amount * -1;
|
||||
$data['datasets'][1]['data'][] = null;
|
||||
}
|
||||
$data['labels'][] = $entry[1];
|
||||
$spent = round($entry[2], 2);
|
||||
$earned = round($entry[3], 2);
|
||||
|
||||
$data['datasets'][0]['data'][] = $spent == 0 ? null : $spent * -1;
|
||||
$data['datasets'][1]['data'][] = $earned == 0 ? null : $earned;
|
||||
}
|
||||
|
||||
return $data;
|
||||
@@ -78,7 +69,7 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry['sum'] != 0) {
|
||||
$data['labels'][] = $entry['name'];
|
||||
$data['datasets'][0]['data'][] = round($entry['sum'], 2);
|
||||
$data['datasets'][0]['data'][] = round(($entry['sum'] * -1), 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +83,9 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function month(Collection $entries)
|
||||
public function period(Collection $entries)
|
||||
{
|
||||
return $this->all($entries, 'monthAndDay');
|
||||
return $this->all($entries);
|
||||
|
||||
}
|
||||
|
||||
@@ -108,8 +99,7 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
@@ -138,12 +128,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInYear(Collection $categories, Collection $entries)
|
||||
public function earnedInPeriod(Collection $categories, Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
@@ -165,4 +154,74 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function spentInPeriod(Collection $categories, Collection $entries)
|
||||
{
|
||||
|
||||
// language:
|
||||
$format = trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$data['labels'][] = $category->name;
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$date = $entry[0]->formatLocalized($format);
|
||||
array_shift($entry);
|
||||
$data['count']++;
|
||||
$data['datasets'][] = ['label' => $date, 'data' => $entry];
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries)
|
||||
{
|
||||
// dataset:
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// get labels from one of the categories (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
foreach ($first['spent'] as $year => $noInterest) {
|
||||
$data['labels'][] = strval($year);
|
||||
}
|
||||
|
||||
// then, loop all entries and create datasets:
|
||||
foreach ($entries as $entry) {
|
||||
$name = $entry['name'];
|
||||
$spent = $entry['spent'];
|
||||
$earned = $entry['earned'];
|
||||
if (array_sum(array_values($spent)) != 0) {
|
||||
$data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)];
|
||||
}
|
||||
if (array_sum(array_values($earned)) != 0) {
|
||||
$data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)];
|
||||
}
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -25,8 +25,7 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
|
||||
{
|
||||
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.monthAndDay.' . $language);
|
||||
$format = trans('config.month_and_day');
|
||||
|
||||
$data = [
|
||||
'count' => 1,
|
||||
|
@@ -22,8 +22,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
public function yearInOut(Collection $entries)
|
||||
{
|
||||
// language:
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$format = trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 2,
|
||||
@@ -49,6 +48,39 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above but other translations.
|
||||
*
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOut(Collection $entries)
|
||||
{
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => []
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry[0]->formatLocalized('%Y');
|
||||
$data['datasets'][0]['data'][] = round($entry[1], 2);
|
||||
$data['datasets'][1]['data'][] = round($entry[2], 2);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
@@ -80,4 +112,35 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOutSummarized($income, $expense, $count)
|
||||
{
|
||||
$data = [
|
||||
'count' => 2,
|
||||
'labels' => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')],
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => trans('firefly.income'),
|
||||
'data' => []
|
||||
],
|
||||
[
|
||||
'label' => trans('firefly.expenses'),
|
||||
'data' => []
|
||||
]
|
||||
],
|
||||
];
|
||||
$data['datasets'][0]['data'][] = round($income, 2);
|
||||
$data['datasets'][1]['data'][] = round($expense, 2);
|
||||
$data['datasets'][0]['data'][] = round(($income / $count), 2);
|
||||
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,22 @@ use Illuminate\Support\Collection;
|
||||
interface ReportChartGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOut(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param string $income
|
||||
* @param string $expense
|
||||
* @param int $count
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYearInOutSummarized($income, $expense, $count);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
|
@@ -53,7 +53,7 @@ class ConnectJournalToPiggyBank
|
||||
}
|
||||
bcscale(2);
|
||||
|
||||
$amount = $journal->actual_amount;
|
||||
$amount = $journal->amount_positive;
|
||||
// if piggy account matches source account, the amount is positive
|
||||
if ($piggyBank->account_id == $journal->source_account->id) {
|
||||
$amount = $amount * -1;
|
||||
|
@@ -150,24 +150,4 @@ class BalanceLine
|
||||
{
|
||||
$this->balanceEntries = $balanceEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the BalanceEntries for a BalanceLine have a "left" value, the amount
|
||||
* of money left in the entire BalanceLine is returned here:
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function sumOfLeft()
|
||||
{
|
||||
$sum = '0';
|
||||
bcscale(2);
|
||||
/** @var BalanceEntry $balanceEntry */
|
||||
foreach ($this->getBalanceEntries() as $balanceEntry) {
|
||||
$sum = bcadd($sum, $balanceEntry->getLeft());
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ class Category
|
||||
// spent is minus zero for an expense report:
|
||||
if ($category->spent < 0) {
|
||||
$this->categories->push($category);
|
||||
$this->addTotal($category->spent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +56,7 @@ class Category
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
$set = $this->categories->sortByDesc(
|
||||
$set = $this->categories->sortBy(
|
||||
function (CategoryModel $category) {
|
||||
return $category->spent;
|
||||
}
|
||||
|
@@ -33,19 +33,24 @@ class Expense
|
||||
*/
|
||||
public function addOrCreateExpense(TransactionJournal $entry)
|
||||
{
|
||||
bcscale(2);
|
||||
|
||||
$accountId = $entry->account_id;
|
||||
$amount = strval(round($entry->amount, 2));
|
||||
if (bccomp('0', $amount) === -1) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
|
||||
if (!$this->expenses->has($accountId)) {
|
||||
$newObject = new stdClass;
|
||||
$newObject->amount = strval(round($entry->amount, 2));
|
||||
$newObject->amount = $amount;
|
||||
$newObject->name = $entry->name;
|
||||
$newObject->count = 1;
|
||||
$newObject->id = $accountId;
|
||||
$this->expenses->put($accountId, $newObject);
|
||||
} else {
|
||||
bcscale(2);
|
||||
$existing = $this->expenses->get($accountId);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount);
|
||||
$existing->amount = bcadd($existing->amount, $amount);
|
||||
$existing->count++;
|
||||
$this->expenses->put($accountId, $existing);
|
||||
}
|
||||
@@ -56,8 +61,18 @@ class Expense
|
||||
*/
|
||||
public function addToTotal($add)
|
||||
{
|
||||
$add = strval(round($add, 2));
|
||||
bcscale(2);
|
||||
|
||||
|
||||
$add = strval(round($add, 2));
|
||||
if (bccomp('0', $add) === -1) {
|
||||
$add = bcmul($add, '-1');
|
||||
}
|
||||
|
||||
// if amount is positive, the original transaction
|
||||
// was a transfer. But since this is an expense report,
|
||||
// that amount must be negative.
|
||||
|
||||
$this->total = bcadd($this->total, $add);
|
||||
}
|
||||
|
||||
@@ -66,7 +81,7 @@ class Expense
|
||||
*/
|
||||
public function getExpenses()
|
||||
{
|
||||
$set = $this->expenses->sortByDesc(
|
||||
$set = $this->expenses->sortBy(
|
||||
function (stdClass $object) {
|
||||
return $object->amount;
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ class Income
|
||||
$accountId = $entry->account_id;
|
||||
if (!$this->incomes->has($accountId)) {
|
||||
$newObject = new stdClass;
|
||||
$newObject->amount = strval(round($entry->amount, 2));
|
||||
$newObject->amount = strval(round($entry->amount_positive, 2));
|
||||
$newObject->name = $entry->name;
|
||||
$newObject->count = 1;
|
||||
$newObject->id = $accountId;
|
||||
@@ -46,7 +46,7 @@ class Income
|
||||
} else {
|
||||
bcscale(2);
|
||||
$existing = $this->incomes->get($accountId);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount);
|
||||
$existing->amount = bcadd($existing->amount, $entry->amount_positive);
|
||||
$existing->count++;
|
||||
$this->incomes->put($accountId, $existing);
|
||||
}
|
||||
|
@@ -296,7 +296,7 @@ class Importer
|
||||
|
||||
// some debug info:
|
||||
$journalId = $journal->id;
|
||||
$type = $journal->transactionType->type;
|
||||
$type = $journal->getTransactionType();
|
||||
/** @var Account $asset */
|
||||
$asset = $this->importData['asset-account-object'];
|
||||
/** @var Account $opposing */
|
||||
@@ -314,13 +314,13 @@ class Importer
|
||||
*/
|
||||
protected function getTransactionType()
|
||||
{
|
||||
$transactionType = TransactionType::where('type', 'Deposit')->first();
|
||||
$transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first();
|
||||
if ($this->importData['amount'] < 0) {
|
||||
$transactionType = TransactionType::where('type', 'Withdrawal')->first();
|
||||
$transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
|
||||
}
|
||||
|
||||
if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) {
|
||||
$transactionType = TransactionType::where('type', 'Transfer')->first();
|
||||
$transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first();
|
||||
}
|
||||
|
||||
return $transactionType;
|
||||
|
@@ -24,7 +24,7 @@ class Currency implements PostProcessorInterface
|
||||
|
||||
// fix currency
|
||||
if (is_null($this->data['currency'])) {
|
||||
$currencyPreference = Preferences::get('currencyPreference', 'EUR');
|
||||
$currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
|
||||
$this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first();
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,8 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget as BudgetModel;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use Illuminate\Support\Collection;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class ReportHelper
|
||||
@@ -43,48 +45,113 @@ class ReportHelper implements ReportHelperInterface
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return CategoryCollection
|
||||
*/
|
||||
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$object = new CategoryCollection;
|
||||
|
||||
/**
|
||||
* GET CATEGORIES:
|
||||
*/
|
||||
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
|
||||
$set = $repository->getCategories();
|
||||
foreach ($set as $category) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $accounts);
|
||||
$category->spent = $spent;
|
||||
$object->addCategory($category);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listOfMonths(Carbon $date)
|
||||
{
|
||||
|
||||
$start = clone $date;
|
||||
$end = Carbon::now();
|
||||
$months = [];
|
||||
while ($start <= $end) {
|
||||
$year = $start->year;
|
||||
|
||||
if (!isset($months[$year])) {
|
||||
$months[$year] = [
|
||||
'start' => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'),
|
||||
'end' => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'),
|
||||
'months' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$currentEnd = clone $start;
|
||||
$currentEnd->endOfMonth();
|
||||
$months[$year]['months'][] = [
|
||||
'formatted' => $start->formatLocalized('%B %Y'),
|
||||
'start' => $start->format('Y-m-d'),
|
||||
'end' => $currentEnd->format('Y-m-d'),
|
||||
'month' => $start->month,
|
||||
'year' => $year,
|
||||
];
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
return $months;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates a full report for the given period on all
|
||||
* the users asset and cash accounts.
|
||||
* given accounts
|
||||
*
|
||||
* @param Carbon $date
|
||||
* @param Carbon $end
|
||||
* @param $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return AccountCollection
|
||||
*/
|
||||
public function getAccountReport(Carbon $date, Carbon $end, $shared)
|
||||
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
|
||||
$accounts = $this->query->getAllAccounts($date, $end, $shared);
|
||||
$start = '0';
|
||||
$end = '0';
|
||||
$diff = '0';
|
||||
$startAmount = '0';
|
||||
$endAmount = '0';
|
||||
$diff = '0';
|
||||
bcscale(2);
|
||||
|
||||
// remove cash account, if any:
|
||||
$accounts = $accounts->filter(
|
||||
function (Account $account) {
|
||||
if ($account->accountType->type != 'Cash account') {
|
||||
return $account;
|
||||
}
|
||||
$accounts->each(
|
||||
function (Account $account) use ($start, $end) {
|
||||
/**
|
||||
* The balance for today always incorporates transactions
|
||||
* made on today. So to get todays "start" balance, we sub one
|
||||
* day.
|
||||
*/
|
||||
$yesterday = clone $start;
|
||||
$yesterday->subDay();
|
||||
|
||||
return null;
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$account->startBalance = Steam::balance($account, $yesterday);
|
||||
$account->endBalance = Steam::balance($account, $end);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// summarize:
|
||||
foreach ($accounts as $account) {
|
||||
$start = bcadd($start, $account->startBalance);
|
||||
$end = bcadd($end, $account->endBalance);
|
||||
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
|
||||
$startAmount = bcadd($startAmount, $account->startBalance);
|
||||
$endAmount = bcadd($endAmount, $account->endBalance);
|
||||
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
|
||||
}
|
||||
|
||||
$object = new AccountCollection;
|
||||
$object->setStart($start);
|
||||
$object->setEnd($end);
|
||||
$object->setStart($startAmount);
|
||||
$object->setEnd($endAmount);
|
||||
$object->setDifference($diff);
|
||||
$object->setAccounts($accounts);
|
||||
|
||||
@@ -92,37 +159,136 @@ class ReportHelper implements ReportHelperInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full report on the users incomes during the period for the given accounts.
|
||||
*
|
||||
* The balance report contains a Balance object which in turn contains:
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* A BalanceHeader object which contains all relevant user asset accounts for the report.
|
||||
* @return Income
|
||||
*/
|
||||
public function getIncomeReport($start, $end, Collection $accounts)
|
||||
{
|
||||
$object = new Income;
|
||||
$set = $this->query->incomeInPeriod($start, $end, $accounts);
|
||||
foreach ($set as $entry) {
|
||||
$object->addToTotal($entry->amount_positive);
|
||||
$object->addOrCreateIncome($entry);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full report on the users expenses during the period for a list of accounts.
|
||||
*
|
||||
* A number of BalanceLine objects, which hold:
|
||||
* - A budget
|
||||
* - A number of BalanceEntry objects.
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* The BalanceEntry object holds:
|
||||
* - The same budget (again)
|
||||
* - A user asset account as mentioned in the BalanceHeader
|
||||
* - The amount of money spent on the budget by the user asset account
|
||||
* @return Expense
|
||||
*/
|
||||
public function getExpenseReport($start, $end, Collection $accounts)
|
||||
{
|
||||
$object = new Expense;
|
||||
$set = $this->query->expenseInPeriod($start, $end, $accounts);
|
||||
foreach ($set as $entry) {
|
||||
$object->addToTotal($entry->amount); // can be positive, if it's a transfer
|
||||
$object->addOrCreateExpense($entry);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @return BudgetCollection
|
||||
*/
|
||||
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$object = new BudgetCollection;
|
||||
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$set = $repository->getBudgets();
|
||||
|
||||
bcscale(2);
|
||||
|
||||
foreach ($set as $budget) {
|
||||
|
||||
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
|
||||
|
||||
// no repetition(s) for this budget:
|
||||
if ($repetitions->count() == 0) {
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setOverspent($spent);
|
||||
$object->addOverspent($spent);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
// one or more repetitions for budget:
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setRepetition($repetition);
|
||||
$expenses = $repository->balanceInPeriod($budget, $start, $end, $accounts);
|
||||
|
||||
// 200 en -100 is 100, vergeleken met 0 === 1
|
||||
// 200 en -200 is 0, vergeleken met 0 === 0
|
||||
// 200 en -300 is -100, vergeleken met 0 === -1
|
||||
|
||||
$left = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : 0;
|
||||
$spent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0';
|
||||
$overspent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount);
|
||||
|
||||
$budgetLine->setLeft($left);
|
||||
$budgetLine->setSpent($spent);
|
||||
$budgetLine->setOverspent($overspent);
|
||||
$budgetLine->setBudgeted($repetition->amount);
|
||||
|
||||
$object->addBudgeted($repetition->amount);
|
||||
$object->addSpent($spent);
|
||||
$object->addLeft($left);
|
||||
$object->addOverspent($overspent);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stuff outside of budgets:
|
||||
$noBudget = $repository->getWithoutBudgetSum($start, $end);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setOverspent($noBudget);
|
||||
$budgetLine->setSpent($noBudget);
|
||||
$object->addOverspent($noBudget);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Balance
|
||||
*/
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface');
|
||||
$balance = new Balance;
|
||||
|
||||
// build a balance header:
|
||||
$header = new BalanceHeader;
|
||||
|
||||
$accounts = $this->query->getAllAccounts($start, $end, $shared);
|
||||
$budgets = $repository->getBudgets();
|
||||
$header = new BalanceHeader;
|
||||
$budgets = $repository->getBudgets();
|
||||
foreach ($accounts as $account) {
|
||||
$header->addAccount($account);
|
||||
}
|
||||
@@ -133,7 +299,8 @@ class ReportHelper implements ReportHelperInterface
|
||||
$line->setBudget($budget);
|
||||
|
||||
// get budget amount for current period:
|
||||
$rep = $repository->getCurrentRepetition($budget, $start);
|
||||
$rep = $repository->getCurrentRepetition($budget, $start, $end);
|
||||
// could be null?
|
||||
$line->setRepetition($rep);
|
||||
|
||||
// loop accounts:
|
||||
@@ -142,7 +309,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
$balanceEntry->setAccount($account);
|
||||
|
||||
// get spent:
|
||||
$spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant.
|
||||
$spent = $this->query->spentInBudget($account, $budget, $start, $end); // I think shared is irrelevant.
|
||||
|
||||
$balanceEntry->setSpent($spent);
|
||||
$line->addBalanceEntry($balanceEntry);
|
||||
@@ -153,6 +320,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
|
||||
// then a new line for without budget.
|
||||
// and one for the tags:
|
||||
// and one for "left unbalanced".
|
||||
$empty = new BalanceLine;
|
||||
$tags = new BalanceLine;
|
||||
$diffLine = new BalanceLine;
|
||||
@@ -164,7 +332,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
$spent = $this->query->spentNoBudget($account, $start, $end);
|
||||
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
|
||||
bcscale(2);
|
||||
$diff = bcsub($spent, $left);
|
||||
$diff = bcadd($spent, $left);
|
||||
|
||||
// budget
|
||||
$budgetEntry = new BalanceEntry;
|
||||
@@ -199,16 +367,19 @@ class ReportHelper implements ReportHelperInterface
|
||||
* This method generates a full report for the given period on all
|
||||
* the users bills and their payments.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* Excludes bills which have not had a payment on the mentioned accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return BillCollection
|
||||
*/
|
||||
public function getBillReport(Carbon $start, Carbon $end)
|
||||
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
|
||||
$bills = $repository->getBills();
|
||||
$bills = $repository->getBillsForAccounts($accounts);
|
||||
$collection = new BillCollection;
|
||||
|
||||
/** @var Bill $bill */
|
||||
@@ -238,167 +409,5 @@ class ReportHelper implements ReportHelperInterface
|
||||
}
|
||||
|
||||
return $collection;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return BudgetCollection
|
||||
*/
|
||||
public function getBudgetReport(Carbon $start, Carbon $end, $shared)
|
||||
{
|
||||
$object = new BudgetCollection;
|
||||
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$set = $repository->getBudgets();
|
||||
|
||||
bcscale(2);
|
||||
|
||||
foreach ($set as $budget) {
|
||||
|
||||
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
|
||||
|
||||
// no repetition(s) for this budget:
|
||||
if ($repetitions->count() == 0) {
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setOverspent($spent);
|
||||
$object->addOverspent($spent);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
// one or more repetitions for budget:
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setBudget($budget);
|
||||
$budgetLine->setRepetition($repetition);
|
||||
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared);
|
||||
$left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0;
|
||||
$spent = $expenses > $repetition->amount ? 0 : $expenses;
|
||||
$overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0;
|
||||
|
||||
$budgetLine->setLeft($left);
|
||||
$budgetLine->setSpent($spent);
|
||||
$budgetLine->setOverspent($overspent);
|
||||
$budgetLine->setBudgeted($repetition->amount);
|
||||
|
||||
$object->addBudgeted($repetition->amount);
|
||||
$object->addSpent($spent);
|
||||
$object->addLeft($left);
|
||||
$object->addOverspent($overspent);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stuff outside of budgets:
|
||||
$noBudget = $repository->getWithoutBudgetSum($start, $end);
|
||||
$budgetLine = new BudgetLine;
|
||||
$budgetLine->setOverspent($noBudget);
|
||||
$object->addOverspent($noBudget);
|
||||
$object->addBudgetLine($budgetLine);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return CategoryCollection
|
||||
*/
|
||||
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
|
||||
{
|
||||
$object = new CategoryCollection;
|
||||
|
||||
|
||||
/**
|
||||
* GET CATEGORIES:
|
||||
*/
|
||||
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
|
||||
$set = $repository->getCategories();
|
||||
foreach ($set as $category) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
|
||||
$category->spent = $spent;
|
||||
$object->addCategory($category);
|
||||
$object->addTotal($spent);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full report on the users expenses during the period.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return Expense
|
||||
*/
|
||||
public function getExpenseReport($start, $end, $shared)
|
||||
{
|
||||
$object = new Expense;
|
||||
$set = $this->query->expenseInPeriodCorrected($start, $end, $shared);
|
||||
foreach ($set as $entry) {
|
||||
$object->addToTotal($entry->amount);
|
||||
$object->addOrCreateExpense($entry);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full report on the users incomes during the period.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return Income
|
||||
*/
|
||||
public function getIncomeReport($start, $end, $shared)
|
||||
{
|
||||
$object = new Income;
|
||||
$set = $this->query->incomeInPeriodCorrected($start, $end, $shared);
|
||||
foreach ($set as $entry) {
|
||||
$object->addToTotal($entry->amount);
|
||||
$object->addOrCreateIncome($entry);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listOfMonths(Carbon $date)
|
||||
{
|
||||
|
||||
$start = clone $date;
|
||||
$end = Carbon::now();
|
||||
$months = [];
|
||||
while ($start <= $end) {
|
||||
$year = $start->year;
|
||||
$months[$year][] = [
|
||||
'formatted' => $start->formatLocalized('%B %Y'),
|
||||
'month' => $start->month,
|
||||
'year' => $year,
|
||||
];
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
return $months;
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
|
||||
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
|
||||
use FireflyIII\Helpers\Collection\Expense;
|
||||
use FireflyIII\Helpers\Collection\Income;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface ReportHelperInterface
|
||||
@@ -21,75 +22,78 @@ interface ReportHelperInterface
|
||||
|
||||
/**
|
||||
* This method generates a full report for the given period on all
|
||||
* the users asset and cash accounts.
|
||||
* given accounts
|
||||
*
|
||||
* @param Carbon $date
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return AccountCollection
|
||||
*/
|
||||
public function getAccountReport(Carbon $date, Carbon $end, $shared);
|
||||
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* This method generates a full report for the given period on all
|
||||
* the users bills and their payments.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* Excludes bills which have not had a payment on the mentioned accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return BillCollection
|
||||
*/
|
||||
public function getBillReport(Carbon $start, Carbon $end);
|
||||
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Balance
|
||||
*/
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, $shared);
|
||||
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return BudgetCollection
|
||||
*/
|
||||
public function getBudgetReport(Carbon $start, Carbon $end, $shared);
|
||||
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return CategoryCollection
|
||||
*/
|
||||
public function getCategoryReport(Carbon $start, Carbon $end, $shared);
|
||||
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* Get a full report on the users expenses during the period.
|
||||
* Get a full report on the users expenses during the period for a list of accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Expense
|
||||
*/
|
||||
public function getExpenseReport($start, $end, $shared);
|
||||
public function getExpenseReport($start, $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* Get a full report on the users incomes during the period.
|
||||
* Get a full report on the users incomes during the period for the given accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Income
|
||||
*/
|
||||
public function getIncomeReport($start, $end, $shared);
|
||||
public function getIncomeReport($start, $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
|
@@ -8,10 +8,10 @@ use Crypt;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class ReportQuery
|
||||
@@ -20,185 +20,6 @@ use Steam;
|
||||
*/
|
||||
class ReportQuery implements ReportQueryInterface
|
||||
{
|
||||
/**
|
||||
* See ReportQueryInterface::incomeInPeriodCorrected.
|
||||
*
|
||||
* This method's length is caused mainly by the query build stuff. Therefor:
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $includeShared
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
*/
|
||||
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
|
||||
{
|
||||
$query = $this->queryJournalsWithTransactions($start, $end);
|
||||
if ($includeShared === false) {
|
||||
$query->where(
|
||||
function (Builder $query) {
|
||||
$query->where(
|
||||
function (Builder $q) { // only get withdrawals not from a shared account
|
||||
$q->where('transaction_types.type', 'Withdrawal');
|
||||
$q->where('acm_from.data', '!=', '"sharedAsset"');
|
||||
}
|
||||
);
|
||||
$query->orWhere(
|
||||
function (Builder $q) { // and transfers from a shared account.
|
||||
$q->where('transaction_types.type', 'Transfer');
|
||||
$q->where('acm_to.data', '=', '"sharedAsset"');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
|
||||
}
|
||||
$query->orderBy('transaction_journals.date');
|
||||
$data = $query->get( // get everything
|
||||
['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
|
||||
);
|
||||
|
||||
$data->each(
|
||||
function (TransactionJournal $journal) {
|
||||
if (intval($journal->account_encrypted) == 1) {
|
||||
$journal->name = Crypt::decrypt($journal->name);
|
||||
}
|
||||
}
|
||||
);
|
||||
$data = $data->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
if ($journal->amount != 0) {
|
||||
return $journal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a users accounts combined with various meta-data related to the start and end date.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $includeShared
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false)
|
||||
{
|
||||
$query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
|
||||
->accountTypeIn(['Default account', 'Asset account', 'Cash account']);
|
||||
if ($includeShared === 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');
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
$set = $query->get(['accounts.*']);
|
||||
$set->each(
|
||||
function (Account $account) use ($start, $end) {
|
||||
/**
|
||||
* The balance for today always incorporates transactions
|
||||
* made on today. So to get todays "start" balance, we sub one
|
||||
* day.
|
||||
*/
|
||||
$yesterday = clone $start;
|
||||
$yesterday->subDay();
|
||||
|
||||
/** @noinspection PhpParamsInspection */
|
||||
$account->startBalance = Steam::balance($account, $yesterday);
|
||||
$account->endBalance = Steam::balance($account, $end);
|
||||
}
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
|
||||
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
|
||||
* regards to tags.
|
||||
*
|
||||
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
|
||||
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
|
||||
* not group and returns different fields.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $includeShared
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
|
||||
{
|
||||
$query = $this->queryJournalsWithTransactions($start, $end);
|
||||
if ($includeShared === false) {
|
||||
// only get deposits not to a shared account
|
||||
// and transfers to a shared account.
|
||||
$query->where(
|
||||
function (Builder $query) {
|
||||
$query->where(
|
||||
function (Builder $q) {
|
||||
$q->where('transaction_types.type', 'Deposit');
|
||||
$q->where('acm_to.data', '!=', '"sharedAsset"');
|
||||
}
|
||||
);
|
||||
$query->orWhere(
|
||||
function (Builder $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->orderBy('transaction_journals.date');
|
||||
|
||||
// get everything
|
||||
$data = $query->get(
|
||||
['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted']
|
||||
);
|
||||
|
||||
$data->each(
|
||||
function (TransactionJournal $journal) {
|
||||
if (intval($journal->account_encrypted) == 1) {
|
||||
$journal->name = Crypt::decrypt($journal->name);
|
||||
}
|
||||
}
|
||||
);
|
||||
$data = $data->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
if ($journal->amount != 0) {
|
||||
return $journal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Covers tags
|
||||
*
|
||||
@@ -209,22 +30,18 @@ class ReportQuery implements ReportQueryInterface
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
|
||||
public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end)
|
||||
{
|
||||
|
||||
bcscale(2);
|
||||
|
||||
return bcmul(
|
||||
Auth::user()->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->transactionTypes(['Withdrawal'])
|
||||
->where('transactions.account_id', $account->id)
|
||||
->before($end)
|
||||
->after($start)
|
||||
->where('budget_transaction_journal.budget_id', $budget->id)
|
||||
->get(['transaction_journals.*'])->sum('amount'), -1
|
||||
);
|
||||
return Auth::user()->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->transactionTypes([TransactionType::WITHDRAWAL])
|
||||
->where('transactions.account_id', $account->id)
|
||||
->before($end)
|
||||
->after($start)
|
||||
->where('budget_transaction_journal.budget_id', $budget->id)
|
||||
->get(['transaction_journals.*'])->sum('amount');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +57,7 @@ class ReportQuery implements ReportQueryInterface
|
||||
Auth::user()->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->transactionTypes(['Withdrawal'])
|
||||
->transactionTypes([TransactionType::WITHDRAWAL])
|
||||
->where('transactions.account_id', $account->id)
|
||||
->before($end)
|
||||
->after($start)
|
||||
@@ -283,4 +100,144 @@ class ReportQuery implements ReportQueryInterface
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
|
||||
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
|
||||
* regards to tags. It will only get the incomes to the specified accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$query = $this->queryJournalsWithTransactions($start, $end);
|
||||
|
||||
$ids = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$ids[] = $account->id;
|
||||
}
|
||||
|
||||
// OR is a deposit
|
||||
// OR any transfer TO the accounts in $accounts, not FROM any of the accounts in $accounts.
|
||||
$query->where(
|
||||
function (Builder $query) use ($ids) {
|
||||
$query->where(
|
||||
function (Builder $q) {
|
||||
$q->where('transaction_types.type', TransactionType::DEPOSIT);
|
||||
}
|
||||
);
|
||||
$query->orWhere(
|
||||
function (Builder $q) use ($ids) {
|
||||
$q->where('transaction_types.type', TransactionType::TRANSFER);
|
||||
$q->whereNotIn('ac_from.id', $ids);
|
||||
$q->whereIn('ac_to.id', $ids);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// only include selected accounts.
|
||||
$query->whereIn('ac_to.id', $ids);
|
||||
$query->orderBy('transaction_journals.date');
|
||||
|
||||
// get everything
|
||||
$data = $query->get(
|
||||
['transaction_journals.*',
|
||||
'transaction_types.type', 'ac_from.name as name',
|
||||
't_from.amount as from_amount',
|
||||
't_to.amount as to_amount',
|
||||
'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted']
|
||||
);
|
||||
|
||||
$data->each(
|
||||
function (TransactionJournal $journal) {
|
||||
if (intval($journal->account_encrypted) == 1) {
|
||||
$journal->name = Crypt::decrypt($journal->name);
|
||||
}
|
||||
}
|
||||
);
|
||||
// $data = $data->filter(
|
||||
// function (TransactionJournal $journal) {
|
||||
// if ($journal->amount != 0) {
|
||||
// return $journal;
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
// );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* See ReportQueryInterface::incomeInPeriod
|
||||
*
|
||||
* This method returns all "expense" journals in a certain period, which are both transfers to a shared account
|
||||
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
|
||||
* not group and returns different fields.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
*/
|
||||
public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$ids = [];
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$ids[] = $account->id;
|
||||
}
|
||||
|
||||
$query = $this->queryJournalsWithTransactions($start, $end);
|
||||
|
||||
// withdrawals from any account are an expense.
|
||||
// transfers away, from an account in the list, to an account not in the list, are an expense.
|
||||
|
||||
$query->where(
|
||||
function (Builder $query) use ($ids) {
|
||||
$query->where(
|
||||
function (Builder $q) {
|
||||
$q->where('transaction_types.type', TransactionType::WITHDRAWAL);
|
||||
}
|
||||
);
|
||||
$query->orWhere(
|
||||
function (Builder $q) use ($ids) {
|
||||
$q->where('transaction_types.type', TransactionType::TRANSFER);
|
||||
$q->whereIn('ac_from.id', $ids);
|
||||
$q->whereNotIn('ac_to.id', $ids);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// expense goes from the selected accounts:
|
||||
$query->whereIn('ac_from.id', $ids);
|
||||
|
||||
$query->orderBy('transaction_journals.date');
|
||||
$data = $query->get( // get everything
|
||||
['transaction_journals.*', 'transaction_types.type',
|
||||
't_from.amount as from_amount',
|
||||
't_to.amount as to_amount',
|
||||
'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
|
||||
);
|
||||
|
||||
$data->each(
|
||||
function (TransactionJournal $journal) {
|
||||
if (intval($journal->account_encrypted) == 1) {
|
||||
$journal->name = Crypt::decrypt($journal->name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -16,44 +16,33 @@ interface ReportQueryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* See ReportQueryInterface::incomeInPeriodCorrected
|
||||
* See ReportQueryInterface::incomeInPeriod
|
||||
*
|
||||
* This method returns all "expense" journals in a certain period, which are both transfers to a shared account
|
||||
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
|
||||
* not group and returns different fields.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $includeShared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
*/
|
||||
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
|
||||
|
||||
/**
|
||||
* Get a users accounts combined with various meta-data related to the start and end date.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $includeShared
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false);
|
||||
public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
|
||||
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
|
||||
* regards to tags.
|
||||
* regards to tags. It will only get the incomes to the specified accounts.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $includeShared
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
|
||||
public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* Covers tags as well.
|
||||
@@ -65,7 +54,7 @@ interface ReportQueryInterface
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end);
|
||||
public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
|
@@ -38,6 +38,8 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function create($what = 'asset')
|
||||
{
|
||||
|
||||
|
||||
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
|
||||
$subTitle = trans('firefly.make_new_' . $what . '_account');
|
||||
|
||||
@@ -211,13 +213,14 @@ class AccountController extends Controller
|
||||
'name' => $request->input('name'),
|
||||
'accountType' => $request->input('what'),
|
||||
'virtualBalance' => round($request->input('virtualBalance'), 2),
|
||||
'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')),
|
||||
'active' => true,
|
||||
'user' => Auth::user()->id,
|
||||
'iban' => $request->input('iban'),
|
||||
'accountRole' => $request->input('accountRole'),
|
||||
'openingBalance' => round($request->input('openingBalance'), 2),
|
||||
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
|
||||
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
|
||||
|
||||
];
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<?php namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\User;
|
||||
@@ -8,6 +9,7 @@ use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Request as Rq;
|
||||
use Session;
|
||||
@@ -23,6 +25,18 @@ class AuthController extends Controller
|
||||
{
|
||||
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
|
||||
|
||||
/**
|
||||
* Log the user out of the application.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getLogout()
|
||||
{
|
||||
Auth::logout();
|
||||
|
||||
return redirect('/auth/login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application registration form.
|
||||
*
|
||||
@@ -74,7 +88,10 @@ class AuthController extends Controller
|
||||
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
|
||||
if (!is_null($foundUser)) {
|
||||
// if it exists, show message:
|
||||
$code = $foundUser->blocked_code;
|
||||
$code = $foundUser->blocked_code;
|
||||
if (strlen($code) == 0) {
|
||||
$code = 'general_blocked';
|
||||
}
|
||||
$message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]);
|
||||
}
|
||||
|
||||
@@ -143,21 +160,35 @@ class AuthController extends Controller
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
|
||||
$data = $request->all();
|
||||
$data['password'] = bcrypt($data['password']);
|
||||
|
||||
// is user email domain blocked?
|
||||
if ($this->isBlockedDomain($data['email'])) {
|
||||
$validator->getMessageBag()->add('email', trans('validation.invalid_domain'));
|
||||
$this->throwValidationException(
|
||||
$request, $validator
|
||||
);
|
||||
}
|
||||
|
||||
Auth::login($this->create($data));
|
||||
|
||||
// get the email address
|
||||
if (Auth::user() instanceof User) {
|
||||
$email = Auth::user()->email;
|
||||
$address = route('index');
|
||||
$email = Auth::user()->email;
|
||||
$address = route('index');
|
||||
$ipAddress = $request->ip();
|
||||
// send email.
|
||||
Mail::send(
|
||||
['emails.registered-html', 'emails.registered'], ['address' => $address], function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Welcome to Firefly III! ');
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Welcome to Firefly III! ');
|
||||
}
|
||||
);
|
||||
} catch (\Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
);
|
||||
|
||||
// set flash message
|
||||
Session::flash('success', 'You have registered successfully!');
|
||||
@@ -180,6 +211,35 @@ class AuthController extends Controller
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getBlockedDomains()
|
||||
{
|
||||
$set = Config::get('mail.blocked_domains');
|
||||
$domains = [];
|
||||
foreach ($set as $entry) {
|
||||
$domain = trim($entry);
|
||||
if (strlen($domain) > 0) {
|
||||
$domains[] = $domain;
|
||||
}
|
||||
}
|
||||
|
||||
return $domains;
|
||||
}
|
||||
|
||||
protected function isBlockedDomain($email)
|
||||
{
|
||||
$parts = explode('@', $email);
|
||||
$blocked = $this->getBlockedDomains();
|
||||
|
||||
if (isset($parts[1]) && in_array($parts[1], $blocked)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
|
@@ -1,7 +1,11 @@
|
||||
<?php namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
/**
|
||||
* Class PasswordController
|
||||
@@ -41,4 +45,38 @@ class PasswordController extends Controller
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function postEmail(Request $request)
|
||||
{
|
||||
$this->validate($request, ['email' => 'required|email']);
|
||||
|
||||
$user = User::whereEmail($request->get('email'))->first();
|
||||
|
||||
if (!is_null($user) && intval($user->blocked) === 1) {
|
||||
$response = 'passwords.blocked';
|
||||
} else {
|
||||
$response = Password::sendResetLink(
|
||||
$request->only('email'), function (Message $message) {
|
||||
$message->subject($this->getEmailSubject());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
switch ($response) {
|
||||
case Password::RESET_LINK_SENT:
|
||||
return redirect()->back()->with('status', trans($response));
|
||||
|
||||
case Password::INVALID_USER:
|
||||
case 'passwords.blocked':
|
||||
return redirect()->back()->withErrors(['email' => trans($response)]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,8 +6,10 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Input;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
@@ -134,26 +136,31 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(BudgetRepositoryInterface $repository)
|
||||
public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
|
||||
{
|
||||
$budgets = $repository->getActiveBudgets();
|
||||
$inactive = $repository->getInactiveBudgets();
|
||||
$spent = '0';
|
||||
$budgeted = '0';
|
||||
$budgets = $repository->getActiveBudgets();
|
||||
$inactive = $repository->getInactiveBudgets();
|
||||
$spent = '0';
|
||||
$budgeted = '0';
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
|
||||
$period = Navigation::periodShow($start, $range);
|
||||
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
||||
|
||||
bcscale(2);
|
||||
/**
|
||||
* Do some cleanup:
|
||||
*/
|
||||
$repository->cleanupBudgets();
|
||||
|
||||
|
||||
// loop the budgets:
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$budget->spent = $repository->balanceInPeriod($budget, $date, $end);
|
||||
$budget->currentRep = $repository->getCurrentRepetition($budget, $date);
|
||||
$budget->spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
|
||||
$budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end);
|
||||
if ($budget->currentRep) {
|
||||
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
|
||||
}
|
||||
@@ -161,13 +168,12 @@ class BudgetController extends Controller
|
||||
|
||||
}
|
||||
|
||||
$dateAsString = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
|
||||
$budgetIncomeTotal = Preferences::get('budgetIncomeTotal' . $dateAsString, 1000)->data;
|
||||
$budgetMaximum = Preferences::get('budgetMaximum', 1000)->data;
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
|
||||
$budgetMaximum = Preferences::get('budgetMaximum', 1000)->data;
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
|
||||
return view(
|
||||
'budgets.index', compact('budgetMaximum', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
|
||||
'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -178,8 +184,9 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function noBudget(BudgetRepositoryInterface $repository)
|
||||
{
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->startOfMonth());
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$list = $repository->getWithoutBudget($start, $end);
|
||||
$subTitle = trans(
|
||||
'firefly.without_budget_between',
|
||||
@@ -194,9 +201,12 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function postUpdateIncome()
|
||||
{
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
|
||||
Preferences::set('budgetIncomeTotal' . $date, intval(Input::get('amount')));
|
||||
Preferences::set($key, intval(Input::get('amount')));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
@@ -272,7 +282,7 @@ class BudgetController extends Controller
|
||||
{
|
||||
$budgetData = [
|
||||
'name' => $request->input('name'),
|
||||
'active' => intval($request->input('active')) == 1
|
||||
'active' => intval($request->input('active')) == 1,
|
||||
];
|
||||
|
||||
$repository->update($budget, $budgetData);
|
||||
@@ -297,8 +307,11 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function updateIncome()
|
||||
{
|
||||
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
|
||||
$amount = Preferences::get('budgetIncomeTotal' . $date, 1000);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||
$amount = Preferences::get($key, 1000);
|
||||
|
||||
return view('budgets.income', compact('amount'));
|
||||
}
|
||||
|
@@ -5,8 +5,11 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Requests\CategoryFormRequest;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Session;
|
||||
use URL;
|
||||
@@ -139,6 +142,31 @@ class CategoryController extends Controller
|
||||
return view('categories.noCategory', compact('list', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showWithDate(CategoryRepositoryInterface $repository, Category $category, $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $category->name;
|
||||
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page'));
|
||||
|
||||
$set = $repository->getJournalsInRange($category, $page, $start, $end);
|
||||
$count = $repository->countJournalsInRange($category, $start, $end);
|
||||
$journals = new LengthAwarePaginator($set, $count, 50, $page);
|
||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
||||
|
||||
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
@@ -151,12 +179,47 @@ class CategoryController extends Controller
|
||||
$page = intval(Input::get('page'));
|
||||
$set = $repository->getJournals($category, $page);
|
||||
$count = $repository->countJournals($category);
|
||||
$totalSum = $repository->journalsSum($category);
|
||||
$periodSum = $repository->journalsSum($category, Session::get('start'), Session::get('end'));
|
||||
$subTitle = $category->name;
|
||||
$journals = new LengthAwarePaginator($set, $count, 50, $page);
|
||||
$journals->setPath('categories/show/' . $category->id);
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'hideCategory', 'totalSum', 'periodSum'));
|
||||
// list of ranges for list of periods:
|
||||
|
||||
// oldest transaction in category:
|
||||
$start = $repository->getFirstActivityDate($category);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('category-show');
|
||||
$cache->addProperty($category->id);
|
||||
if ($cache->has()) {
|
||||
$entries = $cache->get();
|
||||
} else {
|
||||
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
|
||||
// here do something.
|
||||
$spent = $repository->spentInPeriod($category, $end, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod($category, $end, $currentEnd);
|
||||
$dateStr = $end->format('Y-m-d');
|
||||
$dateName = Navigation::periodShow($end, $range);
|
||||
$entries->push([$dateStr, $dateName, $spent, $earned]);
|
||||
|
||||
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||
|
||||
}
|
||||
$cache->store($entries);
|
||||
}
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -35,45 +35,32 @@ class AccountController extends Controller
|
||||
|
||||
|
||||
/**
|
||||
* Shows the balances for all the user's accounts.
|
||||
* Shows the balances for a given set of dates and accounts.
|
||||
*
|
||||
* TODO fix parameters.
|
||||
*
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @param $year
|
||||
* @param $month
|
||||
* @param bool $shared
|
||||
* @param $url
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function all(AccountRepositoryInterface $repository, $year, $month, $shared = false)
|
||||
public function report($report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$start = new Carbon($year . '-' . $month . '-01');
|
||||
$end = clone $start;
|
||||
$end->endOfMonth();
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('all');
|
||||
$cache->addProperty('accounts');
|
||||
$cache->addProperty('default');
|
||||
$cache->addProperty($accounts);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/** @var Collection $accounts */
|
||||
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
|
||||
if ($shared === false) {
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $index => $account) {
|
||||
if ($account->getMeta('accountRole') == 'sharedAsset') {
|
||||
$accounts->forget($index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make chart:
|
||||
$data = $this->generator->all($accounts, $start, $end);
|
||||
$data = $this->generator->frontpage($accounts, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
@@ -6,6 +6,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -35,13 +36,86 @@ class BudgetController extends Controller
|
||||
$this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
*/
|
||||
public function multiYear(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($report_type);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($budgets);
|
||||
$cache->addProperty('multiYearBudget');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
* budget
|
||||
* year:
|
||||
* spent: x
|
||||
* budgeted: x
|
||||
* year
|
||||
* spent: x
|
||||
* budgeted: x
|
||||
*/
|
||||
$entries = new Collection;
|
||||
// go by budget, not by year.
|
||||
foreach ($budgets as $budget) {
|
||||
$entry = ['name' => '', 'spent' => [], 'budgeted' => []];
|
||||
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart < $end) {
|
||||
// fix the date:
|
||||
$currentEnd = clone $currentStart;
|
||||
$currentEnd->endOfYear();
|
||||
|
||||
// get data:
|
||||
if (is_null($budget->id)) {
|
||||
$name = trans('firefly.noBudget');
|
||||
$sum = $repository->getWithoutBudgetSum($currentStart, $currentEnd);
|
||||
$budgeted = 0;
|
||||
} else {
|
||||
$name = $budget->name;
|
||||
$sum = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
|
||||
$budgeted = $repository->getBudgetLimitRepetitions($budget, $currentStart, $currentEnd)->sum('amount');
|
||||
}
|
||||
|
||||
// save to array:
|
||||
$year = $currentStart->year;
|
||||
$entry['name'] = $name;
|
||||
$entry['spent'][$year] = ($sum * -1);
|
||||
$entry['budgeted'][$year] = $budgeted;
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
}
|
||||
$entries->push($entry);
|
||||
}
|
||||
// generate chart with data:
|
||||
$data = $this->generator->multiYear($entries);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function budget(BudgetRepositoryInterface $repository, Budget $budget)
|
||||
public function budget(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
|
||||
{
|
||||
|
||||
// dates and times
|
||||
@@ -50,7 +124,9 @@ class BudgetController extends Controller
|
||||
$last = Session::get('end', new Carbon);
|
||||
$final = clone $last;
|
||||
$final->addYears(2);
|
||||
$last = Navigation::endOfX($last, $range, $final);
|
||||
$last = Navigation::endOfX($last, $range, $final);
|
||||
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
||||
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
@@ -68,7 +144,7 @@ class BudgetController extends Controller
|
||||
$end->subDay();
|
||||
$chartDate = clone $end;
|
||||
$chartDate->startOfMonth();
|
||||
$spent = $repository->balanceInPeriod($budget, $first, $end);
|
||||
$spent = $repository->balanceInPeriod($budget, $first, $end, $accounts) * -1;
|
||||
$entries->push([$chartDate, $spent]);
|
||||
$first = Navigation::addPeriod($first, $range, 0);
|
||||
}
|
||||
@@ -113,7 +189,7 @@ class BudgetController extends Controller
|
||||
/*
|
||||
* Sum of expenses on this day:
|
||||
*/
|
||||
$sum = $repository->expensesOnDayCorrected($budget, $start);
|
||||
$sum = $repository->expensesOnDay($budget, $start);
|
||||
$amount = bcadd($amount, $sum);
|
||||
$entries->push([clone $start, $amount]);
|
||||
$start->addDay();
|
||||
@@ -133,12 +209,13 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function frontpage(BudgetRepositoryInterface $repository)
|
||||
public function frontpage(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
|
||||
{
|
||||
$budgets = $repository->getBudgets();
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$allEntries = new Collection;
|
||||
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
@@ -156,13 +233,13 @@ class BudgetController extends Controller
|
||||
foreach ($budgets as $budget) {
|
||||
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
|
||||
if ($repetitions->count() == 0) {
|
||||
$expenses = $repository->balanceInPeriod($budget, $start, $end, true);
|
||||
$expenses = $repository->balanceInPeriod($budget, $start, $end, $accounts) * -1;
|
||||
$allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]);
|
||||
continue;
|
||||
}
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true);
|
||||
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $accounts) * -1;
|
||||
// $left can be less than zero.
|
||||
// $overspent can be more than zero ( = overspending)
|
||||
|
||||
@@ -197,11 +274,8 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function year(BudgetRepositoryInterface $repository, $year, $shared = false)
|
||||
public function year(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$allBudgets = $repository->getBudgets();
|
||||
$budgets = new Collection;
|
||||
|
||||
@@ -209,16 +283,17 @@ class BudgetController extends Controller
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($report_type);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('year');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// filter empty budgets:
|
||||
|
||||
// filter empty budgets:
|
||||
foreach ($allBudgets as $budget) {
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
|
||||
if ($spent != 0) {
|
||||
$budgets->push($budget);
|
||||
}
|
||||
@@ -234,14 +309,14 @@ class BudgetController extends Controller
|
||||
|
||||
// each budget, fill the row:
|
||||
foreach ($budgets as $budget) {
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $month, $shared);
|
||||
$spent = $repository->balanceInPeriod($budget, $start, $month, $accounts);
|
||||
$row[] = $spent * -1;
|
||||
}
|
||||
$entries->push($row);
|
||||
$start->endOfMonth()->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->year($allBudgets, $entries);
|
||||
$data = $this->generator->year($budgets, $entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
@@ -65,11 +65,16 @@ class CategoryController extends Controller
|
||||
|
||||
while ($start <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($start, $range);
|
||||
$spent = $repository->balanceInPeriod($category, $start, $currentEnd);
|
||||
$entries->push([clone $start, $spent]);
|
||||
$spent = $repository->spentInPeriod($category, $start, $currentEnd);
|
||||
$earned = $repository->earnedInPeriod($category, $start, $currentEnd);
|
||||
$date = Navigation::periodShow($start, $range);
|
||||
$entries->push([clone $start, $date, $spent, $earned]);
|
||||
$start = Navigation::addPeriod($start, $range, 0);
|
||||
|
||||
}
|
||||
// limit the set to the last 40:
|
||||
$entries = $entries->reverse();
|
||||
$entries = $entries->slice(0, 48);
|
||||
$entries = $entries->reverse();
|
||||
|
||||
$data = $this->generator->all($entries);
|
||||
$cache->store($data);
|
||||
@@ -102,7 +107,7 @@ class CategoryController extends Controller
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$array = $repository->getCategoriesAndExpensesCorrected($start, $end);
|
||||
$array = $repository->getCategoriesAndExpenses($start, $end);
|
||||
// sort by callback:
|
||||
uasort(
|
||||
$array,
|
||||
@@ -111,11 +116,89 @@ class CategoryController extends Controller
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($left['sum'] < $right['sum']) ? 1 : -1;
|
||||
return ($left['sum'] < $right['sum']) ? -1 : 1;
|
||||
}
|
||||
);
|
||||
$set = new Collection($array);
|
||||
$data = $this->generator->frontpage($set);
|
||||
$cache->store($data);
|
||||
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
*/
|
||||
public function multiYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($report_type);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($categories);
|
||||
$cache->addProperty('multiYearCategory');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
* category
|
||||
* year:
|
||||
* spent: x
|
||||
* earned: x
|
||||
* year
|
||||
* spent: x
|
||||
* earned: x
|
||||
*/
|
||||
$entries = new Collection;
|
||||
// go by budget, not by year.
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$entry = ['name' => '', 'spent' => [], 'earned' => []];
|
||||
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart < $end) {
|
||||
// fix the date:
|
||||
$currentEnd = clone $currentStart;
|
||||
$currentEnd->endOfYear();
|
||||
|
||||
// get data:
|
||||
if (is_null($category->id)) {
|
||||
$name = trans('firefly.noCategory');
|
||||
$spent = $repository->spentNoCategoryForAccounts($accounts, $currentStart, $currentEnd);
|
||||
$earned = $repository->earnedNoCategoryForAccounts($accounts, $currentStart, $currentEnd);
|
||||
} else {
|
||||
$name = $category->name;
|
||||
$spent = $repository->spentInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd);
|
||||
$earned = $repository->earnedInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd);
|
||||
}
|
||||
|
||||
// save to array:
|
||||
$year = $currentStart->year;
|
||||
$entry['name'] = $name;
|
||||
$entry['spent'][$year] = ($spent * -1);
|
||||
$entry['earned'][$year] = $earned;
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
}
|
||||
$entries->push($entry);
|
||||
}
|
||||
// generate chart with data:
|
||||
$data = $this->generator->multiYear($entries);
|
||||
$cache->store($data);
|
||||
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
@@ -127,7 +210,7 @@ class CategoryController extends Controller
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function month(CategoryRepositoryInterface $repository, Category $category)
|
||||
public function currentPeriod(CategoryRepositoryInterface $repository, Category $category)
|
||||
{
|
||||
$start = clone Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
@@ -138,7 +221,7 @@ class CategoryController extends Controller
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('month');
|
||||
$cache->addProperty('currentPeriod');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
@@ -146,13 +229,57 @@ class CategoryController extends Controller
|
||||
|
||||
|
||||
while ($start <= $end) {
|
||||
$spent = $repository->spentOnDaySumCorrected($category, $start);
|
||||
|
||||
$entries->push([clone $start, $spent]);
|
||||
$spent = $repository->spentOnDaySum($category, $start);
|
||||
$earned = $repository->earnedOnDaySum($category, $start);
|
||||
$date = Navigation::periodShow($start, '1D');
|
||||
$entries->push([clone $start, $date, $spent, $earned]);
|
||||
$start->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->month($entries);
|
||||
$data = $this->generator->period($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('specificPeriod');
|
||||
$cache->addProperty($date);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$entries = new Collection;
|
||||
|
||||
|
||||
while ($start <= $end) {
|
||||
$spent = $repository->spentOnDaySum($category, $start);
|
||||
$earned = $repository->earnedOnDaySum($category, $start);
|
||||
$theDate = Navigation::periodShow($start, '1D');
|
||||
$entries->push([clone $start, $theDate, $spent, $earned]);
|
||||
$start->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->period($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -164,31 +291,32 @@ class CategoryController extends Controller
|
||||
* This chart will only show expenses.
|
||||
*
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function spentInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
|
||||
public function spentInYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
|
||||
$cache = new CacheProperties; // chart properties for cache:
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($report_type);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('spent-in-year');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$allCategories = $repository->getCategories();
|
||||
$entries = new Collection;
|
||||
$categories = $allCategories->filter(
|
||||
function (Category $category) use ($repository, $start, $end, $shared) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
|
||||
function (Category $category) use ($repository, $start, $end, $accounts) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $accounts);
|
||||
if ($spent < 0) {
|
||||
return $category;
|
||||
}
|
||||
@@ -203,7 +331,7 @@ class CategoryController extends Controller
|
||||
$row = [clone $start]; // make a row:
|
||||
|
||||
foreach ($categories as $category) { // each budget, fill the row
|
||||
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
|
||||
$spent = $repository->balanceInPeriod($category, $start, $month, $accounts);
|
||||
if ($spent < 0) {
|
||||
$row[] = $spent * -1;
|
||||
} else {
|
||||
@@ -220,61 +348,194 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* This chart will only show income.
|
||||
* Returns a chart of what has been earned in this period in each category
|
||||
* grouped by month.
|
||||
*
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function earnedInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
|
||||
public function earnedInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
|
||||
$cache = new CacheProperties; // chart properties for cache:
|
||||
$original = clone $start;
|
||||
$cache = new CacheProperties; // chart properties for cache:
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($report_type);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('earned-in-year');
|
||||
$cache->addProperty('earned-in-period');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$categories = new Collection;
|
||||
$sets = new Collection;
|
||||
$entries = new Collection;
|
||||
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$allCategories = $repository->getCategories();
|
||||
$allEntries = new Collection;
|
||||
$categories = $allCategories->filter(
|
||||
function (Category $category) use ($repository, $start, $end, $shared) {
|
||||
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
|
||||
if ($spent > 0) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
return null;
|
||||
// run a very special query each month:
|
||||
$start = clone $original;
|
||||
while ($start < $end) {
|
||||
$currentEnd = clone $start;
|
||||
$currentStart = clone $start;
|
||||
$currentStart->startOfMonth();
|
||||
$currentEnd->endOfMonth();
|
||||
// get a list of categories, and what the user has earned for that category
|
||||
// (if the user has earned anything)
|
||||
$set = $repository->earnedForAccounts($accounts, $currentStart, $currentEnd);
|
||||
$categories = $categories->merge($set);
|
||||
// save the set combined with the data that is in it:
|
||||
// for example:
|
||||
// [december 2015, salary:1000, bonus:200]
|
||||
$sets->push([$currentStart, $set]);
|
||||
$start->addMonth();
|
||||
}
|
||||
// filter categories into a single bunch. Useful later on.
|
||||
// $categories contains all the categories the user has earned money
|
||||
// in in this period.
|
||||
$categories = $categories->unique('id');
|
||||
$categories = $categories->sortBy(
|
||||
function (Category $category) {
|
||||
return $category->name;
|
||||
}
|
||||
);
|
||||
|
||||
// start looping the time again, this time processing the
|
||||
// data for each month.
|
||||
$start = clone $original;
|
||||
while ($start < $end) {
|
||||
$month = clone $start; // month is the current end of the period
|
||||
$month->endOfMonth();
|
||||
$row = [clone $start]; // make a row:
|
||||
$currentEnd = clone $start;
|
||||
$currentStart = clone $start;
|
||||
$currentStart->startOfMonth();
|
||||
$currentEnd->endOfMonth();
|
||||
|
||||
foreach ($categories as $category) { // each budget, fill the row
|
||||
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
|
||||
if ($spent > 0) {
|
||||
$row[] = $spent;
|
||||
// in $sets we have saved all the sets of data for each month
|
||||
// so now we need to retrieve the corrent one.
|
||||
// match is on date of course.
|
||||
$currentSet = $sets->first(
|
||||
function ($key, $value) use ($currentStart) {
|
||||
// set for this date.
|
||||
return ($value[0] == $currentStart);
|
||||
}
|
||||
);
|
||||
// create a row used later on.
|
||||
$row = [clone $currentStart];
|
||||
|
||||
// loop all categories:
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
// if entry is not null, we've earned money in this period for this category.
|
||||
$entry = $currentSet[1]->first(
|
||||
function ($key, $value) use ($category) {
|
||||
return $value->id == $category->id;
|
||||
}
|
||||
);
|
||||
// save amount
|
||||
if (!is_null($entry)) {
|
||||
$row[] = $entry->earned;
|
||||
} else {
|
||||
$row[] = 0;
|
||||
}
|
||||
}
|
||||
$allEntries->push($row);
|
||||
$entries->push($row);
|
||||
$start->addMonth();
|
||||
}
|
||||
$data = $this->generator->earnedInYear($categories, $allEntries);
|
||||
|
||||
$data = $this->generator->earnedInPeriod($categories, $entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a chart of what has been spent in this period in each category
|
||||
* grouped by month.
|
||||
*
|
||||
* @param CategoryRepositoryInterface $repository
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function spentInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$original = clone $start;
|
||||
$cache = new CacheProperties; // chart properties for cache:
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($report_type);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty('category');
|
||||
$cache->addProperty('spent-in-period');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$categories = new Collection;
|
||||
$sets = new Collection;
|
||||
$entries = new Collection;
|
||||
|
||||
// run a very special query each month:
|
||||
$start = clone $original;
|
||||
while ($start < $end) {
|
||||
$currentEnd = clone $start;
|
||||
$currentStart = clone $start;
|
||||
$currentStart->startOfMonth();
|
||||
$currentEnd->endOfMonth();
|
||||
$set = $repository->spentForAccounts($accounts, $currentStart, $currentEnd);
|
||||
$categories = $categories->merge($set);
|
||||
$sets->push([$currentStart, $set]);
|
||||
$start->addMonth();
|
||||
}
|
||||
$categories = $categories->unique('id');
|
||||
$categories = $categories->sortBy(
|
||||
function (Category $category) {
|
||||
return $category->name;
|
||||
}
|
||||
);
|
||||
|
||||
$start = clone $original;
|
||||
while ($start < $end) {
|
||||
$currentEnd = clone $start;
|
||||
$currentStart = clone $start;
|
||||
$currentStart->startOfMonth();
|
||||
$currentEnd->endOfMonth();
|
||||
$currentSet = $sets->first(
|
||||
function ($key, $value) use ($currentStart) {
|
||||
// set for this date.
|
||||
return ($value[0] == $currentStart);
|
||||
}
|
||||
);
|
||||
$row = [clone $currentStart];
|
||||
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
/** @var Category $entry */
|
||||
$entry = $currentSet[1]->first(
|
||||
function ($key, $value) use ($category) {
|
||||
return $value->id == $category->id;
|
||||
}
|
||||
);
|
||||
if (!is_null($entry)) {
|
||||
$row[] = $entry->spent;
|
||||
} else {
|
||||
$row[] = 0;
|
||||
}
|
||||
}
|
||||
$entries->push($row);
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
$data = $this->generator->spentInPeriod($categories, $entries);
|
||||
$cache->store($data);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -36,41 +36,66 @@ class ReportController extends Controller
|
||||
* Summarizes all income and expenses, per month, for a given year.
|
||||
*
|
||||
* @param ReportQueryInterface $query
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function yearInOut(ReportQueryInterface $query, $year, $shared = false)
|
||||
public function yearInOut(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// get start and end of year
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOut');
|
||||
$cache->addProperty($year);
|
||||
$cache->addProperty($shared);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
while ($start < $end) {
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// total income and total expenses:
|
||||
$incomeSum = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount');
|
||||
$expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount');
|
||||
|
||||
$entries->push([clone $start, $incomeSum, $expenseSum]);
|
||||
$start->addMonth();
|
||||
// per year?
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
|
||||
$entries = new Collection;
|
||||
while ($start < $end) {
|
||||
$startOfYear = clone $start;
|
||||
$startOfYear->startOfYear();
|
||||
$endOfYear = clone $startOfYear;
|
||||
$endOfYear->endOfYear();
|
||||
|
||||
// total income and total expenses:
|
||||
$incomeSum = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
|
||||
$expenseSum = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
|
||||
|
||||
$entries->push([clone $start, $incomeSum, $expenseSum]);
|
||||
$start->addYear();
|
||||
}
|
||||
|
||||
$data = $this->generator->multiYearInOut($entries);
|
||||
$cache->store($data);
|
||||
} else {
|
||||
// per month:
|
||||
|
||||
$entries = new Collection;
|
||||
while ($start < $end) {
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// total income and total expenses:
|
||||
$incomeSum = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive');
|
||||
$expenseSum = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive');
|
||||
|
||||
|
||||
$entries->push([clone $start, $incomeSum, $expenseSum]);
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
$data = $this->generator->yearInOut($entries);
|
||||
$cache->store($data);
|
||||
}
|
||||
|
||||
$data = $this->generator->yearInOut($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
@@ -80,44 +105,71 @@ class ReportController extends Controller
|
||||
* Summarizes all income and expenses for a given year. Gives a total and an average.
|
||||
*
|
||||
* @param ReportQueryInterface $query
|
||||
* @param $year
|
||||
* @param bool $shared
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function yearInOutSummarized(ReportQueryInterface $query, $year, $shared = false)
|
||||
public function yearInOutSummarized(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOutSummarized');
|
||||
$cache->addProperty($year);
|
||||
$cache->addProperty($shared);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$start = new Carbon($year . '-01-01');
|
||||
$end = new Carbon($year . '-12-31');
|
||||
$shared = $shared == 'shared' ? true : false;
|
||||
$income = '0';
|
||||
$expense = '0';
|
||||
$count = 0;
|
||||
|
||||
bcscale(2);
|
||||
|
||||
while ($start < $end) {
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// total income and total expenses:
|
||||
$income = bcadd($income, $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
|
||||
$expense = bcadd($expense, $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
|
||||
$count++;
|
||||
$start->addMonth();
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// per year
|
||||
while ($start < $end) {
|
||||
$startOfYear = clone $start;
|
||||
$startOfYear->startOfYear();
|
||||
$endOfYear = clone $startOfYear;
|
||||
$endOfYear->endOfYear();
|
||||
|
||||
// total income and total expenses:
|
||||
$currentIncome = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
|
||||
$currentExpense = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
|
||||
$income = bcadd($income, $currentIncome);
|
||||
$expense = bcadd($expense, $currentExpense);
|
||||
|
||||
$count++;
|
||||
$start->addYear();
|
||||
}
|
||||
|
||||
$data = $this->generator->multiYearInOutSummarized($income, $expense, $count);
|
||||
$cache->store($data);
|
||||
} else {
|
||||
// per month!
|
||||
while ($start < $end) {
|
||||
$month = clone $start;
|
||||
$month->endOfMonth();
|
||||
// total income and total expenses:
|
||||
$currentIncome = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive');
|
||||
$currentExpense = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive');
|
||||
$income = bcadd($income, $currentIncome);
|
||||
$expense = bcadd($expense, $currentExpense);
|
||||
|
||||
$count++;
|
||||
$start->addMonth();
|
||||
}
|
||||
|
||||
$data = $this->generator->yearInOutSummarized($income, $expense, $count);
|
||||
$cache->store($data);
|
||||
}
|
||||
|
||||
$data = $this->generator->yearInOutSummarized($income, $expense, $count);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
|
@@ -34,14 +34,15 @@ abstract class Controller extends BaseController
|
||||
View::share('hideTags', false);
|
||||
|
||||
if (Auth::check()) {
|
||||
$pref = Preferences::get('language', 'en');
|
||||
$pref = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'));
|
||||
$lang = $pref->data;
|
||||
$this->monthFormat = Config::get('firefly.month.' . $lang);
|
||||
$this->monthAndDayFormat = Config::get('firefly.monthAndDay.' . $lang);
|
||||
$this->monthFormat = trans('config.month');
|
||||
$this->monthAndDayFormat = trans('config.month_and_day');
|
||||
|
||||
View::share('monthFormat', $this->monthFormat);
|
||||
View::share('monthAndDayFormat', $this->monthAndDayFormat);
|
||||
View::share('language', $lang);
|
||||
View::share('localeconv', localeconv());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -147,7 +147,7 @@ class CurrencyController extends Controller
|
||||
public function index(CurrencyRepositoryInterface $repository)
|
||||
{
|
||||
$currencies = $repository->get();
|
||||
$defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', 'EUR'));
|
||||
$defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')));
|
||||
|
||||
|
||||
if (!Auth::user()->hasRole('owner')) {
|
||||
|
@@ -120,33 +120,4 @@ class HomeController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @return \Illuminate\Http\RedirectResponse|string
|
||||
*/
|
||||
public function routes()
|
||||
{
|
||||
if (!Auth::user()->hasRole('owner')) {
|
||||
Session::flash('warning', 'This page is broken.');
|
||||
|
||||
return redirect(route('index'));
|
||||
}
|
||||
Log::debug('Make log.');
|
||||
|
||||
// get all routes:
|
||||
$routeCollection = Route::getRoutes();
|
||||
/** @var \Illuminate\Routing\Route $value */
|
||||
foreach ($routeCollection as $value) {
|
||||
$name = $value->getName();
|
||||
$methods = $value->getMethods();
|
||||
$isPost = in_array('POST', $methods);
|
||||
$index = str_replace('.', '-', $name);
|
||||
|
||||
if (strlen($name) > 0 && !$isPost) {
|
||||
echo "'" . $index . "' => '" . $name . "',<br />";
|
||||
}
|
||||
}
|
||||
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,6 @@ use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class JsonController
|
||||
@@ -72,9 +71,8 @@ class JsonController extends Controller
|
||||
*/
|
||||
public function boxBillsPaid(BillRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
|
||||
{
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
$amount = 0;
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
bcscale(2);
|
||||
|
||||
// works for json too!
|
||||
@@ -85,19 +83,14 @@ class JsonController extends Controller
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$bills = $repository->getActiveBills(); // these two functions are the same as the chart
|
||||
// get amount from bills
|
||||
$amount = $repository->billsPaidInRange($start, $end)->sum('paid');
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$amount = bcadd($amount, $repository->billPaymentsInRange($bill, $start, $end));
|
||||
}
|
||||
unset($bill, $bills);
|
||||
|
||||
$creditCards = $accountRepository->getCreditCards(); // Find credit card accounts and possibly unpaid credit card bills.
|
||||
// add credit card bill.
|
||||
$creditCards = $accountRepository->getCreditCards($end); // Find credit card accounts and possibly unpaid credit card bills.
|
||||
/** @var Account $creditCard */
|
||||
foreach ($creditCards as $creditCard) {
|
||||
$balance = Steam::balance($creditCard, $end, true); // if the balance is not zero, the monthly payment is still underway.
|
||||
if ($balance == 0) {
|
||||
if ($creditCard->balance == 0) {
|
||||
// find a transfer TO the credit card which should account for
|
||||
// anything paid. If not, the CC is not yet used.
|
||||
$amount = bcadd($amount, $accountRepository->getTransfersInRange($creditCard, $start, $end)->sum('amount'));
|
||||
@@ -147,14 +140,15 @@ class JsonController extends Controller
|
||||
}
|
||||
unset($bill, $bills, $range, $ranges);
|
||||
|
||||
$creditCards = $accountRepository->getCreditCards();
|
||||
$creditCards = $accountRepository->getCreditCards($end);
|
||||
|
||||
/** @var Account $creditCard */
|
||||
foreach ($creditCards as $creditCard) {
|
||||
$balance = Steam::balance($creditCard, $end, true);
|
||||
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
|
||||
if ($balance < 0) {
|
||||
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
|
||||
if ($creditCard->balance < 0) {
|
||||
// unpaid! create a fake bill that matches the amount.
|
||||
$description = $creditCard->name;
|
||||
$fakeAmount = $balance * -1;
|
||||
$fakeAmount = $creditCard->balance * -1;
|
||||
$fakeBill = $repository->createFakeBill($description, $date, $fakeAmount);
|
||||
$unpaid->push([$fakeBill, $date]);
|
||||
}
|
||||
@@ -176,7 +170,7 @@ class JsonController extends Controller
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function boxIn(ReportQueryInterface $reportQuery)
|
||||
public function boxIn(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository)
|
||||
{
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
@@ -189,8 +183,8 @@ class JsonController extends Controller
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$amount = $reportQuery->incomeInPeriodCorrected($start, $end, true)->sum('amount');
|
||||
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
||||
$amount = $reportQuery->incomeInPeriod($start, $end, $accounts)->sum('to_amount');
|
||||
|
||||
$data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||
$cache->store($data);
|
||||
@@ -203,11 +197,12 @@ class JsonController extends Controller
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function boxOut(ReportQueryInterface $reportQuery)
|
||||
public function boxOut(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository)
|
||||
{
|
||||
$start = Session::get('start', Carbon::now()->startOfMonth());
|
||||
$end = Session::get('end', Carbon::now()->endOfMonth());
|
||||
|
||||
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
||||
|
||||
// works for json too!
|
||||
$cache = new CacheProperties;
|
||||
@@ -218,7 +213,7 @@ class JsonController extends Controller
|
||||
return Response::json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$amount = $reportQuery->expenseInPeriodCorrected($start, $end, true)->sum('amount');
|
||||
$amount = $reportQuery->expenseInPeriod($start, $end, $accounts)->sum('to_amount');
|
||||
|
||||
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
|
||||
$cache->store($data);
|
||||
@@ -240,7 +235,6 @@ class JsonController extends Controller
|
||||
foreach ($list as $entry) {
|
||||
$return[] = $entry->name;
|
||||
}
|
||||
sort($return);
|
||||
|
||||
return Response::json($return);
|
||||
}
|
||||
|
@@ -49,7 +49,6 @@ class NewUserController extends Controller
|
||||
*/
|
||||
public function submit(NewUserFormRequest $request, AccountRepositoryInterface $repository)
|
||||
{
|
||||
|
||||
// create normal asset account:
|
||||
$assetAccount = [
|
||||
'name' => $request->get('bank_name'),
|
||||
@@ -61,7 +60,7 @@ class NewUserController extends Controller
|
||||
'accountRole' => 'defaultAsset',
|
||||
'openingBalance' => round($request->input('bank_balance'), 2),
|
||||
'openingBalanceDate' => new Carbon,
|
||||
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
|
||||
];
|
||||
|
||||
$repository->store($assetAccount);
|
||||
@@ -78,7 +77,7 @@ class NewUserController extends Controller
|
||||
'accountRole' => 'savingAsset',
|
||||
'openingBalance' => round($request->input('savings_balance'), 2),
|
||||
'openingBalanceDate' => new Carbon,
|
||||
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
|
||||
];
|
||||
$repository->store($savingsAccount);
|
||||
}
|
||||
@@ -96,7 +95,7 @@ class NewUserController extends Controller
|
||||
'accountRole' => 'ccAsset',
|
||||
'openingBalance' => null,
|
||||
'openingBalanceDate' => null,
|
||||
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_credit_card_limit')),
|
||||
];
|
||||
$creditCard = $repository->store($creditAccount);
|
||||
|
||||
|
@@ -37,10 +37,12 @@ class PreferencesController extends Controller
|
||||
$viewRange = $viewRangePref->data;
|
||||
$frontPageAccounts = Preferences::get('frontPageAccounts', []);
|
||||
$budgetMax = Preferences::get('budgetMaximum', 1000);
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
|
||||
$budgetMaximum = $budgetMax->data;
|
||||
|
||||
return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange'));
|
||||
$showIncomplete = env('SHOW_INCOMPLETE_TRANSLATIONS', 'false') == 'true';
|
||||
|
||||
return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange', 'showIncomplete'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +72,7 @@ class PreferencesController extends Controller
|
||||
|
||||
// language:
|
||||
$lang = Input::get('language');
|
||||
if (in_array($lang, array_keys(Config::get('firefly.lang')))) {
|
||||
if (in_array($lang, array_keys(Config::get('firefly.languages')))) {
|
||||
Preferences::set('language', $lang);
|
||||
}
|
||||
|
||||
|
@@ -66,7 +66,7 @@ class ProfileController extends Controller
|
||||
}
|
||||
|
||||
// update the user with the new password.
|
||||
Auth::user()->password = $request->get('new_password');
|
||||
Auth::user()->password = bcrypt($request->get('new_password'));
|
||||
Auth::user()->save();
|
||||
|
||||
Session::flash('success', trans('firefly.password_changed'));
|
||||
|
@@ -4,6 +4,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
@@ -41,111 +42,190 @@ class ReportController extends Controller
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository)
|
||||
{
|
||||
$start = Session::get('first');
|
||||
$months = $this->helper->listOfMonths($start);
|
||||
$start = Session::get('first');
|
||||
$months = $this->helper->listOfMonths($start);
|
||||
$startOfMonth = clone Session::get('start');
|
||||
$endOfMonth = clone Session::get('start');
|
||||
$startOfYear = clone Session::get('start');
|
||||
$endOfYear = clone Session::get('start');
|
||||
$startOfMonth->startOfMonth();
|
||||
$endOfMonth->endOfMonth();
|
||||
$startOfYear->startOfYear();
|
||||
$endOfYear->endOfYear();
|
||||
|
||||
// does the user have shared accounts?
|
||||
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
|
||||
$hasShared = false;
|
||||
|
||||
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
|
||||
// get id's for quick links:
|
||||
$accountIds = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->getMeta('accountRole') == 'sharedAsset') {
|
||||
$hasShared = true;
|
||||
}
|
||||
$accountIds [] = $account->id;
|
||||
}
|
||||
$accountList = join(',', $accountIds);
|
||||
|
||||
|
||||
return view('reports.index', compact('months', 'hasShared'));
|
||||
return view('reports.index', compact('months', 'accounts', 'start', 'accountList',
|
||||
'startOfMonth', 'endOfMonth', 'startOfYear', 'endOfYear'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @param string $month
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
* @return View
|
||||
*/
|
||||
public function month($year = '2014', $month = '1', $shared = false)
|
||||
public function defaultYear($report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$start = new Carbon($year . '-' . $month . '-01');
|
||||
$subTitle = trans('firefly.reportForMonth', ['month' => $start->formatLocalized($this->monthFormat)]);
|
||||
$subTitleIcon = 'fa-calendar';
|
||||
$end = clone $start;
|
||||
$incomeTopLength = 8;
|
||||
$expenseTopLength = 8;
|
||||
if ($shared == 'shared') {
|
||||
$shared = true;
|
||||
$subTitle = trans('firefly.reportForMonthShared', ['month' => $start->formatLocalized($this->monthFormat)]);
|
||||
}
|
||||
|
||||
$end->endOfMonth();
|
||||
|
||||
$accounts = $this->helper->getAccountReport($start, $end, $shared);
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $shared);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
|
||||
$budgets = $this->helper->getBudgetReport($start, $end, $shared);
|
||||
$categories = $this->helper->getCategoryReport($start, $end, $shared);
|
||||
$balance = $this->helper->getBalanceReport($start, $end, $shared);
|
||||
$bills = $this->helper->getBillReport($start, $end);
|
||||
|
||||
Session::flash('gaEventCategory', 'report');
|
||||
Session::flash('gaEventAction', 'month');
|
||||
Session::flash('gaEventLabel', $start->format('F Y'));
|
||||
|
||||
|
||||
return view(
|
||||
'reports.month',
|
||||
compact(
|
||||
'start', 'shared',
|
||||
'subTitle', 'subTitleIcon',
|
||||
'accounts',
|
||||
'incomes', 'incomeTopLength',
|
||||
'expenses', 'expenseTopLength',
|
||||
'budgets', 'balance',
|
||||
'categories',
|
||||
'bills'
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $year
|
||||
*
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function year($year, $shared = false)
|
||||
{
|
||||
$start = new Carbon('01-01-' . $year);
|
||||
$end = clone $start;
|
||||
$subTitle = trans('firefly.reportForYear', ['year' => $year]);
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$incomeTopLength = 8;
|
||||
$expenseTopLength = 8;
|
||||
|
||||
if ($shared == 'shared') {
|
||||
$shared = true;
|
||||
$subTitle = trans('firefly.reportForYearShared', ['year' => $year]);
|
||||
}
|
||||
$end->endOfYear();
|
||||
|
||||
$accounts = $this->helper->getAccountReport($start, $end, $shared);
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $shared);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
|
||||
$accountReport = $this->helper->getAccountReport($start, $end, $accounts);
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
|
||||
|
||||
Session::flash('gaEventCategory', 'report');
|
||||
Session::flash('gaEventAction', 'year');
|
||||
Session::flash('gaEventLabel', $start->format('Y'));
|
||||
|
||||
// and some id's, joined:
|
||||
$accountIds = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
$accountIds = join(',', $accountIds);
|
||||
|
||||
return view(
|
||||
'reports.year',
|
||||
compact('start', 'shared', 'accounts', 'incomes', 'expenses', 'subTitle', 'subTitleIcon', 'incomeTopLength', 'expenseTopLength')
|
||||
'reports.default.year',
|
||||
compact(
|
||||
'start', 'accountReport', 'incomes', 'report_type', 'accountIds', 'end',
|
||||
'expenses', 'incomeTopLength', 'expenseTopLength'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function defaultMonth($report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$incomeTopLength = 8;
|
||||
$expenseTopLength = 8;
|
||||
|
||||
// get report stuff!
|
||||
$accountReport = $this->helper->getAccountReport($start, $end, $accounts);
|
||||
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
|
||||
$budgets = $this->helper->getBudgetReport($start, $end, $accounts);
|
||||
$categories = $this->helper->getCategoryReport($start, $end, $accounts);
|
||||
$balance = $this->helper->getBalanceReport($start, $end, $accounts);
|
||||
$bills = $this->helper->getBillReport($start, $end, $accounts);
|
||||
|
||||
// and some id's, joined:
|
||||
$accountIds = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
$accountIds = join(',', $accountIds);
|
||||
|
||||
// continue!
|
||||
return view(
|
||||
'reports.default.month',
|
||||
compact(
|
||||
'start', 'end', 'report_type',
|
||||
'accountReport',
|
||||
'incomes', 'incomeTopLength',
|
||||
'expenses', 'expenseTopLength',
|
||||
'budgets', 'balance',
|
||||
'categories',
|
||||
'bills',
|
||||
'accountIds', 'report_type'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function defaultMultiYear($report_type, $start, $end, $accounts)
|
||||
{
|
||||
|
||||
|
||||
// list of users stuff:
|
||||
$budgets = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface')->getActiveBudgets();
|
||||
$categories = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface')->getCategories();
|
||||
|
||||
// and some id's, joined:
|
||||
$accountIds = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
$accountIds = join(',', $accountIds);
|
||||
|
||||
return view(
|
||||
'reports.default.multi-year', compact('budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'report_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $report_type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function report($report_type, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// throw an error if necessary.
|
||||
if ($end < $start) {
|
||||
return view('error')->with('message', 'End date cannot be before start date, silly!');
|
||||
}
|
||||
|
||||
// lower threshold
|
||||
if ($start < Session::get('first')) {
|
||||
$start = Session::get('first');
|
||||
}
|
||||
|
||||
switch ($report_type) {
|
||||
default:
|
||||
case 'default':
|
||||
|
||||
View::share(
|
||||
'subTitle', trans(
|
||||
'firefly.report_default',
|
||||
[
|
||||
'start' => $start->formatLocalized($this->monthFormat),
|
||||
'end' => $end->formatLocalized($this->monthFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
View::share('subTitleIcon', 'fa-calendar');
|
||||
|
||||
// more than one year date difference means year report.
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// return view('error')->with('message', 'No report yet for this time period.');
|
||||
return $this->defaultMultiYear($report_type, $start, $end, $accounts);
|
||||
}
|
||||
// more than two months date difference means year report.
|
||||
if ($start->diffInMonths($end) > 1) {
|
||||
return $this->defaultYear($report_type, $start, $end, $accounts);
|
||||
}
|
||||
|
||||
return $this->defaultMonth($report_type, $start, $end, $accounts);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ use FireflyIII\Http\Requests\JournalFormRequest;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Input;
|
||||
@@ -45,8 +46,9 @@ class TransactionController extends Controller
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function create(AccountRepositoryInterface $repository, $what = 'deposit')
|
||||
public function create(AccountRepositoryInterface $repository, $what = TransactionType::DEPOSIT)
|
||||
{
|
||||
$what = strtolower($what);
|
||||
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||
$uploadSize = min($maxFileSize, $maxPostSize);
|
||||
@@ -95,7 +97,7 @@ class TransactionController extends Controller
|
||||
*/
|
||||
public function delete(TransactionJournal $journal)
|
||||
{
|
||||
$what = strtolower($journal->transactionType->type);
|
||||
$what = strtolower($journal->getTransactionType());
|
||||
$subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]);
|
||||
|
||||
// put previous url in session
|
||||
@@ -137,7 +139,7 @@ class TransactionController extends Controller
|
||||
public function edit(AccountRepositoryInterface $repository, TransactionJournal $journal)
|
||||
{
|
||||
// cannot edit opening balance
|
||||
if ($journal->transactionType->type == 'Opening balance') {
|
||||
if ($journal->isOpeningBalance()) {
|
||||
return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
|
||||
}
|
||||
|
||||
@@ -145,7 +147,7 @@ class TransactionController extends Controller
|
||||
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||
$uploadSize = min($maxFileSize, $maxPostSize);
|
||||
$what = strtolower($journal->transactionType->type);
|
||||
$what = strtolower($journal->getTransactionType());
|
||||
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
|
||||
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
|
||||
$budgets[0] = trans('form.noBudget');
|
||||
@@ -179,9 +181,9 @@ class TransactionController extends Controller
|
||||
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id;
|
||||
}
|
||||
|
||||
$preFilled['amount'] = $journal->actual_amount;
|
||||
$preFilled['amount'] = $journal->amount_positive;
|
||||
|
||||
if ($journal->transactionType->type == 'Withdrawal') {
|
||||
if ($journal->isWithdrawal()) {
|
||||
$preFilled['account_id'] = $journal->source_account->id;
|
||||
$preFilled['expense_account'] = $journal->destination_account->name_for_editform;
|
||||
} else {
|
||||
@@ -269,8 +271,8 @@ class TransactionController extends Controller
|
||||
$t->after = bcadd($t->before, $t->amount);
|
||||
}
|
||||
);
|
||||
$what = strtolower($journal->transactionType->type);
|
||||
$subTitle = trans('firefly.' . $journal->transactionType->type) . ' "' . e($journal->description) . '"';
|
||||
$what = strtolower($journal->getTransactionType());
|
||||
$subTitle = trans('firefly.' . $journal->getTransactionType()) . ' "' . e($journal->description) . '"';
|
||||
|
||||
return view('transactions.show', compact('journal', 'subTitle', 'what'));
|
||||
}
|
||||
@@ -287,7 +289,7 @@ class TransactionController extends Controller
|
||||
$journalData = $request->getJournalData();
|
||||
|
||||
// if not withdrawal, unset budgetid.
|
||||
if ($journalData['what'] != 'withdrawal') {
|
||||
if ($journalData['what'] != strtolower(TransactionType::WITHDRAWAL)) {
|
||||
$journalData['budget_id'] = 0;
|
||||
}
|
||||
|
||||
@@ -308,7 +310,7 @@ class TransactionController extends Controller
|
||||
// rescan journal, UpdateJournalConnection
|
||||
event(new JournalSaved($journal));
|
||||
|
||||
if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
|
||||
if ($journal->isTransfer() && intval($request->get('piggy_bank_id')) > 0) {
|
||||
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
|
||||
}
|
||||
|
||||
@@ -340,7 +342,7 @@ class TransactionController extends Controller
|
||||
{
|
||||
|
||||
// cannot edit opening balance
|
||||
if ($journal->transactionType->type == 'Opening balance') {
|
||||
if ($journal->isOpeningBalance()) {
|
||||
return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<?php namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use App;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Closure;
|
||||
use Config;
|
||||
@@ -54,15 +55,20 @@ class Authenticate
|
||||
}
|
||||
|
||||
if (intval($this->auth->user()->blocked) == 1) {
|
||||
return redirect()->route('logout');
|
||||
Auth::logout();
|
||||
|
||||
return redirect()->route('index');
|
||||
}
|
||||
|
||||
// if logged in, set user language:
|
||||
$pref = Preferences::get('language', 'en');
|
||||
$pref = Preferences::get('language', env('DEFAULT_LANGUAGE','en_US'));
|
||||
App::setLocale($pref->data);
|
||||
Carbon::setLocale($pref->data);
|
||||
Carbon::setLocale(substr($pref->data,0,2));
|
||||
$locale = explode(',', trans('config.locale'));
|
||||
$locale = array_map('trim', $locale);
|
||||
|
||||
setlocale(LC_TIME, Config::get('firefly.locales.' . $pref->data));
|
||||
setlocale(LC_TIME, $locale);
|
||||
setlocale(LC_MONETARY, $locale);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
@@ -41,18 +41,19 @@ class AccountFormRequest extends Request
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $idRule,
|
||||
'name' => $nameRule,
|
||||
'openingBalance' => 'numeric',
|
||||
'iban' => 'iban',
|
||||
'virtualBalance' => 'numeric',
|
||||
'openingBalanceDate' => 'date',
|
||||
'accountRole' => 'in:' . $accountRoles,
|
||||
'active' => 'boolean',
|
||||
'ccType' => 'in:' . $ccPaymentTypes,
|
||||
'ccMonthlyPaymentDate' => 'date',
|
||||
'balance_currency_id' => 'exists:transaction_currencies,id',
|
||||
'what' => 'in:' . $types
|
||||
'id' => $idRule,
|
||||
'name' => $nameRule,
|
||||
'openingBalance' => 'numeric',
|
||||
'iban' => 'iban',
|
||||
'virtualBalance' => 'numeric',
|
||||
'openingBalanceDate' => 'date',
|
||||
'accountRole' => 'in:' . $accountRoles,
|
||||
'active' => 'boolean',
|
||||
'ccType' => 'in:' . $ccPaymentTypes,
|
||||
'ccMonthlyPaymentDate' => 'date',
|
||||
'amount_currency_id_openingBalance' => 'exists:transaction_currencies,id',
|
||||
'amount_currency_id_virtualBalance' => 'exists:transaction_currencies,id',
|
||||
'what' => 'in:' . $types
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -29,17 +29,18 @@ class BillFormRequest extends Request
|
||||
public function getBillData()
|
||||
{
|
||||
return [
|
||||
'name' => $this->get('name'),
|
||||
'match' => $this->get('match'),
|
||||
'amount_min' => round($this->get('amount_min'), 2),
|
||||
'amount_currency_id' => round($this->get('amount_currency_id'), 2),
|
||||
'amount_max' => round($this->get('amount_max'), 2),
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'user' => Auth::user()->id,
|
||||
'repeat_freq' => $this->get('repeat_freq'),
|
||||
'skip' => intval($this->get('skip')),
|
||||
'automatch' => intval($this->get('automatch')) === 1,
|
||||
'active' => intval($this->get('active')) === 1,
|
||||
'name' => $this->get('name'),
|
||||
'match' => $this->get('match'),
|
||||
'amount_min' => round($this->get('amount_min'), 2),
|
||||
'amount_currency_id_amount_min' => intval($this->get('amount_currency_id_amount_min')),
|
||||
'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')),
|
||||
'amount_max' => round($this->get('amount_max'), 2),
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'user' => Auth::user()->id,
|
||||
'repeat_freq' => $this->get('repeat_freq'),
|
||||
'skip' => intval($this->get('skip')),
|
||||
'automatch' => intval($this->get('automatch')) === 1,
|
||||
'active' => intval($this->get('active')) === 1,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -56,16 +57,17 @@ class BillFormRequest extends Request
|
||||
}
|
||||
|
||||
$rules = [
|
||||
'name' => $nameRule,
|
||||
'match' => $matchRule,
|
||||
'amount_min' => 'required|numeric|min:0.01',
|
||||
'amount_max' => 'required|numeric|min:0.01',
|
||||
'amount_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'automatch' => 'in:1',
|
||||
'active' => 'in:1',
|
||||
'name' => $nameRule,
|
||||
'match' => $matchRule,
|
||||
'amount_min' => 'required|numeric|min:0.01',
|
||||
'amount_max' => 'required|numeric|min:0.01',
|
||||
'amount_currency_id_amount_min' => 'required|exists:transaction_currencies,id',
|
||||
'amount_currency_id_amount_max' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'automatch' => 'in:1',
|
||||
'active' => 'in:1',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
|
@@ -5,6 +5,7 @@ namespace FireflyIII\Http\Requests;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Input;
|
||||
|
||||
/**
|
||||
@@ -30,20 +31,20 @@ class JournalFormRequest extends Request
|
||||
public function getJournalData()
|
||||
{
|
||||
return [
|
||||
'what' => $this->get('what'),
|
||||
'description' => $this->get('description'),
|
||||
'account_id' => intval($this->get('account_id')),
|
||||
'account_from_id' => intval($this->get('account_from_id')),
|
||||
'account_to_id' => intval($this->get('account_to_id')),
|
||||
'expense_account' => $this->get('expense_account'),
|
||||
'revenue_account' => $this->get('revenue_account'),
|
||||
'amount' => round($this->get('amount'), 2),
|
||||
'user' => Auth::user()->id,
|
||||
'amount_currency_id' => intval($this->get('amount_currency_id')),
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'budget_id' => intval($this->get('budget_id')),
|
||||
'category' => $this->get('category'),
|
||||
'tags' => explode(',', $this->get('tags')),
|
||||
'what' => $this->get('what'),
|
||||
'description' => $this->get('description'),
|
||||
'account_id' => intval($this->get('account_id')),
|
||||
'account_from_id' => intval($this->get('account_from_id')),
|
||||
'account_to_id' => intval($this->get('account_to_id')),
|
||||
'expense_account' => $this->get('expense_account'),
|
||||
'revenue_account' => $this->get('revenue_account'),
|
||||
'amount' => round($this->get('amount'), 2),
|
||||
'user' => Auth::user()->id,
|
||||
'amount_currency_id_amount' => intval($this->get('amount_currency_id_amount')),
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'budget_id' => intval($this->get('budget_id')),
|
||||
'category' => $this->get('category'),
|
||||
'tags' => explode(',', $this->get('tags')),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -56,16 +57,16 @@ class JournalFormRequest extends Request
|
||||
{
|
||||
$what = Input::get('what');
|
||||
$rules = [
|
||||
'description' => 'required|min:1,max:255',
|
||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||
'amount' => 'numeric|required|min:0.01',
|
||||
'date' => 'required|date',
|
||||
'amount_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'description' => 'required|min:1,max:255',
|
||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||
'amount' => 'numeric|required|min:0.01',
|
||||
'date' => 'required|date',
|
||||
'amount_currency_id_amount' => 'required|exists:transaction_currencies,id',
|
||||
|
||||
];
|
||||
|
||||
switch ($what) {
|
||||
case 'withdrawal':
|
||||
case strtolower(TransactionType::WITHDRAWAL):
|
||||
$rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||
$rules['expense_account'] = 'between:1,255';
|
||||
$rules['category'] = 'between:1,255';
|
||||
@@ -73,12 +74,12 @@ class JournalFormRequest extends Request
|
||||
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
|
||||
}
|
||||
break;
|
||||
case 'deposit':
|
||||
case strtolower(TransactionType::DEPOSIT):
|
||||
$rules['category'] = 'between:1,255';
|
||||
$rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||
$rules['revenue_account'] = 'between:1,255';
|
||||
break;
|
||||
case 'transfer':
|
||||
case strtolower(TransactionType::TRANSFER):
|
||||
$rules['account_from_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_to_id';
|
||||
$rules['account_to_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_from_id';
|
||||
$rules['category'] = 'between:1,255';
|
||||
|
@@ -27,11 +27,13 @@ class NewUserFormRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'bank_name' => 'required|between:1,200',
|
||||
'bank_balance' => 'required|numeric',
|
||||
'savings_balance' => 'numeric',
|
||||
'credit_card_limit' => 'numeric',
|
||||
'balance_currency_id' => 'exists:transaction_currencies,id',
|
||||
'bank_name' => 'required|between:1,200',
|
||||
'bank_balance' => 'required|numeric',
|
||||
'savings_balance' => 'numeric',
|
||||
'credit_card_limit' => 'numeric',
|
||||
'amount_currency_id_bank_balance' => 'exists:transaction_currencies,id',
|
||||
'amount_currency_id_savings_balance' => 'exists:transaction_currencies,id',
|
||||
'amount_currency_id_credit_card_limit' => 'exists:transaction_currencies,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -36,13 +36,13 @@ class PiggyBankFormRequest extends Request
|
||||
|
||||
|
||||
$rules = [
|
||||
'name' => $nameRule,
|
||||
'account_id' => 'required|belongsToUser:accounts',
|
||||
'targetamount' => 'required|min:0.01',
|
||||
'amount_currency_id' => 'exists:transaction_currencies,id',
|
||||
'startdate' => 'date',
|
||||
'targetdate' => $targetDateRule,
|
||||
'order' => 'integer|min:1',
|
||||
'name' => $nameRule,
|
||||
'account_id' => 'required|belongsToUser:accounts',
|
||||
'targetamount' => 'required|min:0.01',
|
||||
'amount_currency_id_targetamount' => 'exists:transaction_currencies,id',
|
||||
'startdate' => 'date',
|
||||
'targetdate' => $targetDateRule,
|
||||
'order' => 'integer|min:1',
|
||||
|
||||
];
|
||||
|
||||
|
@@ -154,6 +154,19 @@ Breadcrumbs::register(
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'categories.show.date', function (Generator $breadcrumbs, Category $category, Carbon $date) {
|
||||
|
||||
// get current period preference.
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
|
||||
$breadcrumbs->parent('categories.index');
|
||||
$breadcrumbs->push(e($category->name), route('categories.show', [$category->id]));
|
||||
$breadcrumbs->push(Navigation::periodShow($date, $range), route('categories.show.date', [$category->id, $date->format('Y-m-d')]));
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'categories.noCategory', function (Generator $breadcrumbs, $subTitle) {
|
||||
$breadcrumbs->parent('categories.index');
|
||||
@@ -338,30 +351,13 @@ Breadcrumbs::register(
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) {
|
||||
'reports.report', function (Generator $breadcrumbs, Carbon $start, Carbon $end, $reportType, $accountIds) {
|
||||
$breadcrumbs->parent('reports.index');
|
||||
if ($shared) {
|
||||
$title = trans('breadcrumbs.yearly_report_shared', ['date' => $date->year]);
|
||||
} else {
|
||||
$title = trans('breadcrumbs.yearly_report', ['date' => $date->year]);
|
||||
}
|
||||
$breadcrumbs->push($title, route('reports.year', [$date->year]));
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) {
|
||||
$breadcrumbs->parent('reports.year', $date, $shared);
|
||||
$language = Preferences::get('language', 'en')->data;
|
||||
$format = Config::get('firefly.month.' . $language);
|
||||
$monthFormat = trans('config.month_and_day');
|
||||
$title = trans('firefly.report_default', ['start' => $start->formatLocalized($monthFormat), 'end' => $end->formatLocalized($monthFormat)]);
|
||||
|
||||
if ($shared) {
|
||||
$title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->formatLocalized($format)]);
|
||||
} else {
|
||||
$title = trans('breadcrumbs.monthly_report', ['date' => $date->formatLocalized($format)]);
|
||||
}
|
||||
|
||||
$breadcrumbs->push($title, route('reports.month', [$date->year, $date->month]));
|
||||
$breadcrumbs->push($title, route('reports.report', ['url' => 'abcde']));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -403,7 +399,7 @@ Breadcrumbs::register(
|
||||
Breadcrumbs::register(
|
||||
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
|
||||
|
||||
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
|
||||
$breadcrumbs->parent('transactions.index', strtolower($journal->getTransactionType()));
|
||||
$breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
@@ -29,6 +30,111 @@ Route::bind(
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
// account list! Yay!
|
||||
Route::bind(
|
||||
'accountList',
|
||||
function ($value) {
|
||||
if (Auth::check()) {
|
||||
$ids = explode(',', $value);
|
||||
/** @var \Illuminate\Support\Collection $object */
|
||||
$object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->where('account_types.editable', 1)
|
||||
->whereIn('accounts.id', $ids)
|
||||
->where('user_id', Auth::user()->id)
|
||||
->get(['accounts.*']);
|
||||
if ($object->count() > 0) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
// budget list
|
||||
Route::bind(
|
||||
'budgetList',
|
||||
function ($value) {
|
||||
if (Auth::check()) {
|
||||
$ids = explode(',', $value);
|
||||
/** @var \Illuminate\Support\Collection $object */
|
||||
$object = Budget::where('active', 1)
|
||||
->whereIn('id', $ids)
|
||||
->where('user_id', Auth::user()->id)
|
||||
->get();
|
||||
|
||||
// add empty budget if applicable.
|
||||
if (in_array('0', $ids)) {
|
||||
$object->push(new Budget);
|
||||
}
|
||||
|
||||
if ($object->count() > 0) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
|
||||
// category list
|
||||
Route::bind(
|
||||
'categoryList',
|
||||
function ($value) {
|
||||
if (Auth::check()) {
|
||||
$ids = explode(',', $value);
|
||||
/** @var \Illuminate\Support\Collection $object */
|
||||
$object = Category::whereIn('id', $ids)
|
||||
->where('user_id', Auth::user()->id)
|
||||
->get();
|
||||
|
||||
// add empty budget if applicable.
|
||||
if (in_array('0', $ids)) {
|
||||
$object->push(new Category);
|
||||
}
|
||||
|
||||
if ($object->count() > 0) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
|
||||
// Date
|
||||
Route::bind(
|
||||
'start_date',
|
||||
function ($value) {
|
||||
if (Auth::check()) {
|
||||
|
||||
try {
|
||||
$date = new Carbon($value);
|
||||
} catch (Exception $e) {
|
||||
Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id);
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
|
||||
// Date
|
||||
Route::bind(
|
||||
'end_date',
|
||||
function ($value) {
|
||||
if (Auth::check()) {
|
||||
|
||||
try {
|
||||
$date = new Carbon($value);
|
||||
} catch (Exception $e) {
|
||||
Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id);
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
|
||||
Route::bind(
|
||||
'tj', function ($value) {
|
||||
@@ -162,7 +268,7 @@ Route::get('/cron/sendgrid', ['uses' => 'CronController@sendgrid']);
|
||||
|
||||
Route::controllers(
|
||||
[
|
||||
'auth' => 'Auth\AuthController',
|
||||
'auth' => 'Auth\AuthController',
|
||||
'password' => 'Auth\PasswordController',
|
||||
]
|
||||
);
|
||||
@@ -178,7 +284,6 @@ Route::group(
|
||||
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
|
||||
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
|
||||
Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']);
|
||||
Route::get('/routes', ['uses' => 'HomeController@routes']);
|
||||
/**
|
||||
* Account Controller
|
||||
*/
|
||||
@@ -244,6 +349,7 @@ Route::group(
|
||||
Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']);
|
||||
Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']);
|
||||
Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
|
||||
Route::get('/categories/show/{category}/{date}', ['uses' => 'CategoryController@showWithDate', 'as' => 'categories.show.date']);
|
||||
Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']);
|
||||
Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']);
|
||||
Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']);
|
||||
@@ -283,9 +389,7 @@ Route::group(
|
||||
// accounts:
|
||||
Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
|
||||
Route::get('/chart/account/expense', ['uses' => 'Chart\AccountController@expenseAccounts']);
|
||||
Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(
|
||||
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
|
||||
);
|
||||
Route::get('/chart/account/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\AccountController@report']);
|
||||
Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']);
|
||||
|
||||
|
||||
@@ -295,29 +399,34 @@ Route::group(
|
||||
|
||||
// budgets:
|
||||
Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']);
|
||||
Route::get('/chart/budget/year/{year}/{shared?}', ['uses' => 'Chart\BudgetController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
|
||||
|
||||
// this chart is used in reports:
|
||||
Route::get('/chart/budget/year/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@year']);
|
||||
Route::get('/chart/budget/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{budgetList}', ['uses' => 'Chart\BudgetController@multiYear']);
|
||||
|
||||
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']);
|
||||
Route::get('/chart/budget/{budget}', ['uses' => 'Chart\BudgetController@budget']);
|
||||
|
||||
// categories:
|
||||
Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']);
|
||||
Route::get('/chart/category/spent-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@spentInYear'])->where(
|
||||
['year' => '[0-9]{4}', 'shared' => 'shared']
|
||||
|
||||
// these three charts are for reports:
|
||||
Route::get('/chart/category/earned-in-period/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@earnedInPeriod']);
|
||||
Route::get('/chart/category/spent-in-period/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@spentInPeriod']);
|
||||
Route::get(
|
||||
'/chart/category/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{categoryList}', ['uses' => 'Chart\CategoryController@multiYear']
|
||||
);
|
||||
Route::get('/chart/category/earned-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@earnedInYear'])->where(
|
||||
['year' => '[0-9]{4}', 'shared' => 'shared']
|
||||
);
|
||||
Route::get('/chart/category/{category}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
|
||||
|
||||
Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']);
|
||||
Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']);
|
||||
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);
|
||||
|
||||
// piggy banks:
|
||||
Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
|
||||
|
||||
// reports:
|
||||
Route::get('/chart/report/in-out/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOut'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
|
||||
Route::get('/chart/report/in-out-sum/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where(
|
||||
['year' => '[0-9]{4}', 'shared' => 'shared']
|
||||
);
|
||||
Route::get('/chart/report/in-out/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']);
|
||||
Route::get('/chart/report/in-out-sum/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']);
|
||||
|
||||
|
||||
/**
|
||||
@@ -383,12 +492,7 @@ Route::group(
|
||||
* Report Controller
|
||||
*/
|
||||
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
|
||||
Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
|
||||
Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where(
|
||||
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
|
||||
);
|
||||
|
||||
// pop ups for budget report:
|
||||
Route::get('/reports/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']);
|
||||
|
||||
/**
|
||||
* Search Controller
|
||||
|
@@ -39,8 +39,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereSkip($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereNameEncrypted($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatchEncrypted($value)
|
||||
* @property-read \Carbon\Carbon $nextExpectedMatch
|
||||
* @property-read \Carbon\Carbon $lastFoundMatch
|
||||
* @property \Carbon\Carbon $nextExpectedMatch
|
||||
* @property \Carbon\Carbon $lastFoundMatch
|
||||
*/
|
||||
class Bill extends Model
|
||||
{
|
||||
|
@@ -25,7 +25,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUserId($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value)
|
||||
* @property-read float $spent
|
||||
* @property \Carbon\Carbon $lastActivity
|
||||
* @property \Carbon\Carbon $lastActivity
|
||||
*/
|
||||
class Category extends Model
|
||||
{
|
||||
|
@@ -68,7 +68,9 @@ use Watson\Validating\ValidatingTrait;
|
||||
* @property-read int $account_id
|
||||
* @property string $name
|
||||
* @property-read string $symbol
|
||||
* @property-read string $type
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments
|
||||
* @property-read mixed $amount_positive
|
||||
*/
|
||||
class TransactionJournal extends Model
|
||||
{
|
||||
@@ -120,7 +122,7 @@ class TransactionJournal extends Model
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getActualAmountAttribute()
|
||||
public function getAmountPositiveAttribute()
|
||||
{
|
||||
$amount = '0';
|
||||
/** @var Transaction $t */
|
||||
@@ -146,18 +148,10 @@ class TransactionJournal extends Model
|
||||
}
|
||||
|
||||
bcscale(2);
|
||||
$set = $this->transactions->sortByDesc('amount');
|
||||
$amount = $set->first()->amount;
|
||||
|
||||
if (intval($this->tag_count) === 1) {
|
||||
// get amount for single tag:
|
||||
$amount = $this->amountByTag($this->tags()->first(), $amount);
|
||||
}
|
||||
|
||||
if (intval($this->tag_count) > 1) {
|
||||
// get amount for either tag.
|
||||
$amount = $this->amountByTags($amount);
|
||||
|
||||
$transaction = $this->transactions->sortByDesc('amount')->first();
|
||||
$amount = $transaction->amount;
|
||||
if ($this->isWithdrawal()) {
|
||||
$amount = $amount * -1;
|
||||
}
|
||||
$cache->store($amount);
|
||||
|
||||
@@ -173,15 +167,15 @@ class TransactionJournal extends Model
|
||||
*/
|
||||
protected function amountByTagAdvancePayment(Tag $tag, $amount)
|
||||
{
|
||||
if ($this->transactionType->type == 'Withdrawal') {
|
||||
$others = $tag->transactionJournals()->transactionTypes(['Deposit'])->get();
|
||||
if ($this->isWithdrawal()) {
|
||||
$others = $tag->transactionJournals()->transactionTypes([TransactionType::DEPOSIT])->get();
|
||||
foreach ($others as $other) {
|
||||
$amount = bcsub($amount, $other->actual_amount);
|
||||
$amount = bcsub($amount, $other->amount_positive);
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
if ($this->transactionType->type == 'Deposit') {
|
||||
if ($this->isDeposit()) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
@@ -196,10 +190,10 @@ class TransactionJournal extends Model
|
||||
*/
|
||||
protected function amountByTagBalancingAct($tag, $amount)
|
||||
{
|
||||
if ($this->transactionType->type == 'Withdrawal') {
|
||||
$transfer = $tag->transactionJournals()->transactionTypes(['Transfer'])->first();
|
||||
if ($this->isWithdrawal()) {
|
||||
$transfer = $tag->transactionJournals()->transactionTypes([TransactionType::TRANSFER])->first();
|
||||
if ($transfer) {
|
||||
$amount = bcsub($amount, $transfer->actual_amount);
|
||||
$amount = bcsub($amount, $transfer->amount_positive);
|
||||
|
||||
return $amount;
|
||||
}
|
||||
@@ -260,21 +254,6 @@ class TransactionJournal extends Model
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCorrectAmountAttribute()
|
||||
{
|
||||
|
||||
switch ($this->transactionType->type) {
|
||||
case 'Deposit':
|
||||
return $this->transactions()->where('amount', '>', 0)->first()->amount;
|
||||
case 'Withdrawal':
|
||||
return $this->transactions()->where('amount', '<', 0)->first()->amount;
|
||||
}
|
||||
|
||||
return $this->transactions()->where('amount', '>', 0)->first()->amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
@@ -512,4 +491,59 @@ class TransactionJournal extends Model
|
||||
return $this->belongsTo('FireflyIII\User');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTransactionType()
|
||||
{
|
||||
return $this->transactionType->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isWithdrawal()
|
||||
{
|
||||
if (!is_null($this->type)) {
|
||||
return $this->type == TransactionType::WITHDRAWAL;
|
||||
}
|
||||
|
||||
return $this->transactionType->isWithdrawal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeposit()
|
||||
{
|
||||
if (!is_null($this->type)) {
|
||||
return $this->type == TransactionType::DEPOSIT;
|
||||
}
|
||||
|
||||
return $this->transactionType->isDeposit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransfer()
|
||||
{
|
||||
if (!is_null($this->type)) {
|
||||
return $this->type == TransactionType::TRANSFER;
|
||||
}
|
||||
|
||||
return $this->transactionType->isTransfer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isOpeningBalance()
|
||||
{
|
||||
if (!is_null($this->type)) {
|
||||
return $this->type == TransactionType::OPENING_BALANCE;
|
||||
}
|
||||
|
||||
return $this->transactionType->isOpeningBalance();
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,11 @@ class TransactionType extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
const WITHDRAWAL = 'Withdrawal';
|
||||
const DEPOSIT = 'Deposit';
|
||||
const TRANSFER = 'Transfer';
|
||||
const OPENING_BALANCE = 'Opening balance';
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@@ -31,6 +36,38 @@ class TransactionType extends Model
|
||||
return ['created_at', 'updated_at', 'deleted_at'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeposit()
|
||||
{
|
||||
return $this->type === TransactionType::DEPOSIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isOpeningBalance()
|
||||
{
|
||||
return $this->type === TransactionType::OPENING_BALANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransfer()
|
||||
{
|
||||
return $this->type === TransactionType::TRANSFER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isWithdrawal()
|
||||
{
|
||||
return $this->type === TransactionType::WITHDRAWAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
|
@@ -41,7 +41,16 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
public function countAccounts(array $types)
|
||||
{
|
||||
return Auth::user()->accounts()->accountTypeIn($types)->count();
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('user-count-accounts');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$count = Auth::user()->accounts()->accountTypeIn($types)->count();
|
||||
$cache->store($count);
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,6 +78,14 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
public function getAccounts(array $types)
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty('get-accounts');
|
||||
$cache->addProperty($types);
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
/** @var Collection $result */
|
||||
$result = Auth::user()->accounts()->with(
|
||||
['accountmeta' => function (HasMany $query) {
|
||||
@@ -82,23 +99,39 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
}
|
||||
);
|
||||
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method returns the users credit cards, along with some basic information about the
|
||||
* balance they have on their CC. To be used in the JSON boxes on the front page that say
|
||||
* how many bills there are still left to pay. The balance will be saved in field "balance".
|
||||
*
|
||||
* To get the balance, the field "date" is necessary.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getCreditCards()
|
||||
public function getCreditCards(Carbon $date)
|
||||
{
|
||||
return Auth::user()->accounts()
|
||||
->hasMetaValue('accountRole', 'ccAsset')
|
||||
->hasMetaValue('ccType', 'monthlyFull')
|
||||
->leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
|
||||
->groupBy('accounts.id')
|
||||
->get(
|
||||
[
|
||||
'accounts.*',
|
||||
'ccType.data as ccType',
|
||||
'accountRole.data as accountRole'
|
||||
'accountRole.data as accountRole',
|
||||
DB::Raw('SUM(`transactions`.`amount`) AS `balance`')
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -111,8 +144,18 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
public function getFirstTransaction(TransactionJournal $journal, Account $account)
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty('first-transaction');
|
||||
$cache->addProperty($journal->id);
|
||||
$cache->addProperty($account->id);
|
||||
|
||||
return $journal->transactions()->where('account_id', $account->id)->first();
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
$transaction = $journal->transactions()->where('account_id', $account->id)->first();
|
||||
$cache->store($transaction);
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,8 +166,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
public function getFrontpageAccounts(Preference $preference)
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($preference->data);
|
||||
$cache->addProperty('frontPageaccounts');
|
||||
$cache->addProperty('user-frontpage-accounts');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
@@ -158,6 +200,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('frontpage-transactions');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
@@ -172,6 +215,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
->before($end)
|
||||
->after($start)
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->take(10)
|
||||
->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']);
|
||||
@@ -227,14 +271,14 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($ids);
|
||||
$cache->addProperty('piggyAccounts');
|
||||
$cache->addProperty('user-piggy-bank-accounts');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$ids = array_unique($ids);
|
||||
if (count($ids) > 0) {
|
||||
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->get();
|
||||
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->where('accounts.active', 1)->get();
|
||||
}
|
||||
bcscale(2);
|
||||
|
||||
@@ -273,6 +317,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')
|
||||
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
|
||||
->where('account_meta.name', 'accountRole')
|
||||
->where('accounts.active', 1)
|
||||
->where('account_meta.data', '"savingAsset"')
|
||||
->get(['accounts.*']);
|
||||
$start = clone Session::get('start', new Carbon);
|
||||
@@ -319,31 +364,23 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$set = TransactionJournal::whereIn(
|
||||
$set = TransactionJournal::whereIn(
|
||||
'id', function (Builder $q) use ($account, $start, $end) {
|
||||
$q->select('transaction_journals.id')
|
||||
->from('transactions')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transactions.amount', '>', 0)// this makes the filter unnecessary.
|
||||
->where('transaction_journals.user_id', Auth::user()->id)
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->where('transaction_types.type', 'Transfer');
|
||||
->where('transaction_types.type', TransactionType::TRANSFER);
|
||||
|
||||
}
|
||||
)->get();
|
||||
$filtered = $set->filter(
|
||||
function (TransactionJournal $journal) use ($account) {
|
||||
if ($journal->destination_account->id == $account->id) {
|
||||
return $journal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
return $filtered;
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,12 +409,23 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
public function openingBalanceTransaction(Account $account)
|
||||
{
|
||||
return TransactionJournal
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty('opening-balance-journal');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
|
||||
$journal = TransactionJournal
|
||||
::orderBy('transaction_journals.date', 'ASC')
|
||||
->accountIs($account)
|
||||
->transactionTypes(['Opening balance'])
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE])
|
||||
->orderBy('created_at', 'ASC')
|
||||
->first(['transaction_journals.*']);
|
||||
$cache->store($journal);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,7 +596,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
protected function storeInitialBalance(Account $account, Account $opposing, array $data)
|
||||
{
|
||||
$transactionType = TransactionType::whereType('Opening balance')->first();
|
||||
$transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
@@ -642,4 +690,16 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param $accountId
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function find($accountId)
|
||||
{
|
||||
return Auth::user()->accounts()->findOrNew($accountId);
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,15 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function countAccounts(array $types);
|
||||
|
||||
/**
|
||||
* @param $accountId
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
public function find($accountId);
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Account $moveTo
|
||||
@@ -48,9 +57,17 @@ interface AccountRepositoryInterface
|
||||
public function getFirstTransaction(TransactionJournal $journal, Account $account);
|
||||
|
||||
/**
|
||||
* This method returns the users credit cards, along with some basic information about the
|
||||
* balance they have on their CC. To be used in the JSON boxes on the front page that say
|
||||
* how many bills there are still left to pay. The balance will be saved in field "balance".
|
||||
*
|
||||
* To get the balance, the field "date" is necessary.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getCreditCards();
|
||||
public function getCreditCards(Carbon $date);
|
||||
|
||||
/**
|
||||
* Get the accounts of a user that have piggy banks connected to them.
|
||||
|
@@ -5,10 +5,13 @@ namespace FireflyIII\Repositories\Bill;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Steam;
|
||||
|
||||
@@ -20,6 +23,7 @@ use Steam;
|
||||
class BillRepository implements BillRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @deprecated
|
||||
* Returns the sum of all payments connected to this bill between the dates.
|
||||
*
|
||||
* @param Bill $bill
|
||||
@@ -82,7 +86,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
public function getActiveBills()
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = Auth::user()->bills()->orderBy('name', 'ASC')->where('active', 1)->get()->sortBy('name');
|
||||
$set = Auth::user()->bills()->where('active', 1)->get()->sortBy('name');
|
||||
|
||||
return $set;
|
||||
}
|
||||
@@ -97,7 +101,50 @@ class BillRepository implements BillRepositoryInterface
|
||||
|
||||
$set = $set->sortBy(
|
||||
function (Bill $bill) {
|
||||
return strtolower($bill->name);
|
||||
|
||||
$int = $bill->active == 1 ? 0 : 1;
|
||||
|
||||
return $int . strtolower($bill->name);
|
||||
}
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBillsForAccounts(Collection $accounts)
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = Auth::user()->bills()->orderBy('name', 'ASC')->get();
|
||||
|
||||
$ids = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$ids[] = $account->id;
|
||||
}
|
||||
|
||||
$set = $set->filter(
|
||||
function (Bill $bill) use ($ids) {
|
||||
// get transaction journals from or to any of the mentioned accounts.
|
||||
// if zero, return null.
|
||||
$journals = $bill->transactionjournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->whereIn('transactions.account_id', $ids)->count();
|
||||
|
||||
return ($journals > 0);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
$set = $set->sortBy(
|
||||
function (Bill $bill) {
|
||||
|
||||
$int = $bill->active == 1 ? 0 : 1;
|
||||
|
||||
return $int . strtolower($bill->name);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -150,7 +197,9 @@ class BillRepository implements BillRepositoryInterface
|
||||
}
|
||||
$journals = new Collection;
|
||||
if (count($ids) > 0) {
|
||||
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
|
||||
$journals = Auth::user()->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->whereIn('transaction_journals.id', $ids)->get(
|
||||
['transaction_journals.*']
|
||||
);
|
||||
}
|
||||
|
||||
return $journals;
|
||||
@@ -270,10 +319,20 @@ class BillRepository implements BillRepositoryInterface
|
||||
*/
|
||||
public function scan(Bill $bill, TransactionJournal $journal)
|
||||
{
|
||||
|
||||
/*
|
||||
* Can only support withdrawals.
|
||||
*/
|
||||
if (false === $journal->isWithdrawal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$matches = explode(',', $bill->match);
|
||||
$description = strtolower($journal->description) . ' ' . strtolower($journal->destination_account->name);
|
||||
$wordMatch = $this->doWordMatch($matches, $description);
|
||||
$amountMatch = $this->doAmountMatch($journal->amount, $bill->amount_min, $bill->amount_max);
|
||||
$amountMatch = $this->doAmountMatch($journal->amount_positive, $bill->amount_min, $bill->amount_max);
|
||||
Log::debug('Journal #' . $journal->id . ' has description "' . $description . '"');
|
||||
|
||||
|
||||
/*
|
||||
* If both, update!
|
||||
@@ -283,6 +342,8 @@ class BillRepository implements BillRepositoryInterface
|
||||
$journal->save();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Log::debug('Wordmatch: ' . (($wordMatch) ? 'true' : 'false') . ' AmountMatch: ' . (($amountMatch) ? 'true' : 'false'));
|
||||
}
|
||||
if ($bill->id == $journal->bill_id) {
|
||||
// if no match, but bill used to match, remove it:
|
||||
@@ -437,22 +498,21 @@ class BillRepository implements BillRepositoryInterface
|
||||
{
|
||||
|
||||
$accounts = app('FireflyIII\Repositories\Account\AccountRepositoryInterface');
|
||||
$creditCards = $accounts->getCreditCards();
|
||||
$creditCards = $accounts->getCreditCards($end);
|
||||
$paid = $set->get('paid');
|
||||
$unpaid = $set->get('unpaid');
|
||||
|
||||
foreach ($creditCards as $creditCard) {
|
||||
$balance = Steam::balance($creditCard, $end, true);
|
||||
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
|
||||
if ($balance < 0) {
|
||||
if ($creditCard->balance < 0) {
|
||||
// unpaid! create a fake bill that matches the amount.
|
||||
$description = $creditCard->name;
|
||||
$amount = $balance * -1;
|
||||
$amount = $creditCard->balance * -1;
|
||||
$fakeBill = $this->createFakeBill($description, $date, $amount);
|
||||
unset($description, $amount);
|
||||
$unpaid->push([$fakeBill, $date]);
|
||||
}
|
||||
if ($balance == 0) {
|
||||
if ($creditCard->balance == 0) {
|
||||
// find transfer(s) TO the credit card which should account for
|
||||
// anything paid. If not, the CC is not yet used.
|
||||
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
|
||||
@@ -465,4 +525,32 @@ class BillRepository implements BillRepositoryInterface
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns all active bills which have been paid for in the given range,
|
||||
* with the field "paid" indicating how much the bill was for.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function billsPaidInRange(Carbon $start, Carbon $end)
|
||||
{
|
||||
$set = Auth::user()->bills()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.bill_id', '=', 'bills.id')
|
||||
->leftJoin(
|
||||
'transactions', function (JoinClause $join) {
|
||||
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0);
|
||||
}
|
||||
)
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->where('bills.active', 1)
|
||||
->groupBy('bills.id')->get(
|
||||
['bills.*', DB::Raw('SUM(`transactions`.`amount`) as `paid`')]
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@ interface BillRepositoryInterface
|
||||
public function getBillsForChart(Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Returns the sum of all payments connected to this bill between the dates.
|
||||
*
|
||||
* @param Bill $bill
|
||||
@@ -49,6 +50,17 @@ interface BillRepositoryInterface
|
||||
*/
|
||||
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* This method returns all active bills which have been paid for in the given range,
|
||||
* with the field "paid" indicating how much the bill was for.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function billsPaidInRange(Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Create a fake bill to help the chart controller.
|
||||
*
|
||||
@@ -77,6 +89,15 @@ interface BillRepositoryInterface
|
||||
*/
|
||||
public function getBills();
|
||||
|
||||
/**
|
||||
* Gets the bills which have some kind of relevance to the accounts mentioned.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBillsForAccounts(Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param Bill $bill
|
||||
*
|
||||
|
@@ -7,6 +7,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Shared\ComponentRepository;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
@@ -50,12 +51,12 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function expensesOnDayCorrected(Budget $budget, Carbon $date)
|
||||
public function expensesOnDay(Budget $budget, Carbon $date)
|
||||
{
|
||||
bcscale(2);
|
||||
$sum = $budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
|
||||
$sum = $budget->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
|
||||
|
||||
return bcmul($sum, -1);
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,20 +123,28 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Carbon $date
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return LimitRepetition|null
|
||||
*/
|
||||
public function getCurrentRepetition(Budget $budget, Carbon $date)
|
||||
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty($date);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
|
||||
$cache->addProperty('getCurrentRepetition');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
$data = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
|
||||
$data = $budget->limitrepetitions()
|
||||
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
|
||||
->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00'))
|
||||
->first(['limit_repetitions.*']);
|
||||
//Log::debug('Looking for limit reps for budget #' . $budget->id . ' start [' . $start . '] and end [' . $end . '].');
|
||||
//Log::debug(DB::getQueryLog())
|
||||
$cache->store($data);
|
||||
|
||||
return $data;
|
||||
@@ -220,6 +229,8 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return Carbon
|
||||
@@ -235,6 +246,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param Budget $budget
|
||||
* @param Carbon $date
|
||||
*
|
||||
@@ -297,25 +309,23 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
||||
)
|
||||
->after($start)
|
||||
->before($end)
|
||||
->transactionTypes(['Withdrawal'])
|
||||
->transactionTypes([TransactionType::WITHDRAWAL])
|
||||
->get(['transaction_journals.*'])->sum('amount');
|
||||
|
||||
bcscale(2);
|
||||
|
||||
return bcmul($noBudgetSet, -1);
|
||||
return $noBudgetSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param bool $shared
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true)
|
||||
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
return $this->commonBalanceInPeriod($budget, $start, $end, $shared);
|
||||
return $this->commonBalanceInPeriod($budget, $start, $end, $accounts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -35,7 +35,7 @@ interface BudgetRepositoryInterface
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function expensesOnDayCorrected(Budget $budget, Carbon $date);
|
||||
public function expensesOnDay(Budget $budget, Carbon $date);
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
@@ -65,11 +65,12 @@ interface BudgetRepositoryInterface
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Carbon $date
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return LimitRepetition|null
|
||||
*/
|
||||
public function getCurrentRepetition(Budget $budget, Carbon $date);
|
||||
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
@@ -95,6 +96,7 @@ interface BudgetRepositoryInterface
|
||||
public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return Carbon
|
||||
@@ -102,6 +104,7 @@ interface BudgetRepositoryInterface
|
||||
public function getLastBudgetLimitDate(Budget $budget);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param Budget $budget
|
||||
* @param Carbon $date
|
||||
*
|
||||
@@ -127,16 +130,16 @@ interface BudgetRepositoryInterface
|
||||
|
||||
/**
|
||||
*
|
||||
* Same as ::spentInPeriod but corrects journals for their amount (tags).
|
||||
* Same as ::spentInPeriod but corrects journals for a set of accounts
|
||||
*
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param boolean $shared
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true);
|
||||
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
|
@@ -5,9 +5,13 @@ namespace FireflyIII\Repositories\Category;
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Shared\ComponentRepository;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -46,6 +50,13 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('category-list');
|
||||
|
||||
if($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
/** @var Collection $set */
|
||||
$set = Auth::user()->categories()->orderBy('name', 'ASC')->get();
|
||||
$set = $set->sortBy(
|
||||
@@ -54,9 +65,76 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
}
|
||||
);
|
||||
|
||||
$cache->store($set);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount earned without category by accounts in period.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
|
||||
$accountIds = [];
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
|
||||
// is deposit AND account_from is in the list of $accounts
|
||||
// not from any of the accounts in the list?
|
||||
|
||||
return 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)
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->whereIn('transactions.account_id', $accountIds)
|
||||
->transactionTypes([TransactionType::DEPOSIT])
|
||||
->get(['transaction_journals.*'])->sum('amount');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the amount spent without category by accounts in period.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
|
||||
$accountIds = [];
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
|
||||
// is withdrawal or transfer AND account_from is in the list of $accounts
|
||||
|
||||
|
||||
return 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)
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->whereIn('transactions.account_id', $accountIds)
|
||||
->transactionTypes([TransactionType::WITHDRAWAL])
|
||||
->get(['transaction_journals.*'])->sum('amount');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -65,7 +143,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end)
|
||||
public function getCategoriesAndExpenses(Carbon $start, Carbon $end)
|
||||
{
|
||||
$set = Auth::user()->transactionjournals()
|
||||
->leftJoin(
|
||||
@@ -75,7 +153,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
->before($end)
|
||||
->where('categories.user_id', Auth::user()->id)
|
||||
->after($start)
|
||||
->transactionTypes(['Withdrawal'])
|
||||
->transactionTypes([TransactionType::WITHDRAWAL])
|
||||
->get(['categories.id as category_id', 'categories.encrypted as category_encrypted', 'categories.name', 'transaction_journals.*']);
|
||||
|
||||
bcscale(2);
|
||||
@@ -176,17 +254,16 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @param bool $shared
|
||||
* @param Category $category
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false)
|
||||
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
return $this->commonBalanceInPeriod($category, $start, $end, $shared);
|
||||
return $this->commonBalanceInPeriod($category, $start, $end, $accounts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,9 +274,9 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentOnDaySumCorrected(Category $category, Carbon $date)
|
||||
public function spentOnDaySum(Category $category, Carbon $date)
|
||||
{
|
||||
return $category->transactionjournals()->onDate($date)->get(['transaction_journals.*'])->sum('correct_amount');
|
||||
return $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,6 +313,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This method returns the sum of the journals in the category, optionally
|
||||
* limited by a start or end date.
|
||||
*
|
||||
@@ -259,7 +337,272 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
|
||||
$query->before($end);
|
||||
}
|
||||
|
||||
return $query->get(['transaction_journals.*'])->sum('correct_amount');
|
||||
return $query->get(['transaction_journals.*'])->sum('amount');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param \Carbon\Carbon $start
|
||||
* @param \Carbon\Carbon $end
|
||||
*
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentInPeriod(Category $category, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties; // we must cache this.
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('spentInPeriod');
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$sum = $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->before($end)->after($start)->get(['transaction_journals.*'])
|
||||
->sum(
|
||||
'amount'
|
||||
);
|
||||
|
||||
$cache->store($sum);
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param \Carbon\Carbon $start
|
||||
* @param \Carbon\Carbon $end
|
||||
*
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function earnedInPeriod(Category $category, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties; // we must cache this.
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('earnedInPeriod');
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$sum = $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->before($end)->after($start)->get(['transaction_journals.*'])
|
||||
->sum(
|
||||
'amount'
|
||||
);
|
||||
|
||||
$cache->store($sum);
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param int $page
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end)
|
||||
{
|
||||
$offset = $page > 0 ? $page * 50 : 0;
|
||||
|
||||
return $category->transactionJournals()
|
||||
->after($start)
|
||||
->before($end)
|
||||
->withRelevantData()->take(50)->offset($offset)
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->get(
|
||||
['transaction_journals.*']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countJournalsInRange(Category $category, Carbon $start, Carbon $end)
|
||||
{
|
||||
return $category->transactionJournals()->before($end)->after($start)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Corrected for tags.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function earnedOnDaySum(Category $category, Carbon $date)
|
||||
{
|
||||
return $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how much is spent in this period.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$accountIds = [];
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
|
||||
$sum
|
||||
= $category
|
||||
->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->after($start)
|
||||
->before($end)
|
||||
->whereIn('transactions.account_id', $accountIds)
|
||||
->transactionTypes([TransactionType::WITHDRAWAL])
|
||||
->get(['transaction_journals.*'])
|
||||
->sum('amount');
|
||||
|
||||
return $sum;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how much is earned in this period.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$accountIds = [];
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
$sum
|
||||
= $category
|
||||
->transactionjournals()
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->before($end)
|
||||
->whereIn('transactions.account_id', $accountIds)
|
||||
->transactionTypes([TransactionType::DEPOSIT])
|
||||
->after($start)
|
||||
->get(['transaction_journals.*'])
|
||||
->sum('amount');
|
||||
|
||||
return $sum;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of Categories appended with the amount of money that has been earned
|
||||
* in these categories, based on the $accounts involved, in period X.
|
||||
* The amount earned in category X in period X is saved in field "earned".
|
||||
*
|
||||
* @param $accounts
|
||||
* @param $start
|
||||
* @param $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function earnedForAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$accountIds = [];
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
|
||||
|
||||
$collection = Auth::user()->categories()
|
||||
->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
|
||||
->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin(
|
||||
'transactions AS t_src', function (JoinClause $join) {
|
||||
$join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0);
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions AS t_dest', function (JoinClause $join) {
|
||||
$join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
|
||||
}
|
||||
)
|
||||
->whereIn('t_dest.account_id', $accountIds)// to these accounts (earned)
|
||||
->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts
|
||||
->whereIn(
|
||||
'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
|
||||
)// earned from these things.
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->groupBy('categories.id')
|
||||
->get(['categories.*', DB::Raw('SUM(`t_dest`.`amount`) AS `earned`')]);
|
||||
|
||||
return $collection;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of Categories appended with the amount of money that has been spent
|
||||
* in these categories, based on the $accounts involved, in period X.
|
||||
* The amount earned in category X in period X is saved in field "spent".
|
||||
*
|
||||
* @param $accounts
|
||||
* @param $start
|
||||
* @param $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end)
|
||||
{
|
||||
$accountIds = [];
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds[] = $account->id;
|
||||
}
|
||||
|
||||
|
||||
$collection = Auth::user()->categories()
|
||||
->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
|
||||
->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin(
|
||||
'transactions AS t_src', function (JoinClause $join) {
|
||||
$join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0);
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions AS t_dest', function (JoinClause $join) {
|
||||
$join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
|
||||
}
|
||||
)
|
||||
->whereIn('t_src.account_id', $accountIds)// from these accounts (spent)
|
||||
->whereNotIn('t_dest.account_id', $accountIds)//-- but not from these accounts (spent internally)
|
||||
->whereIn(
|
||||
'transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
|
||||
)// spent on these things.
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->groupBy('categories.id')
|
||||
->get(['categories.*', DB::Raw('SUM(`t_dest`.`amount`) AS `spent`')]);
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,13 @@ interface CategoryRepositoryInterface
|
||||
*/
|
||||
public function countJournals(Category $category);
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countJournalsInRange(Category $category, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
@@ -27,11 +34,84 @@ interface CategoryRepositoryInterface
|
||||
*/
|
||||
public function destroy(Category $category);
|
||||
|
||||
/**
|
||||
* Returns a collection of Categories appended with the amount of money that has been earned
|
||||
* in these categories, based on the $accounts involved, in period X.
|
||||
* The amount earned in category X in period X is saved in field "earned".
|
||||
*
|
||||
* @param $accounts
|
||||
* @param $start
|
||||
* @param $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function earnedForAccounts(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Returns a collection of Categories appended with the amount of money that has been spent
|
||||
* in these categories, based on the $accounts involved, in period X.
|
||||
* The amount earned in category X in period X is saved in field "spent".
|
||||
*
|
||||
* @param $accounts
|
||||
* @param $start
|
||||
* @param $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getCategories();
|
||||
|
||||
|
||||
/**
|
||||
* Calculates how much is spent in this period.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Calculate how much is earned in this period.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Returns the amount spent without category by accounts in period.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Returns the amount earned without category by accounts in period.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Corrected for tags.
|
||||
*
|
||||
@@ -40,7 +120,7 @@ interface CategoryRepositoryInterface
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end);
|
||||
public function getCategoriesAndExpenses(Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
@@ -58,12 +138,21 @@ interface CategoryRepositoryInterface
|
||||
public function getJournals(Category $category, $page);
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param int $page
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This method returns the sum of the journals in the category, optionally
|
||||
* limited by a start or end date.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -85,8 +174,18 @@ interface CategoryRepositoryInterface
|
||||
public function getWithoutCategory(Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Corrected for tags.
|
||||
* Corrected for tags and list of accounts.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param \Carbon\Carbon $start
|
||||
* @param \Carbon\Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, Collection $accounts);
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param \Carbon\Carbon $start
|
||||
* @param \Carbon\Carbon $end
|
||||
@@ -95,7 +194,18 @@ interface CategoryRepositoryInterface
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false);
|
||||
public function spentInPeriod(Category $category, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
* @param \Carbon\Carbon $start
|
||||
* @param \Carbon\Carbon $end
|
||||
*
|
||||
* @param bool $shared
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function earnedInPeriod(Category $category, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -106,7 +216,18 @@ interface CategoryRepositoryInterface
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function spentOnDaySumCorrected(Category $category, Carbon $date);
|
||||
public function spentOnDaySum(Category $category, Carbon $date);
|
||||
|
||||
/**
|
||||
*
|
||||
* Corrected for tags.
|
||||
*
|
||||
* @param Category $category
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function earnedOnDaySum(Category $category, Carbon $date);
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
|
@@ -13,6 +13,7 @@ use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
@@ -44,7 +45,16 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
return Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('user-first-journal');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$entry = Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
|
||||
$cache->store($entry);
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +174,7 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'transaction_type_id' => $transactionType->id,
|
||||
'transaction_currency_id' => $data['amount_currency_id'],
|
||||
'transaction_currency_id' => $data['amount_currency_id_amount'],
|
||||
'description' => $data['description'],
|
||||
'completed' => 0,
|
||||
'date' => $data['date'],
|
||||
@@ -327,15 +337,15 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$fromAccount = null;
|
||||
$toAccount = null;
|
||||
switch ($type->type) {
|
||||
case 'Withdrawal':
|
||||
case TransactionType::WITHDRAWAL:
|
||||
list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data);
|
||||
break;
|
||||
|
||||
case 'Deposit':
|
||||
case TransactionType::DEPOSIT:
|
||||
list($fromAccount, $toAccount) = $this->storeDepositAccounts($data);
|
||||
|
||||
break;
|
||||
case 'Transfer':
|
||||
case TransactionType::TRANSFER:
|
||||
$fromAccount = Account::find($data['account_from_id']);
|
||||
$toAccount = Account::find($data['account_to_id']);
|
||||
break;
|
||||
|
@@ -1,149 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Repositories\PiggyBank;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
|
||||
/**
|
||||
* Class PiggyBankPart
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @package FireflyIII\Collection
|
||||
*/
|
||||
class PiggyBankPart
|
||||
{
|
||||
/** @var float */
|
||||
public $amountPerBar;
|
||||
/** @var float */
|
||||
public $cumulativeAmount;
|
||||
/** @var float */
|
||||
public $currentamount;
|
||||
|
||||
/** @var PiggyBankRepetition */
|
||||
public $repetition;
|
||||
|
||||
/** @var Carbon */
|
||||
public $startdate;
|
||||
|
||||
/** @var Carbon */
|
||||
public $targetdate;
|
||||
|
||||
/**
|
||||
* @return PiggyBankRepetition
|
||||
*/
|
||||
public function getRepetition()
|
||||
{
|
||||
return $this->repetition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PiggyBankRepetition $repetition
|
||||
*/
|
||||
public function setRepetition($repetition)
|
||||
{
|
||||
$this->repetition = $repetition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getStartdate()
|
||||
{
|
||||
return $this->startdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $startdate
|
||||
*/
|
||||
public function setStartdate($startdate)
|
||||
{
|
||||
$this->startdate = $startdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getTargetdate()
|
||||
{
|
||||
return $this->targetdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $targetdate
|
||||
*/
|
||||
public function setTargetdate($targetdate)
|
||||
{
|
||||
$this->targetdate = $targetdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float|int
|
||||
*/
|
||||
public function percentage()
|
||||
{
|
||||
bcscale(2);
|
||||
if ($this->getCurrentamount() < $this->getCumulativeAmount()) {
|
||||
$pct = 0;
|
||||
// calculate halfway point?
|
||||
if (bcsub($this->getCumulativeAmount(), $this->getCurrentamount()) < $this->getAmountPerBar()) {
|
||||
$left = $this->getCurrentamount() % $this->getAmountPerBar();
|
||||
$pct = round($left / $this->getAmountPerBar() * 100);
|
||||
}
|
||||
|
||||
return $pct;
|
||||
} else {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getCurrentamount()
|
||||
{
|
||||
return $this->currentamount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $currentamount
|
||||
*/
|
||||
public function setCurrentamount($currentamount)
|
||||
{
|
||||
$this->currentamount = $currentamount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getCumulativeAmount()
|
||||
{
|
||||
return $this->cumulativeAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $cumulativeAmount
|
||||
*/
|
||||
public function setCumulativeAmount($cumulativeAmount)
|
||||
{
|
||||
$this->cumulativeAmount = $cumulativeAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getAmountPerBar()
|
||||
{
|
||||
return $this->amountPerBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $amountPerBar
|
||||
*/
|
||||
public function setAmountPerBar($amountPerBar)
|
||||
{
|
||||
$this->amountPerBar = $amountPerBar;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -3,8 +3,11 @@
|
||||
namespace FireflyIII\Repositories\Shared;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ComponentRepository
|
||||
@@ -14,46 +17,42 @@ use Illuminate\Database\Query\JoinClause;
|
||||
class ComponentRepository
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @param bool $shared
|
||||
* @param $object
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function commonBalanceInPeriod($object, Carbon $start, Carbon $end, $shared = false)
|
||||
protected function commonBalanceInPeriod($object, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$cache = new CacheProperties; // we must cache this.
|
||||
$cache->addProperty($object->id);
|
||||
$cache->addProperty(get_class($object));
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($shared);
|
||||
$cache->addProperty('balanceInPeriod');
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty('balanceInPeriodList');
|
||||
|
||||
$ids = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$ids[] = $account->id;
|
||||
}
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if ($shared === true) { // shared is true: always ignore transfers between accounts!
|
||||
$sum = $object->transactionjournals()->transactionTypes(['Withdrawal'])->before($end)->after($start)
|
||||
->get(['transaction_journals.*'])->sum('amount');
|
||||
} else {
|
||||
// do something else, SEE budgets.
|
||||
// get all journals in this month where the asset account is NOT shared.
|
||||
$sum = $object->transactionjournals()->before($end)->after($start)
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->transactionTypes(['Withdrawal', 'Deposit', 'Opening balance'])
|
||||
->leftJoin(
|
||||
'account_meta', function (JoinClause $join) {
|
||||
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
|
||||
}
|
||||
)->where('account_meta.data', '!=', '"sharedAsset"')->get(['transaction_journals.*'])->sum('correct_amount');
|
||||
}
|
||||
$sum = $object->transactionjournals()
|
||||
->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE])
|
||||
->before($end)
|
||||
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->whereIn('accounts.id', $ids)
|
||||
->after($start)
|
||||
->get(['transaction_journals.*'])->sum('amount');
|
||||
|
||||
$cache->store($sum);
|
||||
|
||||
|
@@ -9,6 +9,7 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -54,6 +55,7 @@ class TagRepository implements TagRepositoryInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This method scans the transaction journals from or to the given asset account
|
||||
* and checks if these are part of a balancing act. If so, it will sum up the amounts
|
||||
* transferred into the balancing act (if any) and return this amount.
|
||||
@@ -77,7 +79,9 @@ class TagRepository implements TagRepositoryInterface
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
$journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes(['Transfer'])->get(['transaction_journals.*']);
|
||||
$journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes([TransactionType::TRANSFER])->get(
|
||||
['transaction_journals.*']
|
||||
);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
@@ -108,6 +112,13 @@ class TagRepository implements TagRepositoryInterface
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('tags-list');
|
||||
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
/** @var Collection $tags */
|
||||
$tags = Auth::user()->tags()->get();
|
||||
$tags = $tags->sortBy(
|
||||
@@ -116,6 +127,8 @@ class TagRepository implements TagRepositoryInterface
|
||||
}
|
||||
);
|
||||
|
||||
$cache->store($tags);
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
@@ -158,7 +171,7 @@ class TagRepository implements TagRepositoryInterface
|
||||
|
||||
if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') {
|
||||
foreach ($tag->transactionjournals as $journal) {
|
||||
if ($journal->transactionType->type == 'Transfer') {
|
||||
if ($journal->isTransfer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -169,7 +182,7 @@ class TagRepository implements TagRepositoryInterface
|
||||
*/
|
||||
$count = 0;
|
||||
foreach ($tag->transactionjournals as $journal) {
|
||||
if ($journal->transactionType->type == 'Withdrawal') {
|
||||
if ($journal->isWithdrawal()) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
@@ -201,7 +214,7 @@ class TagRepository implements TagRepositoryInterface
|
||||
* If any transaction is a deposit, cannot become a balancing act.
|
||||
*/
|
||||
foreach ($tag->transactionjournals as $journal) {
|
||||
if ($journal->transactionType->type == 'Deposit') {
|
||||
if ($journal->isDeposit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -239,10 +252,10 @@ class TagRepository implements TagRepositoryInterface
|
||||
protected function connectBalancingAct(TransactionJournal $journal, Tag $tag)
|
||||
{
|
||||
/** @var TransactionType $withdrawal */
|
||||
$withdrawal = TransactionType::whereType('Withdrawal')->first();
|
||||
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
|
||||
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
|
||||
/** @var TransactionType $transfer */
|
||||
$transfer = TransactionType::whereType('Transfer')->first();
|
||||
$transfer = TransactionType::whereType(TransactionType::TRANSFER)->first();
|
||||
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
|
||||
|
||||
|
||||
@@ -275,11 +288,11 @@ class TagRepository implements TagRepositoryInterface
|
||||
protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag)
|
||||
{
|
||||
/** @var TransactionType $transfer */
|
||||
$transfer = TransactionType::whereType('Transfer')->first();
|
||||
$transfer = TransactionType::whereType(TransactionType::TRANSFER)->first();
|
||||
/** @var TransactionType $withdrawal */
|
||||
$withdrawal = TransactionType::whereType('Withdrawal')->first();
|
||||
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
|
||||
/** @var TransactionType $deposit */
|
||||
$deposit = TransactionType::whereType('Deposit')->first();
|
||||
$deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first();
|
||||
|
||||
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
|
||||
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
|
||||
@@ -326,10 +339,10 @@ class TagRepository implements TagRepositoryInterface
|
||||
foreach ($tag->transactionjournals as $check) {
|
||||
// $checkAccount is the source_account for a withdrawal
|
||||
// $checkAccount is the destination_account for a deposit
|
||||
if ($check->transactionType->type == 'Withdrawal' && $check->source_account->id != $journal->destination_account->id) {
|
||||
if ($check->isWithdrawal() && $check->source_account->id != $journal->destination_account->id) {
|
||||
$match = false;
|
||||
}
|
||||
if ($check->transactionType->type == 'Deposit' && $check->destination_account->id != $journal->destination_account->id) {
|
||||
if ($check->isDeposit() && $check->destination_account->id != $journal->destination_account->id) {
|
||||
$match = false;
|
||||
}
|
||||
|
||||
|
@@ -27,6 +27,7 @@ interface TagRepositoryInterface
|
||||
public function connect(TransactionJournal $journal, Tag $tag);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This method scans the transaction journals from or to the given asset account
|
||||
* and checks if these are part of a balancing act. If so, it will sum up the amounts
|
||||
* transferred into the balancing act (if any) and return this amount.
|
||||
|
@@ -40,7 +40,7 @@ class Amount
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
} else {
|
||||
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
|
||||
$currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
|
||||
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
|
||||
|
||||
$cache->store($currency->symbol);
|
||||
@@ -60,7 +60,7 @@ class Amount
|
||||
{
|
||||
$amount = floatval($amount);
|
||||
$amount = round($amount, 2);
|
||||
$string = number_format($amount, 2, ',', '.');
|
||||
$string = money_format('%!.2n', $amount);
|
||||
|
||||
if ($coloured === true) {
|
||||
if ($amount === 0.0) {
|
||||
@@ -99,24 +99,21 @@ class Amount
|
||||
} else {
|
||||
$symbol = $journal->symbol;
|
||||
}
|
||||
$amount = $journal->amount;
|
||||
if ($journal->transactionType->type == 'Withdrawal') {
|
||||
$amount = $amount * -1;
|
||||
}
|
||||
if ($journal->transactionType->type == 'Transfer' && $coloured) {
|
||||
$txt = '<span class="text-info">' . $this->formatWithSymbol($symbol, $amount, false) . '</span>';
|
||||
|
||||
if ($journal->isTransfer() && $coloured) {
|
||||
$txt = '<span class="text-info">' . $this->formatWithSymbol($symbol, $journal->amount_positive, false) . '</span>';
|
||||
$cache->store($txt);
|
||||
|
||||
return $txt;
|
||||
}
|
||||
if ($journal->transactionType->type == 'Transfer' && !$coloured) {
|
||||
$txt = $this->formatWithSymbol($symbol, $amount, false);
|
||||
if ($journal->isTransfer() && !$coloured) {
|
||||
$txt = $this->formatWithSymbol($symbol, $journal->amount_positive, false);
|
||||
$cache->store($txt);
|
||||
|
||||
return $txt;
|
||||
}
|
||||
|
||||
$txt = $this->formatWithSymbol($symbol, $amount, $coloured);
|
||||
$txt = $this->formatWithSymbol($symbol, $journal->amount, $coloured);
|
||||
$cache->store($txt);
|
||||
|
||||
return $txt;
|
||||
@@ -155,7 +152,7 @@ class Amount
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
} else {
|
||||
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
|
||||
$currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
|
||||
|
||||
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
|
||||
if ($currency) {
|
||||
@@ -164,9 +161,9 @@ class Amount
|
||||
|
||||
return $currency->code;
|
||||
}
|
||||
$cache->store('EUR');
|
||||
$cache->store(env('DEFAULT_CURRENCY', 'EUR'));
|
||||
|
||||
return 'EUR'; // @codeCoverageIgnore
|
||||
return env('DEFAULT_CURRENCY', 'EUR'); // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -106,16 +106,21 @@ class Navigation
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate)
|
||||
public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate = null)
|
||||
{
|
||||
$functionMap = [
|
||||
'1D' => 'endOfDay',
|
||||
'daily' => 'endOfDay',
|
||||
'1W' => 'endOfWeek',
|
||||
'week' => 'endOfWeek',
|
||||
'weekly' => 'endOfWeek',
|
||||
'month' => 'endOfMonth',
|
||||
'1M' => 'endOfMonth',
|
||||
'monthly' => 'endOfMonth',
|
||||
'3M' => 'lastOfQuarter',
|
||||
'quarter' => 'lastOfQuarter',
|
||||
'quarterly' => 'lastOfQuarter',
|
||||
'1Y' => 'endOfYear',
|
||||
'year' => 'endOfYear',
|
||||
'yearly' => 'endOfYear',
|
||||
];
|
||||
@@ -128,7 +133,7 @@ class Navigation
|
||||
|
||||
}
|
||||
|
||||
if ($currentEnd > $maxDate) {
|
||||
if (!is_null($maxDate) && $currentEnd > $maxDate) {
|
||||
return clone $maxDate;
|
||||
}
|
||||
|
||||
@@ -145,12 +150,17 @@ class Navigation
|
||||
public function periodShow(Carbon $date, $repeatFrequency)
|
||||
{
|
||||
$formatMap = [
|
||||
'1D' => '%e %B %Y',
|
||||
'daily' => '%e %B %Y',
|
||||
'1W' => 'Week %W, %Y',
|
||||
'week' => 'Week %W, %Y',
|
||||
'weekly' => 'Week %W, %Y',
|
||||
'3M' => '%B %Y',
|
||||
'quarter' => '%B %Y',
|
||||
'1M' => '%B %Y',
|
||||
'month' => '%B %Y',
|
||||
'monthly' => '%B %Y',
|
||||
'1Y' => '%Y',
|
||||
'year' => '%Y',
|
||||
'yearly' => '%Y',
|
||||
|
||||
@@ -218,12 +228,15 @@ class Navigation
|
||||
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
|
||||
{
|
||||
$date = clone $theDate;
|
||||
|
||||
// 1D 1W 1M 3M 6M 1Y
|
||||
$functionMap = [
|
||||
'1D' => 'subDays',
|
||||
'daily' => 'subDays',
|
||||
'week' => 'subWeeks',
|
||||
'1W' => 'subWeeks',
|
||||
'weekly' => 'subWeeks',
|
||||
'month' => 'subMonths',
|
||||
'1M' => 'subMonths',
|
||||
'monthly' => 'subMonths',
|
||||
'year' => 'subYears',
|
||||
'yearly' => 'subYears',
|
||||
|
@@ -72,6 +72,54 @@ class Steam
|
||||
return round($balance, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the balance for the given account during the whole range, using this format:
|
||||
*
|
||||
* [yyyy-mm-dd] => 123,2
|
||||
*
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*/
|
||||
public function balanceInRange(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
// abuse chart properties:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty('balance-in-range');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$balances = [];
|
||||
$start->subDay();
|
||||
$end->addDay();
|
||||
$startBalance = $this->balance($account, $start);
|
||||
$balances[$start->format('Y-m-d')] = $startBalance;
|
||||
$start->addDay();
|
||||
|
||||
// query!
|
||||
$set = $account->transactions()
|
||||
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->groupBy('transaction_journals.date')
|
||||
->get(['transaction_journals.date', DB::Raw('SUM(`transactions`.`amount`) as `modified`')]);
|
||||
$currentBalance = $startBalance;
|
||||
foreach ($set as $entry) {
|
||||
$currentBalance = bcadd($currentBalance, $entry->modified);
|
||||
$balances[$entry->date] = $currentBalance;
|
||||
}
|
||||
|
||||
$cache->store($balances);
|
||||
|
||||
return $balances;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $ids
|
||||
|
@@ -23,10 +23,10 @@ class Budget extends Twig_Extension
|
||||
{
|
||||
$functions = [];
|
||||
$functions[] = new Twig_SimpleFunction(
|
||||
'spentInRepetitionCorrected', function (LimitRepetition $repetition) {
|
||||
'spentInRepetition', function (LimitRepetition $repetition) {
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($repetition->id);
|
||||
$cache->addProperty('spentInRepetitionCorrected');
|
||||
$cache->addProperty('spentInRepetition');
|
||||
if ($cache->has()) {
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
@@ -68,19 +68,17 @@ class Journal extends Twig_Extension
|
||||
return $cache->get(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$type = $journal->transactionType->type;
|
||||
|
||||
switch ($type) {
|
||||
case 'Withdrawal':
|
||||
switch (true) {
|
||||
case $journal->isWithdrawal():
|
||||
$txt = '<i class="fa fa-long-arrow-left fa-fw" title="' . trans('firefly.withdrawal') . '"></i>';
|
||||
break;
|
||||
case 'Deposit':
|
||||
case $journal->isDeposit():
|
||||
$txt = '<i class="fa fa-long-arrow-right fa-fw" title="' . trans('firefly.deposit') . '"></i>';
|
||||
break;
|
||||
case 'Transfer':
|
||||
case $journal->isTransfer():
|
||||
$txt = '<i class="fa fa-fw fa-exchange" title="' . trans('firefly.transfer') . '"></i>';
|
||||
break;
|
||||
case 'Opening balance':
|
||||
case $journal->isOpeningBalance():
|
||||
$txt = '<i class="fa-fw fa fa-ban" title="' . trans('firefly.openingBalance') . '"></i>';
|
||||
break;
|
||||
default:
|
||||
@@ -181,7 +179,7 @@ class Journal extends Twig_Extension
|
||||
if ($tag->tagMode == 'balancingAct') {
|
||||
// return tag formatted for a "balancing act", even if other
|
||||
// tags are present.
|
||||
$amount = app('amount')->format($journal->actual_amount, false);
|
||||
$amount = app('amount')->format($journal->amount_positive, false);
|
||||
$string = '<a href="' . route('tags.show', [$tag->id]) . '" class="label label-success" title="' . $amount
|
||||
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</a>';
|
||||
|
||||
@@ -189,7 +187,7 @@ class Journal extends Twig_Extension
|
||||
}
|
||||
|
||||
if ($tag->tagMode == 'advancePayment') {
|
||||
if ($journal->transactionType->type == 'Deposit') {
|
||||
if ($journal->isDeposit()) {
|
||||
$amount = app('amount')->formatJournal($journal, false);
|
||||
$string = '<a href="' . route('tags.show', [$tag->id]) . '" class="label label-success" title="' . $amount
|
||||
. '"><i class="fa fa-fw fa-sort-numeric-desc"></i> ' . $tag->tag . '</a>';
|
||||
@@ -201,7 +199,7 @@ class Journal extends Twig_Extension
|
||||
* AdvancePayment with a withdrawal will show the amount with a link to
|
||||
* the tag. The TransactionJournal should properly calculate the amount.
|
||||
*/
|
||||
if ($journal->transactionType->type == 'Withdrawal') {
|
||||
if ($journal->isWithdrawal()) {
|
||||
$amount = app('amount')->formatJournal($journal);
|
||||
|
||||
$string = '<a href="' . route('tags.show', [$tag->id]) . '">' . $amount . '</a>';
|
||||
|
@@ -272,7 +272,8 @@ class FireflyValidator extends Validator
|
||||
$exclude = isset($parameters[2]) ? intval($parameters[2]) : 0;
|
||||
|
||||
// get entries from table
|
||||
$set = DB::table($table)->where('user_id', Auth::user()->id)->where('id', '!=', $exclude)->get([$field]);
|
||||
$set = DB::table($table)->where('user_id', Auth::user()->id)
|
||||
->where('id', '!=', $exclude)->get([$field]);
|
||||
|
||||
foreach ($set as $entry) {
|
||||
$fieldValue = $this->tryDecrypt($entry->$field);
|
||||
@@ -296,6 +297,7 @@ class FireflyValidator extends Validator
|
||||
{
|
||||
$exclude = isset($parameters[0]) ? $parameters[0] : null;
|
||||
$query = DB::table('piggy_banks');
|
||||
$query->whereNull('piggy_banks.deleted_at');
|
||||
$query->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id');
|
||||
$query->where('accounts.user_id', Auth::user()->id);
|
||||
if (!is_null($exclude)) {
|
||||
|
@@ -26,7 +26,7 @@
|
||||
"watson/validating": "~1.0",
|
||||
"doctrine/dbal": "~2.5",
|
||||
"illuminate/html": "~5.0",
|
||||
"league/commonmark": "0.7.*",
|
||||
"league/commonmark": "~0.7",
|
||||
"rcrowe/twigbridge": "0.7.x@dev",
|
||||
"zizaco/entrust": "dev-laravel-5",
|
||||
"league/csv": "^7.1"
|
||||
|
737
composer.lock
generated
737
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => 'en',
|
||||
'locale' => 'en_US',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -65,7 +65,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback_locale' => 'en',
|
||||
'fallback_locale' => 'en_US',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -139,8 +139,8 @@ return [
|
||||
'TwigBridge\ServiceProvider',
|
||||
|
||||
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
|
||||
//'Barryvdh\Debugbar\ServiceProvider',
|
||||
//'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
|
||||
// 'Barryvdh\Debugbar\ServiceProvider',
|
||||
// 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
|
||||
'Zizaco\Entrust\EntrustServiceProvider',
|
||||
|
||||
/*
|
||||
|
@@ -49,7 +49,7 @@ return [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => __DIR__ . '/../storage/database/testing.db',
|
||||
'database' => storage_path('database/testing.db'),
|
||||
'prefix' => '',
|
||||
],
|
||||
|
||||
|
@@ -1,20 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'chart' => 'chartjs',
|
||||
'version' => '3.5.1',
|
||||
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
|
||||
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
|
||||
'csv_import_enabled' => true,
|
||||
'maxUploadSize' => 5242880,
|
||||
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
|
||||
'piggy_bank_periods' => [
|
||||
'chart' => 'chartjs',
|
||||
'version' => '3.5.5',
|
||||
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
|
||||
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
|
||||
'csv_import_enabled' => true,
|
||||
'maxUploadSize' => 5242880,
|
||||
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
|
||||
'piggy_bank_periods' => [
|
||||
'week' => 'Week',
|
||||
'month' => 'Month',
|
||||
'quarter' => 'Quarter',
|
||||
'year' => 'Year'
|
||||
],
|
||||
'periods_to_text' => [
|
||||
'periods_to_text' => [
|
||||
'weekly' => 'A week',
|
||||
'monthly' => 'A month',
|
||||
'quarterly' => 'A quarter',
|
||||
@@ -22,7 +22,7 @@ return [
|
||||
'yearly' => 'A year',
|
||||
],
|
||||
|
||||
'accountRoles' => [
|
||||
'accountRoles' => [
|
||||
'defaultAsset' => 'Default asset account',
|
||||
'sharedAsset' => 'Shared asset account',
|
||||
'savingAsset' => 'Savings account',
|
||||
@@ -98,13 +98,23 @@ return [
|
||||
'Revenue account' => 'revenue',
|
||||
'Cash account' => 'cash',
|
||||
],
|
||||
'languages' => [
|
||||
'en_US' => ['name_locale' => 'English', 'name_english' => 'English', 'complete' => true],
|
||||
'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true],
|
||||
'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portugese (Brazil)', 'complete' => false],
|
||||
'fr_FR' => ['name_locale' => 'Français', 'name_english' => 'French', 'complete' => false],
|
||||
],
|
||||
'lang' => [
|
||||
'en' => 'English',
|
||||
'nl' => 'Nederlands'
|
||||
'en_US' => 'English',
|
||||
'nl_NL' => 'Nederlands',
|
||||
'fr_FR' => 'Français',
|
||||
'pt_BR' => 'Português do Brasil',
|
||||
],
|
||||
'locales' => [
|
||||
'en' => ['en', 'English', 'en_US', 'en_US.utf8'],
|
||||
'nl' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
|
||||
'en_US' => ['en', 'English', 'en_US', 'en_US.utf8'],
|
||||
'nl_NL' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
|
||||
'pt_BR' => ['pt_BR', 'pt_BR.utf8'],
|
||||
'fr_FR' => ['fr_FR', 'fr_FR.utf8'],
|
||||
],
|
||||
'transactionTypesByWhat' => [
|
||||
'expenses' => ['Withdrawal'],
|
||||
@@ -124,13 +134,17 @@ return [
|
||||
|
||||
],
|
||||
|
||||
'month' => [
|
||||
'en' => '%B %Y',
|
||||
'nl' => '%B %Y',
|
||||
'month' => [
|
||||
'en_US' => '%B %Y',
|
||||
'nl_NL' => '%B %Y',
|
||||
'fr_FR' => '%B %Y',
|
||||
'pt_BR' => '%B %Y',
|
||||
],
|
||||
'monthAndDay' => [
|
||||
'en' => '%B %e, %Y',
|
||||
'nl' => '%e %B %Y',
|
||||
'monthAndDay' => [
|
||||
'en_US' => '%B %e, %Y',
|
||||
'nl_NL' => '%e %B %Y',
|
||||
'fr_FR' => '%B %e, %Y',
|
||||
'pt_BR' => '%B %e, %Y',
|
||||
],
|
||||
|
||||
];
|
||||
|
@@ -15,7 +15,8 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('EMAIL_DRIVER', 'smtp'),
|
||||
'blocked_domains' => explode(',', env('BLOCKED_DOMAINS')),
|
||||
'driver' => env('EMAIL_DRIVER', 'smtp'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -28,7 +29,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'),
|
||||
'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -41,7 +42,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'port' => 587,
|
||||
'port' => 587,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -54,7 +55,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'],
|
||||
'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -80,7 +81,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'username' => env('EMAIL_USERNAME', null),
|
||||
'username' => env('EMAIL_USERNAME', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -93,7 +94,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'password' => env('EMAIL_PASSWORD', null),
|
||||
'password' => env('EMAIL_PASSWORD', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -106,7 +107,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'sendmail' => '/usr/sbin/sendmail -bs',
|
||||
'sendmail' => '/usr/sbin/sendmail -bs',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -119,6 +120,6 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'pretend' => env('EMAIL_PRETEND', false),
|
||||
'pretend' => env('EMAIL_PRETEND', false),
|
||||
|
||||
];
|
||||
|
@@ -49,7 +49,7 @@ class TestDataSeeder extends Seeder
|
||||
$this->createPiggybanks();
|
||||
|
||||
// dates:
|
||||
$start = Carbon::now()->subyear()->startOfMonth();
|
||||
$start = Carbon::now()->subYears(5)->startOfMonth();
|
||||
$end = Carbon::now()->endOfDay();
|
||||
|
||||
|
||||
|
@@ -12,10 +12,10 @@ class TransactionTypeSeeder extends Seeder
|
||||
|
||||
DB::table('transaction_types')->delete();
|
||||
|
||||
TransactionType::create(['type' => 'Withdrawal']);
|
||||
TransactionType::create(['type' => 'Deposit']);
|
||||
TransactionType::create(['type' => 'Transfer']);
|
||||
TransactionType::create(['type' => 'Opening balance']);
|
||||
TransactionType::create(['type' => TransactionType::WITHDRAWAL]);
|
||||
TransactionType::create(['type' => TransactionType::DEPOSIT]);
|
||||
TransactionType::create(['type' => TransactionType::TRANSFER]);
|
||||
TransactionType::create(['type' => TransactionType::OPENING_BALANCE]);
|
||||
}
|
||||
|
||||
}
|
||||
|
4
public/dist/css/AdminLTE.min.css
vendored
4
public/dist/css/AdminLTE.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
.skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .content-wrapper,.skin-blue-light .main-footer{border-left:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .treeview-menu>li>a{color:#777}.skin-blue-light .treeview-menu>li.active>a,.skin-blue-light .treeview-menu>li>a:hover{color:#000}.skin-blue-light .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px !important;border-top-right-radius:0 !important;border-bottom-right-radius:0 !important;border-bottom-left-radius:2px !important}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0 !important;border-top-right-radius:2px !important;border-bottom-right-radius:2px !important;border-bottom-left-radius:0 !important}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
|
||||
.skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus,.skin-blue-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .content-wrapper,.skin-blue-light .main-footer{border-left:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .treeview-menu>li>a{color:#777}.skin-blue-light .treeview-menu>li.active>a,.skin-blue-light .treeview-menu>li>a:hover{color:#000}.skin-blue-light .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
|
BIN
public/dist/img/icons.png
vendored
BIN
public/dist/img/icons.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.1 KiB |
4
public/dist/js/app.min.js
vendored
4
public/dist/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
4
public/js/accounting.min.js
vendored
Normal file
4
public/js/accounting.min.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/*!
|
||||
* accounting.js v0.4.2, copyright 2014 Open Exchange Rates, MIT license, http://openexchangerates.github.io/accounting.js
|
||||
*/
|
||||
(function(p,z){function q(a){return!!(""===a||a&&a.charCodeAt&&a.substr)}function m(a){return u?u(a):"[object Array]"===v.call(a)}function r(a){return"[object Object]"===v.call(a)}function s(a,b){var d,a=a||{},b=b||{};for(d in b)b.hasOwnProperty(d)&&null==a[d]&&(a[d]=b[d]);return a}function j(a,b,d){var c=[],e,h;if(!a)return c;if(w&&a.map===w)return a.map(b,d);for(e=0,h=a.length;e<h;e++)c[e]=b.call(d,a[e],e,a);return c}function n(a,b){a=Math.round(Math.abs(a));return isNaN(a)?b:a}function x(a){var b=c.settings.currency.format;"function"===typeof a&&(a=a());return q(a)&&a.match("%v")?{pos:a,neg:a.replace("-","").replace("%v","-%v"),zero:a}:!a||!a.pos||!a.pos.match("%v")?!q(b)?b:c.settings.currency.format={pos:b,neg:b.replace("%v","-%v"),zero:b}:a}var c={version:"0.4.1",settings:{currency:{symbol:"$",format:"%s%v",decimal:".",thousand:",",precision:2,grouping:3},number:{precision:0,grouping:3,thousand:",",decimal:"."}}},w=Array.prototype.map,u=Array.isArray,v=Object.prototype.toString,o=c.unformat=c.parse=function(a,b){if(m(a))return j(a,function(a){return o(a,b)});a=a||0;if("number"===typeof a)return a;var b=b||".",c=RegExp("[^0-9-"+b+"]",["g"]),c=parseFloat((""+a).replace(/\((.*)\)/,"-$1").replace(c,"").replace(b,"."));return!isNaN(c)?c:0},y=c.toFixed=function(a,b){var b=n(b,c.settings.number.precision),d=Math.pow(10,b);return(Math.round(c.unformat(a)*d)/d).toFixed(b)},t=c.formatNumber=c.format=function(a,b,d,i){if(m(a))return j(a,function(a){return t(a,b,d,i)});var a=o(a),e=s(r(b)?b:{precision:b,thousand:d,decimal:i},c.settings.number),h=n(e.precision),f=0>a?"-":"",g=parseInt(y(Math.abs(a||0),h),10)+"",l=3<g.length?g.length%3:0;return f+(l?g.substr(0,l)+e.thousand:"")+g.substr(l).replace(/(\d{3})(?=\d)/g,"$1"+e.thousand)+(h?e.decimal+y(Math.abs(a),h).split(".")[1]:"")},A=c.formatMoney=function(a,b,d,i,e,h){if(m(a))return j(a,function(a){return A(a,b,d,i,e,h)});var a=o(a),f=s(r(b)?b:{symbol:b,precision:d,thousand:i,decimal:e,format:h},c.settings.currency),g=x(f.format);return(0<a?g.pos:0>a?g.neg:g.zero).replace("%s",f.symbol).replace("%v",t(Math.abs(a),n(f.precision),f.thousand,f.decimal))};c.formatColumn=function(a,b,d,i,e,h){if(!a)return[];var f=s(r(b)?b:{symbol:b,precision:d,thousand:i,decimal:e,format:h},c.settings.currency),g=x(f.format),l=g.pos.indexOf("%s")<g.pos.indexOf("%v")?!0:!1,k=0,a=j(a,function(a){if(m(a))return c.formatColumn(a,f);a=o(a);a=(0<a?g.pos:0>a?g.neg:g.zero).replace("%s",f.symbol).replace("%v",t(Math.abs(a),n(f.precision),f.thousand,f.decimal));if(a.length>k)k=a.length;return a});return j(a,function(a){return q(a)&&a.length<k?l?a.replace(f.symbol,f.symbol+Array(k-a.length+1).join(" ")):Array(k-a.length+1).join(" ")+a:a})};if("undefined"!==typeof exports){if("undefined"!==typeof module&&module.exports)exports=module.exports=c;exports.accounting=c}else"function"===typeof define&&define.amd?define([],function(){return c}):(c.noConflict=function(a){return function(){p.accounting=a;c.noConflict=z;return c}}(p.accounting),p.accounting=c)})(this);
|
@@ -13,6 +13,7 @@ var fixHelper = function (e, ui) {
|
||||
$(function () {
|
||||
"use strict";
|
||||
if (typeof(lineChart) === "function" && typeof accountID !== 'undefined') {
|
||||
|
||||
lineChart('chart/account/' + accountID, 'overview-chart');
|
||||
}
|
||||
|
||||
@@ -26,6 +27,8 @@ $(function () {
|
||||
handle: '.handle'
|
||||
}
|
||||
).disableSelection();
|
||||
} else {
|
||||
console.log('its null');
|
||||
}
|
||||
|
||||
});
|
||||
@@ -34,6 +37,7 @@ $(function () {
|
||||
function sortStop(event, ui) {
|
||||
"use strict";
|
||||
var current = $(ui.item);
|
||||
console.log('sort stop');
|
||||
var thisDate = current.data('date');
|
||||
var originalBG = current.css('backgroundColor');
|
||||
|
||||
|
@@ -1,11 +1,19 @@
|
||||
/* globals $, categoryID, columnChart */
|
||||
/* globals $, categoryID, columnChart, categoryDate */
|
||||
$(function () {
|
||||
"use strict";
|
||||
if (typeof categoryID !== 'undefined') {
|
||||
columnChart('chart/category/' + categoryID + '/all', 'all');
|
||||
columnChart('chart/category/' + categoryID + '/month', 'month');
|
||||
// more splits:
|
||||
if ($('#all').length > 0) {
|
||||
columnChart('chart/category/' + categoryID + '/all', 'all');
|
||||
}
|
||||
if ($('#period').length > 0) {
|
||||
columnChart('chart/category/' + categoryID + '/period', 'period');
|
||||
}
|
||||
|
||||
}
|
||||
if (typeof categoryID !== 'undefined' && typeof categoryDate !== undefined) {
|
||||
columnChart('chart/category/' + categoryID + '/period/' + categoryDate, 'period-specific-period');
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
@@ -1,4 +1,4 @@
|
||||
/* globals $, Chart, currencySymbol */
|
||||
/* globals $, Chart, currencySymbol,mon_decimal_point ,accounting, mon_thousands_sep, frac_digits */
|
||||
|
||||
/*
|
||||
Make some colours:
|
||||
@@ -24,6 +24,23 @@ var colourSet = [
|
||||
|
||||
];
|
||||
|
||||
// Settings object that controls default parameters for library methods:
|
||||
accounting.settings = {
|
||||
currency: {
|
||||
symbol : currencySymbol, // default currency symbol is '$'
|
||||
format: "%s %v", // controls output: %s = symbol, %v = value/number (can be object: see below)
|
||||
decimal : mon_decimal_point, // decimal point separator
|
||||
thousand: mon_thousands_sep, // thousands separator
|
||||
precision : frac_digits // decimal places
|
||||
},
|
||||
number: {
|
||||
precision : 0, // default precision on numbers is 0
|
||||
thousand: ",",
|
||||
decimal : "."
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var fillColors = [];
|
||||
var strokePointHighColors = [];
|
||||
|
||||
@@ -45,9 +62,9 @@ var defaultAreaOptions = {
|
||||
animation: false,
|
||||
scaleFontSize: 10,
|
||||
responsive: false,
|
||||
scaleLabel: " <%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
|
||||
scaleLabel: " <%= accounting.formatMoney(value) %>",
|
||||
tooltipFillColor: "rgba(0,0,0,0.5)",
|
||||
multiTooltipTemplate: "<%=datasetLabel%>: <%= '" + currencySymbol + " ' + Number(value).toFixed(2).replace('.', ',') %>"
|
||||
multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>"
|
||||
};
|
||||
|
||||
|
||||
@@ -61,7 +78,7 @@ var defaultPieOptions = {
|
||||
scaleFontSize: 10,
|
||||
responsive: false,
|
||||
tooltipFillColor: "rgba(0,0,0,0.5)",
|
||||
tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>",
|
||||
tooltipTemplate: "<%if (label){%><%=label%>: <%}%> <%= accounting.formatMoney(value) %>",
|
||||
|
||||
};
|
||||
|
||||
@@ -90,10 +107,10 @@ var defaultColumnOptions = {
|
||||
scaleFontSize: 10,
|
||||
responsive: false,
|
||||
animation: false,
|
||||
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
|
||||
scaleLabel: "<%= accounting.formatMoney(value) %>",
|
||||
tooltipFillColor: "rgba(0,0,0,0.5)",
|
||||
tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>",
|
||||
multiTooltipTemplate: "<%=datasetLabel%>: " + currencySymbol + " <%= Number(value).toFixed(2).replace('.', ',') %>"
|
||||
tooltipTemplate: "<%if (label){%><%=label%>: <%}%> <%= accounting.formatMoney(value) %>",
|
||||
multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>"
|
||||
};
|
||||
|
||||
var defaultStackedColumnOptions = {
|
||||
@@ -105,9 +122,9 @@ var defaultStackedColumnOptions = {
|
||||
animation: false,
|
||||
scaleFontSize: 10,
|
||||
responsive: false,
|
||||
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
|
||||
scaleLabel: "<%= accounting.formatMoney(value) %>",
|
||||
tooltipFillColor: "rgba(0,0,0,0.5)",
|
||||
multiTooltipTemplate: "<%=datasetLabel%>: " + currencySymbol + " <%= Number(value).toFixed(2).replace('.', ',') %>"
|
||||
multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>"
|
||||
|
||||
};
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @version: 2.0.8
|
||||
* @version: 2.0.11
|
||||
* @author: Dan Grossman http://www.dangrossman.info/
|
||||
* @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved.
|
||||
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -53,6 +53,7 @@
|
||||
this.timePickerIncrement = 1;
|
||||
this.timePickerSeconds = false;
|
||||
this.linkedCalendars = true;
|
||||
this.autoUpdateInput = true;
|
||||
this.ranges = {};
|
||||
|
||||
this.opens = 'right';
|
||||
@@ -245,9 +246,15 @@
|
||||
if (typeof options.autoApply === 'boolean')
|
||||
this.autoApply = options.autoApply;
|
||||
|
||||
if (typeof options.autoUpdateInput === 'boolean')
|
||||
this.autoUpdateInput = options.autoUpdateInput;
|
||||
|
||||
if (typeof options.linkedCalendars === 'boolean')
|
||||
this.linkedCalendars = options.linkedCalendars;
|
||||
|
||||
if (typeof options.isInvalidDate === 'function')
|
||||
this.isInvalidDate = options.isInvalidDate;
|
||||
|
||||
// update day names order to firstDay
|
||||
if (this.locale.firstDay != 0) {
|
||||
var iterator = this.locale.firstDay;
|
||||
@@ -332,7 +339,6 @@
|
||||
}
|
||||
list += '<li>' + this.locale.customRangeLabel + '</li>';
|
||||
list += '</ul>';
|
||||
this.container.find('.ranges ul').remove();
|
||||
this.container.find('.ranges').prepend(list);
|
||||
}
|
||||
|
||||
@@ -371,7 +377,15 @@
|
||||
this.container.addClass('show-calendar');
|
||||
}
|
||||
|
||||
this.container.removeClass('opensleft opensright').addClass('opens' + this.opens);
|
||||
this.container.addClass('opens' + this.opens);
|
||||
|
||||
//swap the position of the predefined ranges if opens right
|
||||
if (typeof options.ranges !== 'undefined' && this.opens == 'right') {
|
||||
var ranges = this.container.find('.ranges');
|
||||
var html = ranges.clone();
|
||||
ranges.remove();
|
||||
this.container.find('.calendar.left').parent().prepend(html);
|
||||
}
|
||||
|
||||
//apply CSS classes and labels to buttons
|
||||
this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses);
|
||||
@@ -396,8 +410,8 @@
|
||||
.on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
|
||||
.on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))
|
||||
.on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this))
|
||||
.on('keyup.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))
|
||||
.on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.updateFormInputs, this));
|
||||
//.on('keyup.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))
|
||||
.on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this));
|
||||
|
||||
this.container.find('.ranges')
|
||||
.on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
|
||||
@@ -410,7 +424,7 @@
|
||||
this.element.on({
|
||||
'click.daterangepicker': $.proxy(this.show, this),
|
||||
'focus.daterangepicker': $.proxy(this.show, this),
|
||||
'keyup.daterangepicker': $.proxy(this.controlChanged, this),
|
||||
'keyup.daterangepicker': $.proxy(this.elementChanged, this),
|
||||
'keydown.daterangepicker': $.proxy(this.keydown, this)
|
||||
});
|
||||
} else {
|
||||
@@ -421,10 +435,10 @@
|
||||
// if attached to a text input, set the initial value
|
||||
//
|
||||
|
||||
if (this.element.is('input') && !this.singleDatePicker) {
|
||||
if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
|
||||
this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
|
||||
this.element.trigger('change');
|
||||
} else if (this.element.is('input')) {
|
||||
} else if (this.element.is('input') && this.autoUpdateInput) {
|
||||
this.element.val(this.startDate.format(this.locale.format));
|
||||
this.element.trigger('change');
|
||||
}
|
||||
@@ -454,6 +468,9 @@
|
||||
if (this.maxDate && this.startDate.isAfter(this.maxDate))
|
||||
this.startDate = this.maxDate;
|
||||
|
||||
if (!this.isShowing)
|
||||
this.updateElement();
|
||||
|
||||
this.updateMonthsInView();
|
||||
},
|
||||
|
||||
@@ -479,9 +496,16 @@
|
||||
if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate))
|
||||
this.endDate = this.startDate.clone().add(this.dateLimit);
|
||||
|
||||
if (!this.isShowing)
|
||||
this.updateElement();
|
||||
|
||||
this.updateMonthsInView();
|
||||
},
|
||||
|
||||
isInvalidDate: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
updateView: function() {
|
||||
if (this.timePicker) {
|
||||
this.renderTimePicker('left');
|
||||
@@ -506,12 +530,23 @@
|
||||
|
||||
updateMonthsInView: function() {
|
||||
if (this.endDate) {
|
||||
|
||||
//if both dates are visible already, do nothing
|
||||
if (this.leftCalendar.month && this.rightCalendar.month &&
|
||||
(this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
|
||||
&&
|
||||
(this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.leftCalendar.month = this.startDate.clone().date(2);
|
||||
if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) {
|
||||
this.rightCalendar.month = this.endDate.clone().date(2);
|
||||
} else {
|
||||
this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) {
|
||||
this.leftCalendar.month = this.startDate.clone().date(2);
|
||||
@@ -622,7 +657,7 @@
|
||||
startDay = daysInLastMonth - 6;
|
||||
|
||||
// Possible patch for issue #626 https://github.com/dangrossman/bootstrap-daterangepicker/issues/626
|
||||
var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]); // .utcOffset(this.timeZone);
|
||||
var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]).utcOffset(this.timeZone); // .utcOffset(this.timeZone);
|
||||
|
||||
var col, row;
|
||||
for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
|
||||
@@ -769,6 +804,10 @@
|
||||
if (maxDate && calendar[row][col].isAfter(maxDate, 'day'))
|
||||
classes.push('off', 'disabled');
|
||||
|
||||
//don't allow selection of date if a custom function decides it's invalid
|
||||
if (this.isInvalidDate(calendar[row][col]))
|
||||
classes.push('off', 'disabled');
|
||||
|
||||
//highlight the currently selected start date
|
||||
if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD'))
|
||||
classes.push('active', 'start-date');
|
||||
@@ -805,7 +844,7 @@
|
||||
|
||||
renderTimePicker: function(side) {
|
||||
|
||||
var selected, minDate, maxDate = this.maxDate;
|
||||
var html, selected, minDate, maxDate = this.maxDate;
|
||||
|
||||
if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate)))
|
||||
maxDate = this.startDate.clone().add(this.dateLimit);
|
||||
@@ -936,6 +975,11 @@
|
||||
},
|
||||
|
||||
updateFormInputs: function() {
|
||||
|
||||
//ignore mouse movements while an above-calendar text input has focus
|
||||
if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
|
||||
return;
|
||||
|
||||
this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format));
|
||||
if (this.endDate)
|
||||
this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format));
|
||||
@@ -945,6 +989,7 @@
|
||||
} else {
|
||||
this.container.find('button.applyBtn').attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
move: function() {
|
||||
@@ -1044,13 +1089,7 @@
|
||||
this.callback(this.startDate, this.endDate, this.chosenLabel);
|
||||
|
||||
//if picker is attached to a text input, update it
|
||||
if (this.element.is('input') && !this.singleDatePicker) {
|
||||
this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
|
||||
this.element.trigger('change');
|
||||
} else if (this.element.is('input')) {
|
||||
this.element.val(this.startDate.format(this.locale.format));
|
||||
this.element.trigger('change');
|
||||
}
|
||||
this.updateElement();
|
||||
|
||||
$(document).off('.daterangepicker');
|
||||
this.container.hide();
|
||||
@@ -1092,6 +1131,11 @@
|
||||
},
|
||||
|
||||
hoverRange: function(e) {
|
||||
|
||||
//ignore mouse movements while an above-calendar text input has focus
|
||||
if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
|
||||
return;
|
||||
|
||||
var label = e.target.innerHTML;
|
||||
if (label == this.locale.customRangeLabel) {
|
||||
this.updateView();
|
||||
@@ -1100,6 +1144,7 @@
|
||||
this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format));
|
||||
this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
clickRange: function(e) {
|
||||
@@ -1148,6 +1193,10 @@
|
||||
|
||||
hoverDate: function(e) {
|
||||
|
||||
//ignore mouse movements while an above-calendar text input has focus
|
||||
if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
|
||||
return;
|
||||
|
||||
//ignore dates that can't be selected
|
||||
if (!$(e.target).hasClass('available')) return;
|
||||
|
||||
@@ -1170,7 +1219,7 @@
|
||||
var startDate = this.startDate;
|
||||
if (!this.endDate) {
|
||||
this.container.find('.calendar td').each(function(index, el) {
|
||||
|
||||
|
||||
//skip week numbers, only look at dates
|
||||
if ($(el).hasClass('week')) return;
|
||||
|
||||
@@ -1355,8 +1404,8 @@
|
||||
|
||||
formInputsChanged: function(e) {
|
||||
var isRight = $(e.target).closest('.calendar').hasClass('right');
|
||||
var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format);
|
||||
var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format);
|
||||
var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format).utcOffset(this.timeZone);
|
||||
var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format).utcOffset(this.timeZone);
|
||||
|
||||
if (start.isValid() && end.isValid()) {
|
||||
|
||||
@@ -1381,7 +1430,7 @@
|
||||
}
|
||||
},
|
||||
|
||||
controlChanged: function() {
|
||||
elementChanged: function() {
|
||||
if (!this.element.is('input')) return;
|
||||
if (!this.element.val().length) return;
|
||||
|
||||
@@ -1411,6 +1460,16 @@
|
||||
}
|
||||
},
|
||||
|
||||
updateElement: function() {
|
||||
if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
|
||||
this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
|
||||
this.element.trigger('change');
|
||||
} else if (this.element.is('input') && this.autoUpdateInput) {
|
||||
this.element.val(this.startDate.format(this.locale.format));
|
||||
this.element.trigger('change');
|
||||
}
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.container.remove();
|
||||
this.element.off('.daterangepicker');
|
||||
@@ -1429,4 +1488,4 @@
|
||||
return this;
|
||||
};
|
||||
|
||||
}));
|
||||
}));
|
@@ -1,36 +1,48 @@
|
||||
/* globals token, start, end, dateRangeURL, everything, firstDate, moment, currentMonthName, $, previousMonthName, nextMonthName, applyLabel, cancelLabel, toLabel, customRangeLabel, fromLabel, */
|
||||
/* globals token, dateRangeConfig, $, */
|
||||
$(function () {
|
||||
"use strict";
|
||||
$('.currencySelect').click(currencySelect);
|
||||
|
||||
// when you click on a currency, this happens:
|
||||
$('.currency-option').click(currencySelect);
|
||||
|
||||
var 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(
|
||||
// range for the current month:
|
||||
ranges[dateRangeConfig.currentMonth] = [moment().startOf('month'), moment().endOf('month')];
|
||||
|
||||
// range for the previous month:
|
||||
ranges[dateRangeConfig.previousMonth] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
|
||||
|
||||
// range for the next month:
|
||||
ranges[dateRangeConfig.nextMonth] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
|
||||
|
||||
// range for everything:
|
||||
ranges[dateRangeConfig.everything] = [dateRangeConfig.firstDate, moment()];
|
||||
|
||||
|
||||
// build the data range:
|
||||
$('#daterange').text(dateRangeConfig.linkTitle).daterangepicker(
|
||||
{
|
||||
ranges: ranges,
|
||||
opens: 'left',
|
||||
locale: {
|
||||
applyLabel: applyLabel,
|
||||
cancelLabel: cancelLabel,
|
||||
fromLabel: fromLabel,
|
||||
toLabel: toLabel,
|
||||
applyLabel: dateRangeConfig.applyLabel,
|
||||
cancelLabel: dateRangeConfig.cancelLabel,
|
||||
fromLabel: dateRangeConfig.fromLabel,
|
||||
toLabel: dateRangeConfig.toLabel,
|
||||
weekLabel: 'W',
|
||||
customRangeLabel: customRangeLabel,
|
||||
customRangeLabel: dateRangeConfig.customRangeLabel,
|
||||
daysOfWeek: moment.weekdaysMin(),
|
||||
monthNames: moment.monthsShort(),
|
||||
firstDay: moment.localeData()._week.dow
|
||||
},
|
||||
format: 'DD-MM-YYYY',
|
||||
startDate: start,
|
||||
endDate: end
|
||||
format: 'YYYY-MM-DD',
|
||||
startDate: dateRangeConfig.startDate,
|
||||
endDate: dateRangeConfig.endDate
|
||||
},
|
||||
function (start, end, label) {
|
||||
|
||||
// send post.
|
||||
$.post(dateRangeURL, {
|
||||
$.post(dateRangeConfig.URL, {
|
||||
start: start.format('YYYY-MM-DD'),
|
||||
end: end.format('YYYY-MM-DD'),
|
||||
label: label,
|
||||
@@ -52,21 +64,51 @@ $(function () {
|
||||
|
||||
function currencySelect(e) {
|
||||
"use strict";
|
||||
var target = $(e.target);
|
||||
// clicked on
|
||||
var target = $(e.target); // target is the <A> tag.
|
||||
|
||||
// name of the field in question:
|
||||
var name = target.data('name');
|
||||
|
||||
// id of menu button (used later on):
|
||||
var menuID = 'currency_dropdown_' + name;
|
||||
|
||||
// the hidden input with the actual value of the selected currency:
|
||||
var hiddenInputName = 'amount_currency_id_' + target.data('name');
|
||||
|
||||
// span with the current selection (next to the caret):
|
||||
var spanId = 'currency_select_symbol_' + target.data('name');
|
||||
|
||||
// the selected currency symbol:
|
||||
var symbol = target.data('symbol');
|
||||
var code = target.data('code');
|
||||
|
||||
// id of the selected currency.
|
||||
var id = target.data('id');
|
||||
var fieldType = target.data('field');
|
||||
var menu = $('.' + fieldType + 'CurrencyDropdown');
|
||||
|
||||
var symbolHolder = $('#' + fieldType + 'CurrentSymbol');
|
||||
symbolHolder.text(symbol);
|
||||
$('input[name="' + fieldType + '_currency_id"]').val(id);
|
||||
// update the hidden input:
|
||||
$('input[name="' + hiddenInputName + '"]').val(id);
|
||||
|
||||
// close dropdown (hack hack)
|
||||
menu.click();
|
||||
// update the symbol:
|
||||
$('#' + spanId).text(symbol);
|
||||
|
||||
// close the menu (hack hack)
|
||||
$('#' + menuID).click();
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
//var code = target.data('code');
|
||||
//var fieldType = target.data('field');
|
||||
//var menu = $('.' + fieldType + 'CurrencyDropdown');
|
||||
//
|
||||
//var symbolHolder = $('#' + fieldType + 'CurrentSymbol');
|
||||
//symbolHolder.text(symbol);
|
||||
//$('input[name="' + fieldType + '_currency_id"]').val(id);
|
||||
//
|
||||
// close dropdown (hack hack)
|
||||
//menu.click();
|
||||
|
||||
|
||||
//return false;
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ function getBoxAmounts() {
|
||||
var boxes = ['in', 'out', 'bills-unpaid', 'bills-paid'];
|
||||
for (var x in boxes) {
|
||||
var box = boxes[x];
|
||||
$.getJSON('/json/box/' + box).success(putData).fail(failData);
|
||||
$.getJSON('json/box/' + box).success(putData).fail(failData);
|
||||
}
|
||||
}
|
||||
|
||||
|
6
public/js/moment.min.js
vendored
Normal file → Executable file
6
public/js/moment.min.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
@@ -1,15 +1,12 @@
|
||||
/* globals $, lineChart, token, piggyBankID */
|
||||
|
||||
// Return a helper with preserved width of cells
|
||||
var fixHelper = function (e, tr) {
|
||||
var fixHelper = function (e, ui) {
|
||||
"use strict";
|
||||
var $originals = tr.children();
|
||||
var $helper = tr.clone();
|
||||
$helper.children().each(function (index) {
|
||||
// Set helper cell sizes to match the original sizes
|
||||
$(this).width($originals.eq(index).width());
|
||||
ui.children().each(function () {
|
||||
$(this).width($(this).width());
|
||||
});
|
||||
return $helper;
|
||||
return ui;
|
||||
};
|
||||
|
||||
$(function () {
|
||||
|
@@ -1,93 +0,0 @@
|
||||
/* globals google, expenseRestShow:true, incomeRestShow:true, year, shared, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
});
|
||||
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
|
||||
columnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart');
|
||||
columnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart');
|
||||
}
|
||||
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
|
||||
stackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
|
||||
stackedColumnChart('chart/category/spent-in-year/' + year + shared, 'categories-spent-in-year');
|
||||
stackedColumnChart('chart/category/earned-in-year/' + year + shared, 'categories-earned-in-year');
|
||||
}
|
||||
if (typeof lineChart !== 'undefined' && typeof month !== 'undefined') {
|
||||
lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
$('.openModal').on('click', openModal);
|
||||
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
});
|
64
public/js/reports/default/month.js
Normal file
64
public/js/reports/default/month.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
});
|
||||
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
|
||||
// month view:
|
||||
// draw account chart
|
||||
lineChart('/chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart');
|
||||
}
|
||||
|
||||
|
||||
function showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
155
public/js/reports/default/multi-year.js
Normal file
155
public/js/reports/default/multi-year.js
Normal file
@@ -0,0 +1,155 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
});
|
||||
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
|
||||
// income and expense over multi year:
|
||||
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
|
||||
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
|
||||
|
||||
|
||||
$.each($('.account-chart'), function (i, v) {
|
||||
var holder = $(v);
|
||||
console.log('Will draw chart for account #' + holder.data('id'));
|
||||
});
|
||||
|
||||
// draw budget chart based on selected budgets:
|
||||
$('.budget-checkbox').on('change', updateBudgetChart);
|
||||
selectBudgetsByCookie();
|
||||
updateBudgetChart();
|
||||
|
||||
// draw category chart based on selected budgets:
|
||||
$('.category-checkbox').on('change', updateCategoryChart);
|
||||
selectCategoriesByCookie();
|
||||
updateCategoryChart();
|
||||
}
|
||||
|
||||
function selectBudgetsByCookie() {
|
||||
"use strict";
|
||||
var cookie = readCookie('multi-year-budgets');
|
||||
if (cookie !== null) {
|
||||
var cookieArray = cookie.split(',');
|
||||
for (var x in cookieArray) {
|
||||
var budgetId = cookieArray[x];
|
||||
$('.budget-checkbox[value="' + budgetId + '"').prop('checked', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectCategoriesByCookie() {
|
||||
"use strict";
|
||||
var cookie = readCookie('multi-year-categories');
|
||||
if (cookie !== null) {
|
||||
var cookieArray = cookie.split(',');
|
||||
for (var x in cookieArray) {
|
||||
var categoryId = cookieArray[x];
|
||||
$('.category-checkbox[value="' + categoryId + '"').prop('checked', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateBudgetChart() {
|
||||
"use strict";
|
||||
console.log('will update budget chart.');
|
||||
// get all budget ids:
|
||||
var budgets = [];
|
||||
$.each($('.budget-checkbox'), function (i, v) {
|
||||
var current = $(v);
|
||||
if (current.prop('checked')) {
|
||||
budgets.push(current.val());
|
||||
}
|
||||
});
|
||||
|
||||
if (budgets.length > 0) {
|
||||
|
||||
var budgetIds = budgets.join(',');
|
||||
|
||||
// remove old chart:
|
||||
$('#budgets-chart').replaceWith('<canvas id="budgets-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>');
|
||||
|
||||
// hide message:
|
||||
$('#budgets-chart-message').hide();
|
||||
|
||||
// draw chart. Redraw when exists? Not sure if we support that.
|
||||
columnChart('chart/budget/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + budgetIds, 'budgets-chart');
|
||||
createCookie('multi-year-budgets', budgets, 365);
|
||||
} else {
|
||||
// hide canvas, show message:
|
||||
$('#budgets-chart-message').show();
|
||||
$('#budgets-chart').hide();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateCategoryChart() {
|
||||
"use strict";
|
||||
console.log('will update category chart.');
|
||||
// get all category ids:
|
||||
var categories = [];
|
||||
$.each($('.category-checkbox'), function (i, v) {
|
||||
var current = $(v);
|
||||
if (current.prop('checked')) {
|
||||
categories.push(current.val());
|
||||
}
|
||||
});
|
||||
|
||||
if (categories.length > 0) {
|
||||
|
||||
var categoryIds = categories.join(',');
|
||||
|
||||
// remove old chart:
|
||||
$('#categories-chart').replaceWith('<canvas id="categories-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>');
|
||||
|
||||
// hide message:
|
||||
$('#categories-chart-message').hide();
|
||||
|
||||
// draw chart. Redraw when exists? Not sure if we support that.
|
||||
columnChart('chart/category/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + categoryIds, 'categories-chart');
|
||||
createCookie('multi-year-categories', categories, 365);
|
||||
} else {
|
||||
// hide canvas, show message:
|
||||
$('#categories-chart-message').show();
|
||||
$('#categories-chart').hide();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createCookie(name, value, days) {
|
||||
"use strict";
|
||||
var expires;
|
||||
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toGMTString();
|
||||
} else {
|
||||
expires = "";
|
||||
}
|
||||
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
"use strict";
|
||||
var nameEQ = encodeURIComponent(name) + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function eraseCookie(name) {
|
||||
createCookie(name, "", -1);
|
||||
}
|
208
public/js/reports/default/reports.js
Normal file
208
public/js/reports/default/reports.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
if ($('#inputDateRange').length > 0) {
|
||||
|
||||
picker = $('#inputDateRange').daterangepicker(
|
||||
{
|
||||
locale: {
|
||||
format: 'YYYY-MM-DD',
|
||||
firstDay: 1,
|
||||
},
|
||||
minDate: minDate,
|
||||
drops: 'up',
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// set values from cookies, if any:
|
||||
if (readCookie('report-type') !== null) {
|
||||
$('select[name="report_type"]').val(readCookie('report-type'));
|
||||
}
|
||||
|
||||
if ((readCookie('report-accounts') !== null)) {
|
||||
var arr = readCookie('report-accounts').split(',');
|
||||
arr.forEach(function (val) {
|
||||
$('input[type="checkbox"][value="' + val + '"]').prop('checked', true);
|
||||
});
|
||||
}
|
||||
|
||||
// set date:
|
||||
var startStr = readCookie('report-start');
|
||||
var endStr = readCookie('report-end');
|
||||
if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) {
|
||||
var startDate = moment(startStr, "YYYYMMDD");
|
||||
var endDate = moment(endStr, "YYYYMMDD");
|
||||
var datePicker = $('#inputDateRange').data('daterangepicker');
|
||||
datePicker.setStartDate(startDate);
|
||||
datePicker.setEndDate(endDate);
|
||||
}
|
||||
}
|
||||
|
||||
$('.openModal').on('click', openModal);
|
||||
|
||||
$('.date-select').on('click', preSelectDate);
|
||||
|
||||
$('#report-form').on('submit', catchSubmit);
|
||||
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
});
|
||||
|
||||
function catchSubmit() {
|
||||
"use strict";
|
||||
// default;20141201;20141231;4;5
|
||||
// report name:
|
||||
var url = '' + $('select[name="report_type"]').val() + '/';
|
||||
|
||||
// date, processed:
|
||||
var picker = $('#inputDateRange').data('daterangepicker');
|
||||
url += moment(picker.startDate).format("YYYYMMDD") + '/';
|
||||
url += moment(picker.endDate).format("YYYYMMDD") + '/';
|
||||
|
||||
// all account ids:
|
||||
var count = 0;
|
||||
var accounts = [];
|
||||
$.each($('.account-checkbox'), function (i, v) {
|
||||
var c = $(v);
|
||||
if (c.prop('checked')) {
|
||||
url += c.val() + ';';
|
||||
accounts.push(c.val());
|
||||
count++;
|
||||
}
|
||||
});
|
||||
if (count > 0) {
|
||||
// set cookie to remember choices.
|
||||
createCookie('report-type', $('select[name="report_type"]').val(), 365);
|
||||
createCookie('report-accounts', accounts, 365);
|
||||
createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365);
|
||||
createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365);
|
||||
|
||||
window.location.href = reportURL + "/" + url;
|
||||
}
|
||||
//console.log(url);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function preSelectDate(e) {
|
||||
"use strict";
|
||||
var link = $(e.target);
|
||||
var picker = $('#inputDateRange').data('daterangepicker');
|
||||
picker.setStartDate(link.data('start'));
|
||||
picker.setEndDate(link.data('end'));
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
|
||||
|
||||
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
|
||||
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
|
||||
}
|
||||
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
|
||||
stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets');
|
||||
stackedColumnChart('chart/category/spent-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-year');
|
||||
stackedColumnChart('chart/category/earned-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-year');
|
||||
}
|
||||
|
||||
if (typeof lineChart !== 'undefined' && typeof accountIds !== 'undefined') {
|
||||
lineChart('/chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function createCookie(name, value, days) {
|
||||
var expires;
|
||||
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toGMTString();
|
||||
} else {
|
||||
expires = "";
|
||||
}
|
||||
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
var nameEQ = encodeURIComponent(name) + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function eraseCookie(name) {
|
||||
createCookie(name, "", -1);
|
||||
}
|
67
public/js/reports/default/year.js
Normal file
67
public/js/reports/default/year.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
});
|
||||
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
|
||||
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
|
||||
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
|
||||
stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets');
|
||||
stackedColumnChart('chart/category/spent-in-period/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-period');
|
||||
stackedColumnChart('chart/category/earned-in-period/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-period');
|
||||
|
||||
}
|
||||
|
||||
|
||||
function showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user