mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-18 10:16:49 +00:00
Compare commits
338 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
956019ff4a | ||
|
8279cf0e88 | ||
|
43c32abfe8 | ||
|
0e66939408 | ||
|
22d2a523fb | ||
|
bc825a8603 | ||
|
c9cfda34a1 | ||
|
e8dfbff73f | ||
|
62e41f1997 | ||
|
8c9f90f1b4 | ||
|
1453a78e49 | ||
|
7efaf51595 | ||
|
6bc6674ab1 | ||
|
d6c7ff0ccb | ||
|
28f655dba1 | ||
|
6a3de12894 | ||
|
c7940333ec | ||
|
8860378757 | ||
|
728fda0116 | ||
|
0c72e1831f | ||
|
7da21976ec | ||
|
b739859c64 | ||
|
d25665f843 | ||
|
1f41f7bd0f | ||
|
dd8638ca98 | ||
|
4ba9ff05b0 | ||
|
618aad5432 | ||
|
e46fc7501e | ||
|
7b91e98d46 | ||
|
85be218f92 | ||
|
71206e395e | ||
|
9b2d2e16b0 | ||
|
5ae01b382e | ||
|
d92a0753a6 | ||
|
f937a74507 | ||
|
c3584ad20c | ||
|
c049d5cfa6 | ||
|
6c9990e0be | ||
|
b34e4cd31b | ||
|
7852b8a785 | ||
|
6eeb60db5c | ||
|
d076cfc08f | ||
|
68a93ff97c | ||
|
295dcb4f65 | ||
|
d9849f60c0 | ||
|
7ebb68e36c | ||
|
f029f7607b | ||
|
2ba5733ebc | ||
|
3fe1d1d368 | ||
|
438c372583 | ||
|
797aa4858e | ||
|
8c858cd066 | ||
|
85aebd39b9 | ||
|
9a5a037424 | ||
|
7d557cbf91 | ||
|
dbbc85a576 | ||
|
eb78cf20c2 | ||
|
4a99399952 | ||
|
6075d75ee2 | ||
|
f4c56fee66 | ||
|
04c59304da | ||
|
4b3c31a11a | ||
|
14576d2753 | ||
|
72ca1c20c7 | ||
|
93645819b8 | ||
|
39468f871b | ||
|
faa47781d2 | ||
|
2c196bab6d | ||
|
9ae71075ef | ||
|
0013cdfa78 | ||
|
52f3f64f7b | ||
|
670fa77dd7 | ||
|
8baea2feb9 | ||
|
c56f937521 | ||
|
0b613c3b8c | ||
|
78f297e18f | ||
|
bd8a285d6d | ||
|
b44602fd55 | ||
|
41238903e1 | ||
|
a0c88e9b33 | ||
|
5d184aa53e | ||
|
9f9bf86a9f | ||
|
53af9345eb | ||
|
da6bcf04df | ||
|
ec4ec1a147 | ||
|
350e0b08b1 | ||
|
9340ca09e6 | ||
|
a1cef5c339 | ||
|
94875adb6c | ||
|
75a524c656 | ||
|
e1e94a788c | ||
|
8417f45d02 | ||
|
685310a368 | ||
|
45e7a4576a | ||
|
f8c5c15655 | ||
|
26190524f4 | ||
|
5d901a7ecb | ||
|
929d8b3adc | ||
|
cd6e37b9cb | ||
|
b647386541 | ||
|
174fd88435 | ||
|
cc9211b7c2 | ||
|
a9795fb095 | ||
|
8554aae21e | ||
|
5a2ef36f2a | ||
|
01e3f91ece | ||
|
7ec9c090cc | ||
|
b057d69f8e | ||
|
ff4e1838bc | ||
|
e4ecd0b7ff | ||
|
1ba35f73e1 | ||
|
240f3c126b | ||
|
23925a0076 | ||
|
50b72cf229 | ||
|
ee6b72afa5 | ||
|
781621960d | ||
|
e15ea04186 | ||
|
73f0cc705b | ||
|
0c072c7d51 | ||
|
884bed85a1 | ||
|
a319264428 | ||
|
6506e70a91 | ||
|
e6fcb19db7 | ||
|
a3fba53182 | ||
|
f8438dd9d3 | ||
|
028a0dcae1 | ||
|
b4a06b5bbd | ||
|
4fe1a5d527 | ||
|
865930c5b2 | ||
|
96b4e2c196 | ||
|
b7e7c7e9e2 | ||
|
7771669db7 | ||
|
ef59eb6e1f | ||
|
a14b2bc5a7 | ||
|
47349589cb | ||
|
79afe84f30 | ||
|
171187b25c | ||
|
7f1b661e61 | ||
|
2c2a3a5475 | ||
|
1677ca9619 | ||
|
204da3e846 | ||
|
f36d423b1e | ||
|
79c7280046 | ||
|
e10fc4a854 | ||
|
5088df103f | ||
|
13b96f6136 | ||
|
757662ca4b | ||
|
4ef324cf24 | ||
|
cb02e0ee71 | ||
|
ec3a90688e | ||
|
6dcecdcc64 | ||
|
25d917240d | ||
|
0906915a87 | ||
|
9c92a94177 | ||
|
1b125ecd22 | ||
|
25a2bcd76e | ||
|
b2e09f4240 | ||
|
560165850f | ||
|
0bb07e1eeb | ||
|
0c0f2109f6 | ||
|
f546670342 | ||
|
eecb6c6679 | ||
|
750b9d8038 | ||
|
9ce28fdd2e | ||
|
07af64ada5 | ||
|
a0ab0ec902 | ||
|
752f8582aa | ||
|
4d0eed8c9b | ||
|
0d7a8305f3 | ||
|
2e8c0ec537 | ||
|
3155ec9e2b | ||
|
7bbca7f6a8 | ||
|
f7579db4ad | ||
|
2f47c58df5 | ||
|
7e7ac264d2 | ||
|
98d6c90e90 | ||
|
da49afa37b | ||
|
64364c3e77 | ||
|
b6f0fd1949 | ||
|
0663a18f3a | ||
|
c5928897eb | ||
|
570373e875 | ||
|
228afc2eea | ||
|
6b61621d6a | ||
|
424133fa83 | ||
|
02e30c1fcc | ||
|
e17a9d559b | ||
|
36744377f6 | ||
|
7c479f73c0 | ||
|
85b3c4683b | ||
|
c5d2fabfec | ||
|
a294f757ff | ||
|
04515da0bc | ||
|
6d60d64a82 | ||
|
32b5a84a0c | ||
|
4b42ef0db8 | ||
|
abc7b9912d | ||
|
727717931a | ||
|
1d66b16468 | ||
|
b918429c43 | ||
|
888273d4a0 | ||
|
31b5d5ba72 | ||
|
b148d0868e | ||
|
c1491383a8 | ||
|
5f07918682 | ||
|
8de6bd7ceb | ||
|
5d4f1bc76d | ||
|
8583b574ac | ||
|
3600e1b5e7 | ||
|
fe57648349 | ||
|
f0e0cdb49b | ||
|
cf69333c6d | ||
|
3f7e16d270 | ||
|
a63f1638f4 | ||
|
8ec2a3a391 | ||
|
d875f0e580 | ||
|
729534b4f3 | ||
|
bd6a56a55e | ||
|
96976db350 | ||
|
8735190461 | ||
|
709a14e5c9 | ||
|
c89d2a52b5 | ||
|
6084d16ea8 | ||
|
1688fdb786 | ||
|
6cfb5ee2e9 | ||
|
2db560ed7d | ||
|
45567cdf65 | ||
|
508ad5157b | ||
|
8fc41e0226 | ||
|
a08dfe1e3c | ||
|
49cc8a97a3 | ||
|
5b8583dd2b | ||
|
f653bc5f6e | ||
|
a6a9794fc7 | ||
|
fdb8f61e37 | ||
|
69422cc796 | ||
|
5f9a9bc89a | ||
|
4d0d05e0f8 | ||
|
0113fedbd4 | ||
|
a7d35cd1c3 | ||
|
43600fe6cb | ||
|
0b5e25960f | ||
|
0c8a1b51e9 | ||
|
cb49f5e8d8 | ||
|
a0e3088ca3 | ||
|
b86be6f52f | ||
|
4c573e1300 | ||
|
1a3d77f117 | ||
|
2656da13b1 | ||
|
d272ebd95c | ||
|
7612f1f91a | ||
|
22a2fe3f61 | ||
|
1ebb59b352 | ||
|
77e2cf40df | ||
|
0edffd8ea1 | ||
|
ee6e047596 | ||
|
bd55636b3f | ||
|
b24e97a449 | ||
|
d45355fc3f | ||
|
b2206f640a | ||
|
962cad33e2 | ||
|
d65214b75a | ||
|
7b4c151df5 | ||
|
28d6f51961 | ||
|
d2f9deb82b | ||
|
d9b05b5f59 | ||
|
a8f4b33c57 | ||
|
ee849ea12f | ||
|
f9d3cf231f | ||
|
0713ca7709 | ||
|
1e2124c5ed | ||
|
37435da459 | ||
|
05dbd30bbd | ||
|
4b947638a7 | ||
|
3d113b9aae | ||
|
d1b3681bf3 | ||
|
9dd4b07314 | ||
|
3814f0f3c3 | ||
|
b1e907fae9 | ||
|
5c03a1a9c8 | ||
|
20ac07a386 | ||
|
13e1292bb7 | ||
|
8e542531b3 | ||
|
43afdb021a | ||
|
aeca2ef3b2 | ||
|
205a593721 | ||
|
46649fe228 | ||
|
adb97fcb05 | ||
|
98160e9b63 | ||
|
9c5d192d90 | ||
|
47bebb614e | ||
|
5f7fb77db2 | ||
|
1d15bc0b10 | ||
|
7bc4c6d115 | ||
|
45973a53f5 | ||
|
8e5e3de8b0 | ||
|
8738cd4b04 | ||
|
24a7dac235 | ||
|
a3088f6806 | ||
|
72f7b5f3ea | ||
|
a636c508a2 | ||
|
599db95f73 | ||
|
f5f78ab79b | ||
|
9af9383c29 | ||
|
4b97b86c09 | ||
|
11fb46830c | ||
|
e8dec6d95c | ||
|
bb4ee7470d | ||
|
2e8071db9e | ||
|
4d2901aa02 | ||
|
37bbfab20a | ||
|
fb9161b82d | ||
|
000c9d8974 | ||
|
878b664930 | ||
|
afe28b5581 | ||
|
4106b2e4c0 | ||
|
e1be4909b9 | ||
|
7a0347c0c2 | ||
|
a7e0e3fc15 | ||
|
5e480eca36 | ||
|
6c8d594df7 | ||
|
e24f5ec9f3 | ||
|
1379c0652e | ||
|
1f87b0bd2d | ||
|
787a437ca4 | ||
|
c0bdb35cb3 | ||
|
4b9cf67413 | ||
|
86ff3be741 | ||
|
8bc8e8d9fe | ||
|
227a12d75d | ||
|
2ddd4314f1 | ||
|
b980b5baea | ||
|
4ba34ab511 | ||
|
5be317d73c | ||
|
af16205965 | ||
|
39917b77c1 | ||
|
124ecb1372 | ||
|
33c0c1bea6 |
@@ -35,8 +35,7 @@ MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
MUST_CONFIRM_ACCOUNT=false
|
||||
|
||||
SEND_ERROR_MESSAGE=true
|
||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||
|
||||
ANALYTICS_ID=
|
||||
|
45
.env.testing
Executable file
45
.env.testing
Executable file
@@ -0,0 +1,45 @@
|
||||
APP_ENV=testing
|
||||
APP_DEBUG=true
|
||||
APP_FORCE_SSL=false
|
||||
APP_FORCE_ROOT=
|
||||
APP_KEY=TestTestTestTestTestTestTestTest
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
COOKIE_PATH="/"
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SECURE=false
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_FROM=changeme@example.com
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
SHOW_INCOMPLETE_TRANSLATIONS=false
|
||||
|
||||
ANALYTICS_ID=
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
PUSHER_KEY=
|
||||
PUSHER_SECRET=
|
||||
PUSHER_APP_ID=
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ result.html
|
||||
test-import.sh
|
||||
test-import-report.txt
|
||||
public/google*.html
|
||||
.env.backup
|
||||
|
18
.travis.yml
Normal file
18
.travis.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
language: php
|
||||
sudo: false
|
||||
php:
|
||||
- '7.0'
|
||||
|
||||
install:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- composer selfupdate
|
||||
- rm composer.lock
|
||||
- composer update --no-scripts
|
||||
- php artisan clear-compiled
|
||||
- php artisan optimize
|
||||
- php artisan env
|
||||
- ./test.sh -r
|
||||
- php artisan env
|
||||
|
||||
script:
|
||||
- phpunit
|
69
CHANGELOG.md
69
CHANGELOG.md
@@ -2,6 +2,75 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [4.2.0] - 2016-11-27
|
||||
### Added
|
||||
- Lots of (empty) tests
|
||||
- Expanded transaction lists (#377)
|
||||
- New charts at account view
|
||||
- First code for #305
|
||||
|
||||
|
||||
### Changed
|
||||
- Updated all email messages.
|
||||
- Made some fonts local
|
||||
|
||||
|
||||
### Deprecated
|
||||
- Initial release.
|
||||
|
||||
### Removed
|
||||
- Initial release.
|
||||
|
||||
### Fixed
|
||||
- Issue #408
|
||||
- Various issues with split journals
|
||||
- Issue #414, thx @zjean
|
||||
- Issue #419, thx @schwalberich
|
||||
- Issue #422, thx @xzaz
|
||||
- Various import bugs, such as #416 (@zjean)
|
||||
|
||||
|
||||
### Security
|
||||
- Initial release.
|
||||
|
||||
|
||||
## [4.1.7] - 2016-11-19
|
||||
### Added
|
||||
- Check for database table presence in console commands.
|
||||
- Category report
|
||||
- Reinstated old test routines.
|
||||
|
||||
|
||||
### Changed
|
||||
- Confirm account setting is no longer in `.env` file.
|
||||
- Titles are now in reverse (current page > parent > firefly iii)
|
||||
- Easier update of language files thanks to Github implementation.
|
||||
- Uniform colours for charts.
|
||||
|
||||
### Fixed
|
||||
- Made all pages more mobile friendly.
|
||||
- Fixed #395 found by @marcoveeneman.
|
||||
- Fixed #398 found by @marcoveeneman.
|
||||
- Fixed #401 found by @marcoveeneman.
|
||||
- Many optimizations.
|
||||
- Updated many libraries.
|
||||
- Various bugs found by myself.
|
||||
|
||||
|
||||
## [4.1.6] - 2016-11-06
|
||||
### Added
|
||||
- New budget table for multi year report.
|
||||
|
||||
### Changed
|
||||
- Greatly expanded help pages and their function.
|
||||
- Built a new transaction collector, which I think was the idea of @roberthorlings originally.
|
||||
- Rebuilt seach engine.
|
||||
|
||||
### Fixed
|
||||
- #375, thanks to @schoentoon which made it impossible to resurrect currencies.
|
||||
- #370 thanks to @ksmolder
|
||||
- #378, thanks to @HomelessAvatar
|
||||
|
||||
## [4.1.5] - 2016-11-01
|
||||
### Changed
|
||||
- Report parts are loaded using AJAX, making a lot of code more simple.
|
||||
|
@@ -1,5 +1,7 @@
|
||||
# Firefly III [](https://secure.php.net/downloads.php#v7.0.4) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
|
||||
|
||||
[](https://travis-ci.org/JC5/firefly-iii)
|
||||
|
||||
## A personal finances manager
|
||||
|
||||
[](https://i.nder.be/hhfv03hp) [](https://i.nder.be/hhmwmqw9)
|
||||
@@ -10,7 +12,7 @@
|
||||
|
||||
## Installation
|
||||
|
||||
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://jc5.github.io/firefly-iii/installation-guide/).
|
||||
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/installation-guide/).
|
||||
|
||||
## More about Firefly III
|
||||
|
||||
@@ -25,6 +27,6 @@ Firefly works on the principle that if you know where you're money is going, you
|
||||
- Firefly has lots of features without becoming fancy or bloated.
|
||||
- If you feel you're missing something you can just ask me and I'll add it!
|
||||
|
||||
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://jc5.github.io/firefly-iii/).
|
||||
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/).
|
||||
|
||||
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
|
||||
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
|
||||
|
@@ -50,9 +50,7 @@ class CreateImport extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
@@ -51,9 +51,7 @@ class Import extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
@@ -66,7 +64,7 @@ class Import extends Command
|
||||
return;
|
||||
}
|
||||
|
||||
$this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type);
|
||||
$this->line(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type));
|
||||
|
||||
$monolog = Log::getMonolog();
|
||||
$handler = new CommandHandler($this);
|
||||
|
86
app/Console/Commands/MoveRepository.php
Normal file
86
app/Console/Commands/MoveRepository.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* MoveRepository.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class MoveRepository
|
||||
*
|
||||
* @package FireflyIII\Console\Commands
|
||||
*/
|
||||
class MoveRepository extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Alerts the user that the Github repository will move, if they are interested to know this.';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly:github-move';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$moveDate = new Carbon('2017-01-01');
|
||||
$final = new Carbon('2017-03-01');
|
||||
$now = new Carbon;
|
||||
|
||||
// display message before 2017-01-01
|
||||
if ($moveDate > $now) {
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line('The Github repository for Firefly III will MOVE');
|
||||
$this->line('This move will be on January 1st 2017');
|
||||
$this->line('');
|
||||
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
|
||||
$this->line('');
|
||||
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
// display message after 2017-01-01 but before 2017-03-01
|
||||
if ($moveDate <= $now && $now <= $final) {
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line('The Github repository for Firefly III has MOVED');
|
||||
$this->line('This move was on January 1st 2017!');
|
||||
$this->line('');
|
||||
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
|
||||
$this->line('');
|
||||
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Log;
|
||||
use Schema;
|
||||
|
||||
/**
|
||||
* Class UpgradeDatabase
|
||||
@@ -65,6 +66,12 @@ class UpgradeDatabase extends Command
|
||||
*/
|
||||
private function setTransactionIdentifier()
|
||||
{
|
||||
// if table does not exist, return false
|
||||
if (!Schema::hasTable('transaction_journals')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$subQuery = TransactionJournal
|
||||
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
@@ -99,7 +106,7 @@ class UpgradeDatabase extends Command
|
||||
} catch (QueryException $e) {
|
||||
Log::error($e->getMessage());
|
||||
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
|
||||
$this->error('This field is required for Firefly III version ' . config('firefly.version') . ' to run.');
|
||||
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
|
||||
$this->error('Please run "php artisan migrate" to add this field to the table.');
|
||||
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
|
||||
break 2;
|
||||
|
@@ -63,21 +63,20 @@ class UpgradeFireflyInstructions extends Command
|
||||
|
||||
}
|
||||
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
|
||||
if (is_null($text)) {
|
||||
$this->line('Thank you for installing Firefly III, v' . $version);
|
||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
||||
$this->info('There are no extra upgrade instructions.');
|
||||
$this->line('Firefly III should be ready for use.');
|
||||
} else {
|
||||
$this->line('Thank you for installing Firefly III, v' . $version);
|
||||
$this->line('If you are upgrading from a previous version,');
|
||||
$this->line('please follow these upgrade instructions carefully:');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
$this->line('');
|
||||
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
|
||||
$this->info(wordwrap($text));
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
$this->line('+------------------------------------------------------------------------------+');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,9 @@ use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Schema;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
@@ -61,16 +63,21 @@ class VerifyDatabase extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// if table does not exist, return false
|
||||
if (!Schema::hasTable('users')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->reportObject('budget');
|
||||
$this->reportObject('category');
|
||||
$this->reportObject('tag');
|
||||
|
||||
// accounts with no transactions.
|
||||
$this->reportAccounts();
|
||||
// budgets with no limits
|
||||
$this->reportBudgetLimits();
|
||||
// budgets with no transactions
|
||||
$this->reportBudgets();
|
||||
// categories with no transactions
|
||||
$this->reportCategories();
|
||||
// tags with no transactions
|
||||
$this->reportTags();
|
||||
|
||||
// sum of transactions is not zero.
|
||||
$this->reportSum();
|
||||
// any deleted transaction journals that have transactions that are NOT deleted:
|
||||
@@ -127,50 +134,11 @@ class VerifyDatabase extends Command
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
|
||||
. '") which has no budget limits.';
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on budgets without any transactions.
|
||||
*/
|
||||
private function reportBudgets()
|
||||
{
|
||||
$set = Budget
|
||||
::leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
|
||||
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
|
||||
->distinct()
|
||||
->whereNull('budget_transaction_journal.budget_id')
|
||||
->whereNull('budgets.deleted_at')
|
||||
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
|
||||
. '") which has no transactions.';
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on categories without any transactions.
|
||||
*/
|
||||
private function reportCategories()
|
||||
{
|
||||
$set = Category
|
||||
::leftJoin('category_transaction_journal', 'categories.id', '=', 'category_transaction_journal.category_id')
|
||||
->leftJoin('users', 'categories.user_id', '=', 'users.id')
|
||||
->distinct()
|
||||
->whereNull('category_transaction_journal.category_id')
|
||||
->whereNull('categories.deleted_at')
|
||||
->get(['categories.id', 'categories.name', 'categories.user_id', 'users.email']);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has category #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
|
||||
. '") which has no transactions.';
|
||||
$line = sprintf(
|
||||
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
|
||||
$entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name)
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
@@ -291,6 +259,39 @@ class VerifyDatabase extends Command
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
private function reportObject(string $name)
|
||||
{
|
||||
$plural = str_plural($name);
|
||||
$class = sprintf('FireflyIII\Models\%s', ucfirst($name));
|
||||
$field = $name == 'tag' ? 'tag' : 'name';
|
||||
$set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id')
|
||||
->leftJoin('users', $plural . '.user_id', '=', 'users.id')
|
||||
->distinct()
|
||||
->whereNull($name . '_transaction_journal.' . $name . '_id')
|
||||
->whereNull($plural . '.deleted_at')
|
||||
->get([$plural . '.id', $plural . '.' . $field . ' as name', $plural . '.user_id', 'users.email']);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
|
||||
$objName = $entry->name;
|
||||
try {
|
||||
$objName = Crypt::decrypt($objName);
|
||||
} catch (DecryptException $e) {
|
||||
// it probably was not encrypted.
|
||||
}
|
||||
|
||||
$line = sprintf(
|
||||
'Notice: User #%d (%s) has %s #%d ("%s") which has no transactions.',
|
||||
$entry->user_id, $entry->email, $name, $entry->id, $objName
|
||||
);
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports for each user when the sum of their transactions is not zero.
|
||||
*/
|
||||
@@ -308,34 +309,12 @@ class VerifyDatabase extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on tags without any transactions.
|
||||
*/
|
||||
private function reportTags()
|
||||
{
|
||||
$set = Tag
|
||||
::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id')
|
||||
->leftJoin('users', 'tags.user_id', '=', 'users.id')
|
||||
->distinct()
|
||||
->whereNull('tag_transaction_journal.tag_id')
|
||||
->whereNull('tags.deleted_at')
|
||||
->get(['tags.id', 'tags.tag', 'tags.user_id', 'users.email']);
|
||||
|
||||
/** @var stdClass $entry */
|
||||
foreach ($set as $entry) {
|
||||
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has tag #' . $entry->id . ' ("' . $entry->tag
|
||||
. '") which has no transactions.';
|
||||
$this->line($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports on deleted transactions that are connected to a not deleted journal.
|
||||
*/
|
||||
private function reportTransactions()
|
||||
{
|
||||
$set = Transaction
|
||||
::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
$set = Transaction::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->whereNotNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->get(
|
||||
|
@@ -16,6 +16,7 @@ namespace FireflyIII\Console;
|
||||
use FireflyIII\Console\Commands\CreateImport;
|
||||
use FireflyIII\Console\Commands\EncryptFile;
|
||||
use FireflyIII\Console\Commands\Import;
|
||||
use FireflyIII\Console\Commands\MoveRepository;
|
||||
use FireflyIII\Console\Commands\ScanAttachments;
|
||||
use FireflyIII\Console\Commands\UpgradeDatabase;
|
||||
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
|
||||
@@ -63,7 +64,7 @@ class Kernel extends ConsoleKernel
|
||||
EncryptFile::class,
|
||||
ScanAttachments::class,
|
||||
UpgradeDatabase::class,
|
||||
|
||||
MoveRepository::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
46
app/Events/RequestedNewPassword.php
Normal file
46
app/Events/RequestedNewPassword.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* RequestedNewPassword.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RequestedNewPassword
|
||||
*
|
||||
* @package FireflyIII\Events
|
||||
*/
|
||||
class RequestedNewPassword extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public $ipAddress;
|
||||
public $token;
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a users tries to reset his or her password.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $token
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(User $user, string $token, string $ipAddress)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->token = $token;
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
|
||||
}
|
@@ -77,7 +77,8 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
if ($exception instanceof FireflyException || $exception instanceof ErrorException) {
|
||||
$doMailError = env('SEND_ERROR_MESSAGE', true);
|
||||
if (($exception instanceof FireflyException || $exception instanceof ErrorException) && $doMailError) {
|
||||
$userData = [
|
||||
'id' => 0,
|
||||
'email' => 'unknown@example.com',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalCollector.php
|
||||
* JournalExportCollector.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
@@ -21,11 +21,11 @@ use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class JournalCollector
|
||||
* Class JournalExportCollector
|
||||
*
|
||||
* @package FireflyIII\Export\Collector
|
||||
*/
|
||||
class JournalCollector extends BasicCollector implements CollectorInterface
|
||||
class JournalExportCollector extends BasicCollector implements CollectorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
@@ -298,7 +298,7 @@ class JournalCollector extends BasicCollector implements CollectorInterface
|
||||
'transactions AS opposing', function (JoinClause $join) {
|
||||
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
|
||||
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'))
|
||||
->where('transactions.identifier', '=', 'opposing.identifier');
|
||||
->where('transactions.identifier', '=', DB::raw('opposing.identifier'));
|
||||
}
|
||||
)
|
||||
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
@@ -14,7 +14,6 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Export\Exporter;
|
||||
|
||||
use FireflyIII\Export\Entry\Entry;
|
||||
use FireflyIII\Export\Entry\EntryAccount;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use League\Csv\Writer;
|
||||
use SplFileObject;
|
||||
|
@@ -15,7 +15,7 @@ namespace FireflyIII\Export;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Export\Collector\AttachmentCollector;
|
||||
use FireflyIII\Export\Collector\JournalCollector;
|
||||
use FireflyIII\Export\Collector\JournalExportCollector;
|
||||
use FireflyIII\Export\Collector\UploadCollector;
|
||||
use FireflyIII\Export\Entry\Entry;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
@@ -45,8 +45,6 @@ class Processor
|
||||
public $job;
|
||||
/** @var array */
|
||||
public $settings;
|
||||
/** @var \FireflyIII\Export\ConfigurationFile */
|
||||
private $configurationMaker;
|
||||
/** @var Collection */
|
||||
private $exportEntries;
|
||||
/** @var Collection */
|
||||
@@ -93,8 +91,8 @@ class Processor
|
||||
*/
|
||||
public function collectJournals(): bool
|
||||
{
|
||||
/** @var JournalCollector $collector */
|
||||
$collector = app(JournalCollector::class, [$this->job]);
|
||||
/** @var JournalExportCollector $collector */
|
||||
$collector = app(JournalExportCollector::class, [$this->job]);
|
||||
$collector->setDates($this->settings['startDate'], $this->settings['endDate']);
|
||||
$collector->setAccounts($this->settings['accounts']);
|
||||
$collector->run();
|
||||
|
@@ -24,6 +24,15 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface AccountChartGeneratorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param array $names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $values, array $names): array;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
|
@@ -14,6 +14,7 @@ namespace FireflyIII\Generator\Chart\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Support\ChartColour;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -83,6 +84,37 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @param array $names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pieChart(array $values, array $names): array
|
||||
{
|
||||
$data = [
|
||||
'datasets' => [
|
||||
0 => [],
|
||||
],
|
||||
'labels' => [],
|
||||
];
|
||||
$index = 0;
|
||||
foreach ($values as $categoryId => $value) {
|
||||
|
||||
// make larger than 0
|
||||
if (bccomp($value, '0') === -1) {
|
||||
$value = bcmul($value, '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][0]['data'][] = round($value, 2);
|
||||
$data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||
$data['labels'][] = $names[$categoryId];
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
|
@@ -14,7 +14,8 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Bill;
|
||||
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Support\ChartColour;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -37,7 +38,7 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface
|
||||
'datasets' => [
|
||||
[
|
||||
'data' => [round($unpaid, 2), round(bcmul($paid, '-1'), 2)],
|
||||
'backgroundColor' => ['rgba(53, 124, 165,0.7)', 'rgba(0, 141, 76, 0.7)',],
|
||||
'backgroundColor' => [ChartColour::getColour(0), ChartColour::getColour(1)],
|
||||
],
|
||||
|
||||
],
|
||||
@@ -61,13 +62,13 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface
|
||||
$minAmount = [];
|
||||
$maxAmount = [];
|
||||
$actualAmount = [];
|
||||
/** @var TransactionJournal $entry */
|
||||
/** @var Transaction $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$data['labels'][] = $entry->date->formatLocalized($format);
|
||||
$minAmount[] = round($bill->amount_min, 2);
|
||||
$maxAmount[] = round($bill->amount_max, 2);
|
||||
// journalAmount has been collected in BillRepository::getJournals
|
||||
$actualAmount[] = round(TransactionJournal::amountPositive($entry), 2);
|
||||
$actualAmount[] = bcmul($entry->transaction_amount, '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][] = [
|
||||
|
@@ -39,26 +39,9 @@ interface BudgetChartGeneratorInterface
|
||||
public function frontpage(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $viewRange
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries, string $viewRange) : array;
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $budgets, Collection $entries): array;
|
||||
|
||||
public function period(array $entries): array;
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@ namespace FireflyIII\Generator\Chart\Budget;
|
||||
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
|
||||
/**
|
||||
* Class ChartJsBudgetChartGenerator
|
||||
@@ -101,49 +100,15 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array
|
||||
public function period(array $entries) : array
|
||||
{
|
||||
// dataset:
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
// get labels from one of the budgets (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
$keys = array_keys($first['budgeted']);
|
||||
foreach ($keys as $year) {
|
||||
$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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param string $viewRange
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function period(Collection $entries, string $viewRange) : array
|
||||
{
|
||||
$data = [
|
||||
'labels' => [],
|
||||
'labels' => array_keys($entries),
|
||||
'datasets' => [
|
||||
0 => [
|
||||
'label' => trans('firefly.budgeted'),
|
||||
@@ -156,9 +121,8 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
|
||||
],
|
||||
'count' => 2,
|
||||
];
|
||||
foreach ($entries as $entry) {
|
||||
$label = Navigation::periodShow($entry['date'], $viewRange);
|
||||
$data['labels'][] = $label;
|
||||
|
||||
foreach ($entries as $label => $entry) {
|
||||
// data set 0 is budgeted
|
||||
// data set 1 is spent:
|
||||
$data['datasets'][0]['data'][] = $entry['budgeted'];
|
||||
@@ -169,42 +133,4 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function year(Collection $budgets, Collection $entries): array
|
||||
{
|
||||
// language:
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$data = [
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
foreach ($budgets as $budget) {
|
||||
$data['labels'][] = $budget->name;
|
||||
}
|
||||
// also add "no budget"
|
||||
$data['labels'][] = strval(trans('firefly.no_budget'));
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($entries as $entry) {
|
||||
$array = [
|
||||
'label' => $entry[0]->formatLocalized($format),
|
||||
'data' => [],
|
||||
];
|
||||
array_shift($entry);
|
||||
$array['data'] = $entry;
|
||||
$data['datasets'][] = $array;
|
||||
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -30,14 +30,6 @@ interface CategoryChartGeneratorInterface
|
||||
*/
|
||||
public function all(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInPeriod(Collection $categories, Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
@@ -46,11 +38,11 @@ interface CategoryChartGeneratorInterface
|
||||
public function frontpage(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array;
|
||||
public function mainReportChart(array $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
@@ -60,10 +52,10 @@ interface CategoryChartGeneratorInterface
|
||||
public function period(Collection $entries): array;
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function spentInPeriod(Collection $categories, Collection $entries): array;
|
||||
public function pieChart(array $data): array;
|
||||
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Generator\Chart\Category;
|
||||
|
||||
use FireflyIII\Support\ChartColour;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
|
||||
@@ -57,39 +58,6 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function earnedInPeriod(Collection $categories, Collection $entries): array
|
||||
{
|
||||
|
||||
// language:
|
||||
$format = (string)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
|
||||
*
|
||||
@@ -118,29 +86,46 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries): array
|
||||
public function mainReportChart(array $entries): array
|
||||
{
|
||||
// get labels from one of the categories (assuming there's at least one):
|
||||
$first = $entries->first();
|
||||
$data = ['count' => 0, 'labels' => array_keys($first['spent']), 'datasets' => [],];
|
||||
|
||||
// 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' => 0,
|
||||
'labels' => array_keys($entries),
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
|
||||
foreach ($entries as $row) {
|
||||
foreach ($row['in'] as $categoryId => $amount) {
|
||||
// get in:
|
||||
$data['datasets'][$categoryId . 'in']['data'][] = round($amount, 2);
|
||||
|
||||
// get out:
|
||||
$opposite = $row['out'][$categoryId];
|
||||
$data['datasets'][$categoryId . 'out']['data'][] = round($opposite, 2);
|
||||
|
||||
// set name:
|
||||
$data['datasets'][$categoryId . 'out']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')';
|
||||
$data['datasets'][$categoryId . 'in']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.income'))) . ')';
|
||||
|
||||
}
|
||||
}
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
// remove empty rows:
|
||||
foreach ($data['datasets'] as $key => $content) {
|
||||
if (array_sum($content['data']) === 0.0) {
|
||||
unset($data['datasets'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// re-key the datasets array:
|
||||
$data['datasets'] = array_values($data['datasets']);
|
||||
$data['count'] = count($data['datasets']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -158,35 +143,32 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
* @param Collection $entries
|
||||
* @param array $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function spentInPeriod(Collection $categories, Collection $entries): array
|
||||
public function pieChart(array $entries): array
|
||||
{
|
||||
|
||||
// language:
|
||||
$format = (string)trans('config.month');
|
||||
|
||||
$data = [
|
||||
'count' => 0,
|
||||
$data = [
|
||||
'datasets' => [
|
||||
0 => [],
|
||||
],
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
];
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$data['labels'][] = $category->name;
|
||||
}
|
||||
|
||||
$index = 0;
|
||||
foreach ($entries as $entry) {
|
||||
$date = $entry[0]->formatLocalized($format);
|
||||
array_shift($entry);
|
||||
$data['count']++;
|
||||
$data['datasets'][] = ['label' => $date, 'data' => $entry];
|
||||
|
||||
if (bccomp($entry['amount'], '0') === -1) {
|
||||
$entry['amount'] = bcmul($entry['amount'], '-1');
|
||||
}
|
||||
|
||||
$data['datasets'][0]['data'][] = round($entry['amount'], 2);
|
||||
$data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||
$data['labels'][] = $entry['name'];
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
147
app/Generator/Report/Audit/MonthReportGenerator.php
Normal file
147
app/Generator/Report/Audit/MonthReportGenerator.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* MonthReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Audit;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Standard
|
||||
*/
|
||||
class MonthReportGenerator implements ReportGeneratorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
|
||||
|
||||
$auditData = [];
|
||||
$dayBefore = clone $this->start;
|
||||
$dayBefore->subDay();
|
||||
/** @var Account $account */
|
||||
foreach ($this->accounts as $account) {
|
||||
// balance the day before:
|
||||
$id = $account->id;
|
||||
$dayBeforeBalance = Steam::balance($account, $dayBefore);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
|
||||
$journals = $collector->getJournals();
|
||||
$journals = $journals->reverse();
|
||||
$startBalance = $dayBeforeBalance;
|
||||
|
||||
|
||||
/** @var Transaction $journal */
|
||||
foreach ($journals as $transaction) {
|
||||
$transaction->before = $startBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
$newBalance = bcadd($startBalance, $transactionAmount);
|
||||
$transaction->after = $newBalance;
|
||||
$startBalance = $newBalance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse set again.
|
||||
*/
|
||||
$auditData[$id]['journals'] = $journals->reverse();
|
||||
$auditData[$id]['exists'] = $journals->count() > 0;
|
||||
$auditData[$id]['end'] = $this->end->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$auditData[$id]['endBalance'] = Steam::balance($account, $this->end);
|
||||
$auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
|
||||
}
|
||||
|
||||
$reportType = 'audit';
|
||||
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||
|
||||
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
|
||||
'interest_date', 'book_date', 'process_date',
|
||||
// three new optional fields.
|
||||
'due_date', 'payment_date', 'invoice_date',
|
||||
'from', 'to', 'budget', 'category', 'bill',
|
||||
// more new optional fields
|
||||
'internal_reference', 'notes',
|
||||
|
||||
'create_date', 'update_date',
|
||||
];
|
||||
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
|
||||
|
||||
return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow'))
|
||||
->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts)
|
||||
->render();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->end = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->start = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
27
app/Generator/Report/Audit/MultiYearReportGenerator.php
Normal file
27
app/Generator/Report/Audit/MultiYearReportGenerator.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* MultiYearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Audit;
|
||||
|
||||
|
||||
/**
|
||||
* Class MultiYearReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Audit
|
||||
*/
|
||||
class MultiYearReportGenerator extends MonthReportGenerator
|
||||
{
|
||||
/**
|
||||
* Doesn't do anything different.
|
||||
*/
|
||||
}
|
28
app/Generator/Report/Audit/YearReportGenerator.php
Normal file
28
app/Generator/Report/Audit/YearReportGenerator.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* YearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Audit;
|
||||
|
||||
|
||||
/**
|
||||
* Class YearReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Audit
|
||||
*/
|
||||
class YearReportGenerator extends MonthReportGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* Doesn't do anything different.
|
||||
*/
|
||||
}
|
336
app/Generator/Report/Category/MonthReportGenerator.php
Normal file
336
app/Generator/Report/Category/MonthReportGenerator.php
Normal file
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
/**
|
||||
* MonthReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Category;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Category
|
||||
*/
|
||||
class MonthReportGenerator extends Support implements ReportGeneratorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Collection */
|
||||
private $categories;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Collection */
|
||||
private $expenses;
|
||||
/** @var Collection */
|
||||
private $income;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* MonthReportGenerator constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->income = new Collection;
|
||||
$this->expenses = new Collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||
$categoryIds = join(',', $this->categories->pluck('id')->toArray());
|
||||
$reportType = 'category';
|
||||
$expenses = $this->getExpenses();
|
||||
$income = $this->getIncome();
|
||||
$accountSummary = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income));
|
||||
$categorySummary = $this->getObjectSummary($this->summarizeByCategory($expenses), $this->summarizeByCategory($income));
|
||||
$averageExpenses = $this->getAverages($expenses, SORT_ASC);
|
||||
$averageIncome = $this->getAverages($income, SORT_DESC);
|
||||
$topExpenses = $this->getTopExpenses();
|
||||
$topIncome = $this->getTopIncome();
|
||||
|
||||
|
||||
// render!
|
||||
return view(
|
||||
'reports.category.month',
|
||||
compact(
|
||||
'accountIds', 'categoryIds', 'topIncome', 'reportType', 'accountSummary', 'categorySummary', 'averageExpenses', 'averageIncome', 'topExpenses'
|
||||
)
|
||||
)
|
||||
->with('start', $this->start)->with('end', $this->end)
|
||||
->with('categories', $this->categories)
|
||||
->with('accounts', $this->accounts)
|
||||
->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
$this->categories = $categories;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->end = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->start = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
* @param int $sortFlag
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAverages(Collection $collection, int $sortFlag): array
|
||||
{
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
// opposing name and ID:
|
||||
$opposingId = $transaction->opposing_account_id;
|
||||
|
||||
// is not set?
|
||||
if (!isset($result[$opposingId])) {
|
||||
$name = $transaction->opposing_account_name;
|
||||
$encrypted = intval($transaction->opposing_account_encrypted);
|
||||
$name = $encrypted === 1 ? Crypt::decrypt($name) : $name;
|
||||
$result[$opposingId] = [
|
||||
'name' => $name,
|
||||
'count' => 1,
|
||||
'id' => $opposingId,
|
||||
'average' => $transaction->transaction_amount,
|
||||
'sum' => $transaction->transaction_amount,
|
||||
];
|
||||
continue;
|
||||
}
|
||||
$result[$opposingId]['count']++;
|
||||
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
|
||||
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
|
||||
}
|
||||
|
||||
// sort result by average:
|
||||
$average = [];
|
||||
foreach ($result as $key => $row) {
|
||||
$average[$key] = floatval($row['average']);
|
||||
}
|
||||
|
||||
array_multisort($average, $sortFlag, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getExpenses(): Collection
|
||||
{
|
||||
if ($this->expenses->count() > 0) {
|
||||
Log::debug('Return previous set of expenses.');
|
||||
|
||||
return $this->expenses;
|
||||
}
|
||||
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||
->setCategories($this->categories)->withOpposingAccount()->disableFilter();
|
||||
|
||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$transactions = self::filterExpenses($transactions, $accountIds);
|
||||
$this->expenses = $transactions;
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getIncome(): Collection
|
||||
{
|
||||
if ($this->income->count() > 0) {
|
||||
return $this->income;
|
||||
}
|
||||
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
|
||||
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||
->setCategories($this->categories)->withOpposingAccount();
|
||||
$accountIds = $this->accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$transactions = self::filterIncome($transactions, $accountIds);
|
||||
$this->income = $transactions;
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $spent
|
||||
* @param array $earned
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getObjectSummary(array $spent, array $earned): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
/**
|
||||
* @var int $accountId
|
||||
* @var string $entry
|
||||
*/
|
||||
foreach ($spent as $objectId => $entry) {
|
||||
if (!isset($return[$objectId])) {
|
||||
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
||||
}
|
||||
|
||||
$return[$objectId]['spent'] = $entry;
|
||||
}
|
||||
unset($entry);
|
||||
|
||||
/**
|
||||
* @var int $accountId
|
||||
* @var string $entry
|
||||
*/
|
||||
foreach ($earned as $objectId => $entry) {
|
||||
if (!isset($return[$objectId])) {
|
||||
$return[$objectId] = ['spent' => 0, 'earned' => 0];
|
||||
}
|
||||
|
||||
$return[$objectId]['earned'] = $entry;
|
||||
}
|
||||
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getTopExpenses(): Collection
|
||||
{
|
||||
$transactions = $this->getExpenses()->sortBy('transaction_amount');
|
||||
|
||||
$transactions = $transactions->each(
|
||||
function (Transaction $transaction) {
|
||||
if (intval($transaction->opposing_account_encrypted) === 1) {
|
||||
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getTopIncome(): Collection
|
||||
{
|
||||
$transactions = $this->getIncome()->sortByDesc('transaction_amount');
|
||||
|
||||
$transactions = $transactions->each(
|
||||
function (Transaction $transaction) {
|
||||
if (intval($transaction->opposing_account_encrypted) === 1) {
|
||||
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function summarizeByAccount(Collection $collection): array
|
||||
{
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$accountId = $transaction->account_id;
|
||||
$result[$accountId] = $result[$accountId] ?? '0';
|
||||
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function summarizeByCategory(Collection $collection): array
|
||||
{
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($collection as $transaction) {
|
||||
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||
$transCatId = intval($transaction->transaction_category_id);
|
||||
$categoryId = max($jrnlCatId, $transCatId);
|
||||
$result[$categoryId] = $result[$categoryId] ?? '0';
|
||||
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
27
app/Generator/Report/Category/MultiYearReportGenerator.php
Normal file
27
app/Generator/Report/Category/MultiYearReportGenerator.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* MultiYearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Category;
|
||||
|
||||
|
||||
/**
|
||||
* Class MultiYearReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Audit
|
||||
*/
|
||||
class MultiYearReportGenerator extends MonthReportGenerator
|
||||
{
|
||||
/**
|
||||
* Doesn't do anything different.
|
||||
*/
|
||||
}
|
91
app/Generator/Report/Category/Support.php
Normal file
91
app/Generator/Report/Category/Support.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* Support.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Category;
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
|
||||
/**
|
||||
* Class Support
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Category
|
||||
*/
|
||||
class Support
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
* @param array $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public static function filterExpenses(Collection $collection, array $accounts): Collection
|
||||
{
|
||||
$result = $collection->filter(
|
||||
function (Transaction $transaction) use ($accounts) {
|
||||
$opposing = $transaction->opposing_account_id;
|
||||
// remove internal transfer
|
||||
if (in_array($opposing, $accounts)) {
|
||||
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
|
||||
|
||||
return null;
|
||||
}
|
||||
// remove positive amount
|
||||
if (bccomp($transaction->transaction_amount, '0') === 1) {
|
||||
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
* @param array $accounts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public static function filterIncome(Collection $collection, array $accounts): Collection
|
||||
{
|
||||
$result = $collection->filter(
|
||||
function (Transaction $transaction) use ($accounts) {
|
||||
$opposing = $transaction->opposing_account_id;
|
||||
// remove internal transfer
|
||||
if (in_array($opposing, $accounts)) {
|
||||
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
|
||||
|
||||
return null;
|
||||
}
|
||||
// remove positive amount
|
||||
if (bccomp($transaction->transaction_amount, '0') === -1) {
|
||||
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
28
app/Generator/Report/Category/YearReportGenerator.php
Normal file
28
app/Generator/Report/Category/YearReportGenerator.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* YearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Category;
|
||||
|
||||
|
||||
/**
|
||||
* Class YearReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Audit
|
||||
*/
|
||||
class YearReportGenerator extends MonthReportGenerator
|
||||
{
|
||||
|
||||
/**
|
||||
* Doesn't do anything different.
|
||||
*/
|
||||
}
|
60
app/Generator/Report/ReportGeneratorFactory.php
Normal file
60
app/Generator/Report/ReportGeneratorFactory.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportGeneratorFactory.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
|
||||
/**
|
||||
* Class ReportGeneratorFactory
|
||||
*
|
||||
* @package FireflyIII\Generator\Report
|
||||
*/
|
||||
class ReportGeneratorFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public static function reportGenerator(string $type, Carbon $start, Carbon $end): ReportGeneratorInterface
|
||||
{
|
||||
$period = 'Month';
|
||||
// more than two months date difference means year report.
|
||||
if ($start->diffInMonths($end) > 1) {
|
||||
$period = 'Year';
|
||||
}
|
||||
|
||||
// more than one year date difference means multi year report.
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
$period = 'MultiYear';
|
||||
}
|
||||
|
||||
|
||||
$class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period);
|
||||
if (class_exists($class)) {
|
||||
/** @var ReportGeneratorInterface $obj */
|
||||
$obj = new $class;
|
||||
$obj->setStartDate($start);
|
||||
$obj->setEndDate($end);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period));
|
||||
}
|
||||
}
|
60
app/Generator/Report/ReportGeneratorInterface.php
Normal file
60
app/Generator/Report/ReportGeneratorInterface.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportGeneratorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface ReportGeneratorInterface
|
||||
*
|
||||
* @package FireflyIII\Generator\Report
|
||||
*/
|
||||
interface ReportGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface;
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface;
|
||||
|
||||
}
|
99
app/Generator/Report/Standard/MonthReportGenerator.php
Normal file
99
app/Generator/Report/Standard/MonthReportGenerator.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* MonthReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Standard;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Standard
|
||||
*/
|
||||
class MonthReportGenerator implements ReportGeneratorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
/** @var ReportHelperInterface $helper */
|
||||
$helper = app(ReportHelperInterface::class);
|
||||
$bills = $helper->getBillReport($this->start, $this->end, $this->accounts);
|
||||
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||
$reportType = 'default';
|
||||
|
||||
// continue!
|
||||
return view(
|
||||
'reports.default.month',
|
||||
compact('bills', 'accountIds', 'reportType')
|
||||
)->with('start', $this->start)->with('end', $this->end)->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->end = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->start = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
96
app/Generator/Report/Standard/MultiYearReportGenerator.php
Normal file
96
app/Generator/Report/Standard/MultiYearReportGenerator.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* MultiYearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Standard;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Standard
|
||||
*/
|
||||
class MultiYearReportGenerator implements ReportGeneratorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
// and some id's, joined:
|
||||
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||
$reportType = 'default';
|
||||
|
||||
// continue!
|
||||
return view(
|
||||
'reports.default.multi-year',
|
||||
compact('accountIds', 'reportType')
|
||||
)->with('start', $this->start)->with('end', $this->end)->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->end = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->start = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
96
app/Generator/Report/Standard/YearReportGenerator.php
Normal file
96
app/Generator/Report/Standard/YearReportGenerator.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* YearReportGenerator.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Generator\Report\Standard;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class MonthReportGenerator
|
||||
*
|
||||
* @package FireflyIII\Generator\Report\Standard
|
||||
*/
|
||||
class YearReportGenerator implements ReportGeneratorInterface
|
||||
{
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Carbon */
|
||||
private $end;
|
||||
/** @var Carbon */
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function generate(): string
|
||||
{
|
||||
// and some id's, joined:
|
||||
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
|
||||
$reportType = 'default';
|
||||
|
||||
// continue!
|
||||
return view(
|
||||
'reports.default.year',
|
||||
compact('accountIds', 'reportType')
|
||||
)->with('start', $this->start)->with('end', $this->end)->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): ReportGeneratorInterface
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): ReportGeneratorInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setEndDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->end = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return ReportGeneratorInterface
|
||||
*/
|
||||
public function setStartDate(Carbon $date): ReportGeneratorInterface
|
||||
{
|
||||
$this->start = $date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@@ -14,8 +14,10 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\StoredBudgetLimit;
|
||||
use FireflyIII\Events\UpdatedBudgetLimit;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Log;
|
||||
@@ -38,37 +40,9 @@ class BudgetEventHandler
|
||||
*/
|
||||
public function storeRepetition(StoredBudgetLimit $event):bool
|
||||
{
|
||||
$budgetLimit = $event->budgetLimit;
|
||||
$end = $event->end;
|
||||
$set = $budgetLimit->limitrepetitions()
|
||||
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
|
||||
->where('enddate', $end->format('Y-m-d 00:00:00'))
|
||||
->get();
|
||||
if ($set->count() == 0) {
|
||||
$repetition = new LimitRepetition;
|
||||
$repetition->startdate = $budgetLimit->startdate;
|
||||
$repetition->enddate = $end;
|
||||
$repetition->amount = $budgetLimit->amount;
|
||||
$repetition->budgetLimit()->associate($budgetLimit);
|
||||
|
||||
try {
|
||||
$repetition->save();
|
||||
} catch (QueryException $e) {
|
||||
Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if ($set->count() == 1) {
|
||||
$repetition = $set->first();
|
||||
$repetition->amount = $budgetLimit->amount;
|
||||
$repetition->save();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->processRepetitionChange($event->budgetLimit, $event->end);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates, if present the budget limit repetition part of a budget limit.
|
||||
*
|
||||
@@ -78,16 +52,25 @@ class BudgetEventHandler
|
||||
*/
|
||||
public function updateRepetition(UpdatedBudgetLimit $event): bool
|
||||
{
|
||||
$budgetLimit = $event->budgetLimit;
|
||||
$end = $event->end;
|
||||
$set = $budgetLimit->limitrepetitions()
|
||||
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
|
||||
->where('enddate', $end->format('Y-m-d 00:00:00'))
|
||||
->get();
|
||||
return $this->processRepetitionChange($event->budgetLimit, $event->end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date):bool
|
||||
{
|
||||
$set = $budgetLimit->limitrepetitions()
|
||||
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
|
||||
->where('enddate', $date->format('Y-m-d 00:00:00'))
|
||||
->get();
|
||||
if ($set->count() == 0) {
|
||||
$repetition = new LimitRepetition;
|
||||
$repetition->startdate = $budgetLimit->startdate;
|
||||
$repetition->enddate = $end;
|
||||
$repetition->enddate = $date;
|
||||
$repetition->amount = $budgetLimit->amount;
|
||||
$repetition->budgetLimit()->associate($budgetLimit);
|
||||
|
||||
|
@@ -21,6 +21,7 @@ use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\Support\Events\BillScanner;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class StoredJournalEventHandler
|
||||
@@ -42,30 +43,69 @@ class StoredJournalEventHandler
|
||||
$journal = $event->journal;
|
||||
$piggyBankId = $event->piggyBankId;
|
||||
|
||||
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
|
||||
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
|
||||
|
||||
if (is_null($piggyBank)) {
|
||||
Log::error('No such piggy bank!');
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name));
|
||||
// update piggy bank rep for date of transaction journal.
|
||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||
if (is_null($repetition)) {
|
||||
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$amount = TransactionJournal::amountPositive($journal);
|
||||
Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
// if piggy account matches source account, the amount is positive
|
||||
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
|
||||
if (in_array($piggyBank->account_id, $sources)) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id));
|
||||
}
|
||||
|
||||
// if the amount is positive:
|
||||
// make sure it fits in piggy bank:
|
||||
if (bccomp($amount, '0') === 1) {
|
||||
// amount is positive
|
||||
$room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount));
|
||||
Log::debug(sprintf('Room in piggy bank for extra money is %f', $room));
|
||||
if (bccomp($room, $amount) === -1) {
|
||||
// $room is smaller than $amount
|
||||
Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $room));
|
||||
$amount = $room;
|
||||
}
|
||||
}
|
||||
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
// amount is negative
|
||||
Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount));
|
||||
$compare = bcmul($repetition->currentamount, '-1');
|
||||
if (bccomp($compare, $amount) === 1) {
|
||||
// $currentamount is smaller than $amount
|
||||
Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
|
||||
Log::debug(sprintf('New amount is %f', $compare));
|
||||
$amount = $compare;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
|
||||
$repetition->save();
|
||||
|
||||
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
|
||||
/** @var PiggyBankEvent $event */
|
||||
$event = PiggyBankEvent::create(
|
||||
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
|
||||
);
|
||||
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -15,11 +15,8 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
|
||||
use FireflyIII\Events\UpdatedTransactionJournal;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\Support\Events\BillScanner;
|
||||
|
||||
@@ -30,50 +27,6 @@ use FireflyIII\Support\Events\BillScanner;
|
||||
*/
|
||||
class UpdatedJournalEventHandler
|
||||
{
|
||||
/**
|
||||
* This method will try to reconnect a journal to a piggy bank, updating the piggy bank repetition.
|
||||
*
|
||||
* @param UpdatedTransactionJournal $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function connectToPiggyBank(UpdatedTransactionJournal $event): bool
|
||||
{
|
||||
$journal = $event->journal;
|
||||
|
||||
if (!$journal->isTransfer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the event connected to this journal:
|
||||
/** @var PiggyBankEvent $event */
|
||||
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
|
||||
if (is_null($event)) {
|
||||
return false;
|
||||
}
|
||||
$piggyBank = $event->piggyBank()->first();
|
||||
$repetition = null;
|
||||
if (!is_null($piggyBank)) {
|
||||
/** @var PiggyBankRepetition $repetition */
|
||||
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
|
||||
}
|
||||
|
||||
if (is_null($repetition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$amount = TransactionJournal::amount($journal);
|
||||
$diff = bcsub($amount, $event->amount); // update current repetition
|
||||
|
||||
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
|
||||
$repetition->save();
|
||||
|
||||
|
||||
$event->amount = $amount;
|
||||
$event->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will check all the rules when a journal is updated.
|
||||
|
@@ -14,10 +14,13 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Exception;
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Events\ConfirmedUser;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\ResentConfirmation;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Mail\Message;
|
||||
use Log;
|
||||
use Mail;
|
||||
@@ -81,36 +84,7 @@ class UserEventHandler
|
||||
*/
|
||||
public function sendConfirmationMessage(RegisteredUser $event): bool
|
||||
{
|
||||
$user = $event->user;
|
||||
$ipAddress = $event->ipAddress;
|
||||
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
|
||||
if ($confirmAccount === false) {
|
||||
Preferences::setForUser($user, 'user_confirmed', true);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
|
||||
Preferences::mark();
|
||||
|
||||
return true;
|
||||
}
|
||||
$email = $user->email;
|
||||
$code = str_random(16);
|
||||
$route = route('do_confirm_account', [$code]);
|
||||
Preferences::setForUser($user, 'user_confirmed', false);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
|
||||
Preferences::setForUser($user, 'user_confirmed_code', $code);
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
|
||||
function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Please confirm your Firefly III account');
|
||||
}
|
||||
);
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->sendConfirmation($event->user, $event->ipAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,37 +98,35 @@ class UserEventHandler
|
||||
*/
|
||||
function sendConfirmationMessageAgain(ResentConfirmation $event): bool
|
||||
{
|
||||
$user = $event->user;
|
||||
$ipAddress = $event->ipAddress;
|
||||
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
|
||||
if ($confirmAccount === false) {
|
||||
Preferences::setForUser($user, 'user_confirmed', true);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
|
||||
Preferences::mark();
|
||||
return $this->sendConfirmation($event->user, $event->ipAddress);
|
||||
|
||||
return true;
|
||||
}
|
||||
$email = $user->email;
|
||||
$code = str_random(16);
|
||||
$route = route('do_confirm_account', [$code]);
|
||||
Preferences::setForUser($user, 'user_confirmed', false);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
|
||||
Preferences::setForUser($user, 'user_confirmed_code', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestedNewPassword $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sendNewPassword(RequestedNewPassword $event): bool
|
||||
{
|
||||
$email = $event->user->email;
|
||||
$ipAddress = $event->ipAddress;
|
||||
$token = $event->token;
|
||||
|
||||
$url = route('password.reset', [$token]);
|
||||
|
||||
// send email.
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
|
||||
function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Please confirm your Firefly III account');
|
||||
}
|
||||
['emails.password-html', 'emails.password-text'], ['url' => $url, 'ip' => $ipAddress], function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Your password reset request');
|
||||
}
|
||||
);
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,7 +151,7 @@ class UserEventHandler
|
||||
// send email.
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
|
||||
['emails.registered-html', 'emails.registered-text'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Welcome to Firefly III! ');
|
||||
}
|
||||
);
|
||||
@@ -222,4 +194,43 @@ class UserEventHandler
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $ipAddress
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function sendConfirmation(User $user, string $ipAddress): bool
|
||||
{
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
if ($mustConfirmAccount === false) {
|
||||
Preferences::setForUser($user, 'user_confirmed', true);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
|
||||
Preferences::mark();
|
||||
|
||||
return true;
|
||||
}
|
||||
$email = $user->email;
|
||||
$code = str_random(16);
|
||||
$route = route('do_confirm_account', [$code]);
|
||||
Preferences::setForUser($user, 'user_confirmed', false);
|
||||
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
|
||||
Preferences::setForUser($user, 'user_confirmed_code', $code);
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.confirm-account-html', 'emails.confirm-account-text'], ['route' => $route, 'ip' => $ipAddress],
|
||||
function (Message $message) use ($email) {
|
||||
$message->to($email, $email)->subject('Please confirm your Firefly III account');
|
||||
}
|
||||
);
|
||||
} catch (Swift_TransportException $e) {
|
||||
Log::error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -17,10 +17,8 @@ use FireflyIII\Models\Attachment;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Input;
|
||||
use Log;
|
||||
use Storage;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Class AttachmentHelper
|
||||
@@ -236,13 +234,8 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
private function getFiles()
|
||||
{
|
||||
$files = null;
|
||||
try {
|
||||
if (Input::hasFile('attachments')) {
|
||||
$files = Input::file('attachments');
|
||||
}
|
||||
} catch (TypeError $e) {
|
||||
// Log it, do nothing else.
|
||||
Log::error($e->getMessage());
|
||||
if (Input::hasFile('attachments')) {
|
||||
$files = Input::file('attachments');
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
@@ -13,7 +13,10 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class Bill
|
||||
@@ -26,7 +29,11 @@ class Bill
|
||||
/**
|
||||
* @var Collection
|
||||
*/
|
||||
protected $bills;
|
||||
private $bills;
|
||||
/** @var Carbon */
|
||||
private $endDate;
|
||||
/** @var Carbon */
|
||||
private $startDate;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -44,6 +51,43 @@ class Bill
|
||||
$this->bills->push($bill);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function filterBills()
|
||||
{
|
||||
Log::debug('Now in filterBills()');
|
||||
/** @var BillRepositoryInterface $repository */
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$start = $this->startDate;
|
||||
$end = $this->endDate;
|
||||
$lines = $this->bills->filter(
|
||||
function (BillLine $line) use ($repository, $start, $end) {
|
||||
// next expected match?
|
||||
$date = $start;
|
||||
Log::debug(sprintf('Now at bill line for bill "%s"', $line->getBill()->name));
|
||||
Log::debug(sprintf('Default date to use is start date: %s', $date->format('Y-m-d')));
|
||||
if ($line->isHit()) {
|
||||
$date = $line->getLastHitDate();
|
||||
Log::debug(sprintf('Line was hit, see date: %s. Always include it.', $date->format('Y-m-d')));
|
||||
|
||||
return $line;
|
||||
}
|
||||
$expected = $repository->nextExpectedMatch($line->getBill(), $date);
|
||||
Log::debug(sprintf('Next expected match is %s', $expected->format('Y-m-d')));
|
||||
if ($expected <= $end && $expected >= $start) {
|
||||
Log::debug('This date is inside report limits');
|
||||
|
||||
return $line;
|
||||
}
|
||||
Log::debug('This date is OUTSIDE report limits');
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$this->bills = $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
@@ -62,4 +106,20 @@ class Bill
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $endDate
|
||||
*/
|
||||
public function setEndDate(Carbon $endDate)
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $startDate
|
||||
*/
|
||||
public function setStartDate(Carbon $startDate)
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
declare(strict_types = 1);
|
||||
namespace FireflyIII\Helpers\Collection;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Bill as BillModel;
|
||||
|
||||
/**
|
||||
@@ -23,8 +24,6 @@ use FireflyIII\Models\Bill as BillModel;
|
||||
class BillLine
|
||||
{
|
||||
|
||||
/** @var bool */
|
||||
protected $active;
|
||||
/** @var string */
|
||||
protected $amount;
|
||||
/** @var BillModel */
|
||||
@@ -35,10 +34,19 @@ class BillLine
|
||||
protected $max;
|
||||
/** @var string */
|
||||
protected $min;
|
||||
|
||||
/** @var Carbon */
|
||||
private $lastHitDate;
|
||||
/** @var int */
|
||||
private $transactionJournalId;
|
||||
|
||||
/**
|
||||
* BillLine constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->lastHitDate = new Carbon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -124,15 +132,7 @@ class BillLine
|
||||
*/
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $active
|
||||
*/
|
||||
public function setActive(bool $active)
|
||||
{
|
||||
$this->active = $active;
|
||||
return intval($this->bill->active) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,4 +151,21 @@ class BillLine
|
||||
$this->hit = $hit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $lastHitDate
|
||||
*/
|
||||
public function setLastHitDate(Carbon $lastHitDate)
|
||||
{
|
||||
$this->lastHitDate = $lastHitDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getLastHitDate(): Carbon
|
||||
{
|
||||
return $this->lastHitDate;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
641
app/Helpers/Collector/JournalCollector.php
Normal file
641
app/Helpers/Collector/JournalCollector.php
Normal file
@@ -0,0 +1,641 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Maybe this is a good idea after all...
|
||||
*
|
||||
* Class JournalCollector
|
||||
*
|
||||
* @package FireflyIII\Helpers\Collector
|
||||
*/
|
||||
class JournalCollector implements JournalCollectorInterface
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
private $accountIds = [];
|
||||
/** @var int */
|
||||
private $count = 0;
|
||||
/** @var array */
|
||||
private $fields
|
||||
= [
|
||||
'transaction_journals.id as journal_id',
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.encrypted',
|
||||
//'transaction_journals.transaction_currency_id',
|
||||
'transaction_currencies.code as transaction_currency_code',
|
||||
//'transaction_currencies.symbol as transaction_currency_symbol',
|
||||
'transaction_types.type as transaction_type_type',
|
||||
'transaction_journals.bill_id',
|
||||
'bills.name as bill_name',
|
||||
'transactions.id as id',
|
||||
'transactions.amount as transaction_amount',
|
||||
'transactions.description as transaction_description',
|
||||
'transactions.account_id',
|
||||
'transactions.identifier',
|
||||
'transactions.transaction_journal_id',
|
||||
'accounts.name as account_name',
|
||||
'accounts.encrypted as account_encrypted',
|
||||
'account_types.type as account_type',
|
||||
];
|
||||
/** @var bool */
|
||||
private $filterTransfers = false;
|
||||
/** @var bool */
|
||||
private $joinedBudget = false;
|
||||
/** @var bool */
|
||||
private $joinedCategory = false;
|
||||
/** @var bool */
|
||||
private $joinedOpposing = false;
|
||||
/** @var bool */
|
||||
private $joinedTag = false;
|
||||
/** @var int */
|
||||
private $limit;
|
||||
/** @var int */
|
||||
private $offset;
|
||||
/** @var int */
|
||||
private $page = 1;
|
||||
/** @var EloquentBuilder */
|
||||
private $query;
|
||||
/** @var bool */
|
||||
private $run = false;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* JournalCollector constructor.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->query = $this->startQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
if ($this->run === true) {
|
||||
throw new FireflyException('Cannot count after run in JournalCollector.');
|
||||
}
|
||||
|
||||
$countQuery = clone $this->query;
|
||||
|
||||
// dont need some fields:
|
||||
$countQuery->getQuery()->limit = null;
|
||||
$countQuery->getQuery()->offset = null;
|
||||
$countQuery->getQuery()->unionLimit = null;
|
||||
$countQuery->getQuery()->groups = null;
|
||||
$countQuery->getQuery()->orders = null;
|
||||
$countQuery->groupBy('accounts.user_id');
|
||||
$this->count = $countQuery->count();
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function disableFilter(): JournalCollectorInterface
|
||||
{
|
||||
$this->filterTransfers = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournals(): Collection
|
||||
{
|
||||
$this->run = true;
|
||||
$set = $this->query->get(array_values($this->fields));
|
||||
Log::debug(sprintf('Count of set is %d', $set->count()));
|
||||
$set = $this->filterTransfers($set);
|
||||
Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count()));
|
||||
|
||||
// loop for decryption.
|
||||
$set->each(
|
||||
function (Transaction $transaction) {
|
||||
$transaction->date = new Carbon($transaction->date);
|
||||
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
|
||||
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
|
||||
}
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LengthAwarePaginator
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getPaginatedJournals(): LengthAwarePaginator
|
||||
{
|
||||
if ($this->run === true) {
|
||||
throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.');
|
||||
}
|
||||
$this->count();
|
||||
$set = $this->getJournals();
|
||||
$journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page);
|
||||
|
||||
return $journals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): JournalCollectorInterface
|
||||
{
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$this->query->whereIn('transactions.account_id', $accountIds);
|
||||
Log::debug(sprintf('setAccounts: %s', join(', ', $accountIds)));
|
||||
$this->accountIds = $accountIds;
|
||||
}
|
||||
|
||||
if ($accounts->count() > 1) {
|
||||
$this->filterTransfers = true;
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setAllAssetAccounts(): JournalCollectorInterface
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class, [$this->user]);
|
||||
$accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$this->query->whereIn('transactions.account_id', $accountIds);
|
||||
$this->accountIds = $accountIds;
|
||||
}
|
||||
|
||||
if ($accounts->count() > 1) {
|
||||
$this->filterTransfers = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $bills
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setBills(Collection $bills): JournalCollectorInterface
|
||||
{
|
||||
if ($bills->count() > 0) {
|
||||
$billIds = $bills->pluck('id')->toArray();
|
||||
$this->query->whereIn('transaction_journals.bill_id', $billIds);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setBudget(Budget $budget): JournalCollectorInterface
|
||||
{
|
||||
$this->joinBudgetTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($budget) {
|
||||
$q->where('budget_transaction.budget_id', $budget->id);
|
||||
$q->orWhere('budget_transaction_journal.budget_id', $budget->id);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): JournalCollectorInterface
|
||||
{
|
||||
$budgetIds = $budgets->pluck('id')->toArray();
|
||||
if (count($budgetIds) === 0) {
|
||||
return $this;
|
||||
}
|
||||
$this->joinBudgetTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($budgetIds) {
|
||||
$q->whereIn('budget_transaction.budget_id', $budgetIds);
|
||||
$q->orWhereIn('budget_transaction_journal.budget_id', $budgetIds);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): JournalCollectorInterface
|
||||
{
|
||||
$categoryIds = $categories->pluck('id')->toArray();
|
||||
if (count($categoryIds) === 0) {
|
||||
return $this;
|
||||
}
|
||||
$this->joinCategoryTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($categoryIds) {
|
||||
$q->whereIn('category_transaction.category_id', $categoryIds);
|
||||
$q->orWhereIn('category_transaction_journal.category_id', $categoryIds);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setCategory(Category $category): JournalCollectorInterface
|
||||
{
|
||||
$this->joinCategoryTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($category) {
|
||||
$q->where('category_transaction.category_id', $category->id);
|
||||
$q->orWhere('category_transaction_journal.category_id', $category->id);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setLimit(int $limit): JournalCollectorInterface
|
||||
{
|
||||
$this->limit = $limit;
|
||||
$this->query->limit($limit);
|
||||
Log::debug(sprintf('Set limit to %d', $limit));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setOffset(int $offset): JournalCollectorInterface
|
||||
{
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setPage(int $page): JournalCollectorInterface
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
if ($page > 0) {
|
||||
$page--;
|
||||
}
|
||||
Log::debug(sprintf('Page is %d', $page));
|
||||
|
||||
if (!is_null($this->limit)) {
|
||||
$offset = ($this->limit * $page);
|
||||
$this->offset = $offset;
|
||||
$this->query->skip($offset);
|
||||
Log::debug(sprintf('Changed offset to %d', $offset));
|
||||
}
|
||||
if (is_null($this->limit)) {
|
||||
Log::debug('The limit is zero, cannot set the page.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface
|
||||
{
|
||||
if ($start <= $end) {
|
||||
$this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d'));
|
||||
$this->query->where('transaction_journals.date', '<=', $end->format('Y-m-d'));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setTag(Tag $tag): JournalCollectorInterface
|
||||
{
|
||||
$this->joinTagTables();
|
||||
$this->query->where('tag_transaction_journal.tag_id', $tag->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setTypes(array $types): JournalCollectorInterface
|
||||
{
|
||||
if (count($types) > 0) {
|
||||
Log::debug('Set query collector types', $types);
|
||||
$this->query->whereIn('transaction_types.type', $types);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withBudgetInformation(): JournalCollectorInterface
|
||||
{
|
||||
$this->joinBudgetTables();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withCategoryInformation(): JournalCollectorInterface
|
||||
{
|
||||
|
||||
$this->joinCategoryTables();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withOpposingAccount(): JournalCollectorInterface
|
||||
{
|
||||
$this->joinOpposingTables();
|
||||
|
||||
$accountIds = $this->accountIds;
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q1) use ($accountIds) {
|
||||
// set 1:
|
||||
// where source is in the set of $accounts
|
||||
// but destination is not.
|
||||
$q1->where(
|
||||
function (EloquentBuilder $q2) use ($accountIds) {
|
||||
// transactions.account_id in set
|
||||
$q2->whereIn('transactions.account_id', $accountIds);
|
||||
// opposing.account_id not in set
|
||||
$q2->whereNotIn('opposing.account_id', $accountIds);
|
||||
|
||||
}
|
||||
);
|
||||
// set 1:
|
||||
// where source is not in the set of $accounts
|
||||
// but destination is.
|
||||
$q1->orWhere(
|
||||
function (EloquentBuilder $q3) use ($accountIds) {
|
||||
// transactions.account_id not in set
|
||||
$q3->whereNotIn('transactions.account_id', $accountIds);
|
||||
// B in set
|
||||
// opposing.account_id not in set
|
||||
$q3->whereIn('opposing.account_id', $accountIds);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withoutBudget(): JournalCollectorInterface
|
||||
{
|
||||
$this->joinBudgetTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('budget_transaction.budget_id');
|
||||
$q->whereNull('budget_transaction_journal.budget_id');
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withoutCategory(): JournalCollectorInterface
|
||||
{
|
||||
$this->joinCategoryTables();
|
||||
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('category_transaction.category_id');
|
||||
$q->whereNull('category_transaction_journal.category_id');
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the set of accounts used by the collector includes more than one asset
|
||||
* account, chances are the set include double entries: transfers get selected
|
||||
* on both the source, and then again on the destination account.
|
||||
*
|
||||
* This method filters them out.
|
||||
*
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function filterTransfers(Collection $set): Collection
|
||||
{
|
||||
if ($this->filterTransfers) {
|
||||
$set = $set->filter(
|
||||
function (Transaction $transaction) {
|
||||
if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) {
|
||||
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Included journal #%d (transaction #%d) because its a %s with amount %f',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->id,
|
||||
$transaction->transaction_type_type,
|
||||
$transaction->transaction_amount
|
||||
)
|
||||
);
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Removed journal #%d (transaction #%d) because its a %s with amount %f',
|
||||
$transaction->transaction_journal_id,
|
||||
$transaction->id,
|
||||
$transaction->transaction_type_type,
|
||||
$transaction->transaction_amount
|
||||
)
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinBudgetTables()
|
||||
{
|
||||
if (!$this->joinedBudget) {
|
||||
// join some extra tables:
|
||||
$this->joinedBudget = true;
|
||||
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
|
||||
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
|
||||
$this->fields[] = 'budget_transaction.budget_id as transaction_budget_id';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinCategoryTables()
|
||||
{
|
||||
if (!$this->joinedCategory) {
|
||||
// join some extra tables:
|
||||
$this->joinedCategory = true;
|
||||
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
|
||||
$this->fields[] = 'category_transaction_journal.category_id as transaction_journal_category_id';
|
||||
$this->fields[] = 'category_transaction.category_id as transaction_category_id';
|
||||
}
|
||||
}
|
||||
|
||||
private function joinOpposingTables()
|
||||
{
|
||||
if (!$this->joinedOpposing) {
|
||||
// join opposing transaction (hard):
|
||||
$this->query->leftJoin(
|
||||
'transactions as opposing', function (JoinClause $join) {
|
||||
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
|
||||
->where('opposing.identifier', '=', DB::raw('transactions.identifier'))
|
||||
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'));
|
||||
}
|
||||
);
|
||||
$this->query->leftJoin('accounts as opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id');
|
||||
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
|
||||
$this->query->whereNull('opposing.deleted_at');
|
||||
|
||||
$this->fields[] = 'opposing.account_id as opposing_account_id';
|
||||
$this->fields[] = 'opposing_accounts.name as opposing_account_name';
|
||||
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
|
||||
$this->fields[] = 'opposing_account_types.type as opposing_account_type';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function joinTagTables()
|
||||
{
|
||||
if (!$this->joinedTag) {
|
||||
// join some extra tables:
|
||||
$this->joinedTag = true;
|
||||
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EloquentBuilder
|
||||
*/
|
||||
private function startQuery(): EloquentBuilder
|
||||
{
|
||||
|
||||
$query = Transaction
|
||||
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
|
||||
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
|
||||
->whereNull('transactions.deleted_at')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC');
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
}
|
166
app/Helpers/Collector/JournalCollectorInterface.php
Normal file
166
app/Helpers/Collector/JournalCollectorInterface.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalCollectorInterface.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface JournalCollectorInterface
|
||||
*
|
||||
* @package FireflyIII\Helpers\Collector
|
||||
*/
|
||||
interface JournalCollectorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function disableFilter(): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournals(): Collection;
|
||||
|
||||
/**
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function getPaginatedJournals():LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setAccounts(Collection $accounts): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setAllAssetAccounts(): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $bills
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setBills(Collection $bills): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setBudget(Budget $budget): JournalCollectorInterface;
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $budgets
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setBudgets(Collection $budgets): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setCategories(Collection $categories): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setCategory(Category $category): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setLimit(int $limit): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setOffset(int $offset): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setPage(int $page): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setTag(Tag $tag): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
*
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function setTypes(array $types): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withBudgetInformation(): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withCategoryInformation(): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withOpposingAccount(): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withoutBudget(): JournalCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return JournalCollectorInterface
|
||||
*/
|
||||
public function withoutCategory(): JournalCollectorInterface;
|
||||
}
|
@@ -26,6 +26,8 @@ use Route;
|
||||
*/
|
||||
class Help implements HelpInterface
|
||||
{
|
||||
/** @var string */
|
||||
protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
@@ -35,7 +37,9 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function getFromCache(string $route, string $language): string
|
||||
{
|
||||
return Cache::get('help.' . $route . '.' . $language);
|
||||
$line = sprintf('help.%s.%s', $route, $language);
|
||||
|
||||
return Cache::get($line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,9 +53,10 @@ class Help implements HelpInterface
|
||||
|
||||
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
|
||||
Log::debug(sprintf('Trying to get %s...', $uri));
|
||||
$opt = ['useragent' => $this->userAgent];
|
||||
$content = '';
|
||||
try {
|
||||
$result = Requests::get($uri);
|
||||
$result = Requests::get($uri, [], $opt);
|
||||
} catch (Requests_Exception $e) {
|
||||
Log::error($e);
|
||||
|
||||
@@ -69,6 +74,9 @@ class Help implements HelpInterface
|
||||
$converter = new CommonMarkConverter();
|
||||
$content = $converter->convertToHtml($content);
|
||||
}
|
||||
if (strlen($content) === 0) {
|
||||
Log::warning('Raw content length is zero.');
|
||||
}
|
||||
|
||||
return $content;
|
||||
|
||||
@@ -93,7 +101,8 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function inCache(string $route, string $language):bool
|
||||
{
|
||||
$result = Cache::has('help.' . $route . '.' . $language);
|
||||
$line = sprintf('help.%s.%s', $route, $language);
|
||||
$result = Cache::has($line);
|
||||
if ($result) {
|
||||
Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language));
|
||||
}
|
||||
@@ -115,8 +124,12 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function putInCache(string $route, string $language, string $content)
|
||||
{
|
||||
$key = 'help.' . $route . '.' . $language;
|
||||
Log::debug(sprintf('Will store entry in cache: %s', $key));
|
||||
Cache::put($key, $content, 10080); // a week.
|
||||
$key = sprintf('help.%s.%s', $route, $language);
|
||||
if (strlen($content) > 0) {
|
||||
Log::debug(sprintf('Will store entry in cache: %s', $key));
|
||||
Cache::put($key, $content, 10080); // a week.
|
||||
return;
|
||||
}
|
||||
Log::info(sprintf('Will not cache %s because content is empty.', $key));
|
||||
}
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ interface HelpInterface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inCache(string $route, string $language ): bool;
|
||||
public function inCache(string $route, string $language): bool;
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
|
@@ -20,7 +20,6 @@ use FireflyIII\Helpers\Collection\BudgetLine;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@@ -44,57 +43,19 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // at 43, its ok.
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
* @return array
|
||||
*/
|
||||
public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection
|
||||
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget-year');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
$budgets = $this->repository->getBudgets();
|
||||
$report = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
|
||||
$data = $this->filterBudgetPeriodReport($report);
|
||||
|
||||
$current = clone $start;
|
||||
$return = new Collection;
|
||||
$set = $this->repository->getBudgets();
|
||||
$budgets = [];
|
||||
$spent = [];
|
||||
$headers = $this->createYearHeaders($current, $end);
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
$id = $budget->id;
|
||||
$budgets[$id] = $budget->name;
|
||||
$current = clone $start;
|
||||
$budgetData = $this->getBudgetSpentData($current, $end, $budget, $accounts);
|
||||
$sum = $budgetData['sum'];
|
||||
$spent[$id] = $budgetData['spent'];
|
||||
|
||||
if (bccomp('0', $sum) === 0) {
|
||||
// not spent anything.
|
||||
unset($spent[$id]);
|
||||
unset($budgets[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
$return->put('headers', $headers);
|
||||
$return->put('budgets', $budgets);
|
||||
$return->put('spent', $spent);
|
||||
|
||||
$cache->store($return);
|
||||
|
||||
return $return;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,33 +143,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
{
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
}
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
@@ -230,48 +164,29 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $current
|
||||
* @param Carbon $end
|
||||
* Filters empty results from getBudgetPeriodReport
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function createYearHeaders(Carbon $current, Carbon $end): array
|
||||
private function filterBudgetPeriodReport(array $data): array
|
||||
{
|
||||
$headers = [];
|
||||
while ($current < $end) {
|
||||
$short = $current->format('m-Y');
|
||||
$headers[$short] = $current->formatLocalized((string)trans('config.month'));
|
||||
$current->addMonth();
|
||||
/**
|
||||
* @var int $budgetId
|
||||
* @var array $set
|
||||
*/
|
||||
foreach ($data as $budgetId => $set) {
|
||||
$sum = '0';
|
||||
foreach ($set['entries'] as $amount) {
|
||||
$sum = bcadd($amount, $sum);
|
||||
}
|
||||
$data[$budgetId]['sum'] = $sum;
|
||||
if (bccomp('0', $sum) === 0) {
|
||||
unset($data[$budgetId]);
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $current
|
||||
* @param Carbon $end
|
||||
* @param Budget $budget
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBudgetSpentData(Carbon $current, Carbon $end, Budget $budget, Collection $accounts): array
|
||||
{
|
||||
$sum = '0';
|
||||
$spent = [];
|
||||
while ($current < $end) {
|
||||
$currentEnd = clone $current;
|
||||
$currentEnd->endOfMonth();
|
||||
$format = $current->format('m-Y');
|
||||
$budgetSpent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $current, $currentEnd);
|
||||
$spent[$format] = $budgetSpent;
|
||||
$sum = bcadd($sum, $budgetSpent);
|
||||
$current->addMonth();
|
||||
}
|
||||
|
||||
return [
|
||||
'spent' => $spent,
|
||||
'sum' => $sum,
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@@ -25,14 +25,15 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface BudgetReportHelperInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return Collection
|
||||
* @return array
|
||||
*/
|
||||
public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection;
|
||||
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
|
@@ -19,18 +19,15 @@ use FireflyIII\Helpers\Collection\BillLine;
|
||||
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
|
||||
use FireflyIII\Helpers\Collection\Expense;
|
||||
use FireflyIII\Helpers\Collection\Income;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Helpers\FiscalHelperInterface;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use stdClass;
|
||||
|
||||
@@ -44,20 +41,16 @@ class ReportHelper implements ReportHelperInterface
|
||||
|
||||
/** @var BudgetRepositoryInterface */
|
||||
protected $budgetRepository;
|
||||
/** @var TagRepositoryInterface */
|
||||
protected $tagRepository;
|
||||
|
||||
/**
|
||||
* ReportHelper constructor.
|
||||
*
|
||||
*
|
||||
* @param BudgetRepositoryInterface $budgetRepository
|
||||
* @param TagRepositoryInterface $tagRepository
|
||||
*/
|
||||
public function __construct(BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository)
|
||||
public function __construct(BudgetRepositoryInterface $budgetRepository)
|
||||
{
|
||||
$this->budgetRepository = $budgetRepository;
|
||||
$this->tagRepository = $tagRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,37 +70,44 @@ class ReportHelper implements ReportHelperInterface
|
||||
/** @var BillRepositoryInterface $repository */
|
||||
$repository = app(BillRepositoryInterface::class);
|
||||
$bills = $repository->getBillsForAccounts($accounts);
|
||||
$journals = $repository->getAllJournalsInRange($bills, $start, $end);
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills);
|
||||
$journals = $collector->getJournals();
|
||||
$collection = new BillCollection;
|
||||
$collection->setStartDate($start);
|
||||
$collection->setEndDate($end);
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
$billLine = new BillLine;
|
||||
$billLine->setBill($bill);
|
||||
$billLine->setActive(intval($bill->active) === 1);
|
||||
$billLine->setMin(strval($bill->amount_min));
|
||||
$billLine->setMax(strval($bill->amount_max));
|
||||
$billLine->setHit(false);
|
||||
// is hit in period?
|
||||
|
||||
$entry = $journals->filter(
|
||||
function (TransactionJournal $journal) use ($bill) {
|
||||
return $journal->bill_id === $bill->id;
|
||||
function (Transaction $transaction) use ($bill) {
|
||||
return $transaction->bill_id === $bill->id;
|
||||
}
|
||||
);
|
||||
$first = $entry->first();
|
||||
if (!is_null($first)) {
|
||||
$billLine->setTransactionJournalId($first->id);
|
||||
$billLine->setAmount($first->journalAmount);
|
||||
$billLine->setAmount($first->transaction_amount);
|
||||
$billLine->setLastHitDate($first->date);
|
||||
$billLine->setHit(true);
|
||||
}
|
||||
|
||||
// non active AND non hit? do not add:
|
||||
// bill is active, or bill is hit:
|
||||
if ($billLine->isActive() || $billLine->isHit()) {
|
||||
$collection->addBill($billLine);
|
||||
}
|
||||
}
|
||||
|
||||
// do some extra filtering.
|
||||
$collection->filterBills();
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
@@ -231,104 +231,4 @@ class ReportHelper implements ReportHelperInterface
|
||||
return $months;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of tags and their comparitive size with amounts bla bla.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array
|
||||
{
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$set = Tag::
|
||||
leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id')
|
||||
->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin(
|
||||
'transactions AS source', function (JoinClause $join) {
|
||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0');
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions AS destination', function (JoinClause $join) {
|
||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', '0');
|
||||
}
|
||||
)
|
||||
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
|
||||
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
|
||||
->where(
|
||||
// source.account_id in accountIds XOR destination.account_id in accountIds
|
||||
function (Builder $query) use ($ids) {
|
||||
$query->where(
|
||||
function (Builder $q1) use ($ids) {
|
||||
$q1->whereIn('source.account_id', $ids)
|
||||
->whereNotIn('destination.account_id', $ids);
|
||||
}
|
||||
)->orWhere(
|
||||
function (Builder $q2) use ($ids) {
|
||||
$q2->whereIn('destination.account_id', $ids)
|
||||
->whereNotIn('source.account_id', $ids);
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
->get(['tags.id', 'tags.tag', 'transaction_journals.id as journal_id', 'destination.amount']);
|
||||
$collection = [];
|
||||
if ($set->count() === 0) {
|
||||
return $collection;
|
||||
}
|
||||
/** @var Tag $entry */
|
||||
foreach ($set as $entry) {
|
||||
// less than zero? multiply to be above zero.
|
||||
$amount = $entry->amount;
|
||||
$id = intval($entry->id);
|
||||
$previousAmount = $collection[$id]['amount'] ?? '0';
|
||||
$collection[$id] = [
|
||||
'id' => $id,
|
||||
'tag' => $entry->tag,
|
||||
'amount' => bcadd($previousAmount, $amount),
|
||||
];
|
||||
}
|
||||
|
||||
// cleanup collection (match "fonts")
|
||||
$max = strval(max(array_column($collection, 'amount')));
|
||||
foreach ($collection as $id => $entry) {
|
||||
$size = bcdiv($entry['amount'], $max, 4);
|
||||
if (bccomp($size, '0.25') === -1) {
|
||||
$size = '0.5';
|
||||
}
|
||||
$collection[$id]['fontsize'] = $size;
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
{
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
}
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
}
|
||||
|
@@ -80,15 +80,4 @@ interface ReportHelperInterface
|
||||
*/
|
||||
public function listOfMonths(Carbon $date): array;
|
||||
|
||||
/**
|
||||
* Returns an array of tags and their comparitive size with amounts bla bla.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array;
|
||||
|
||||
}
|
||||
|
@@ -13,19 +13,24 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\AccountFormRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -65,9 +70,18 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function create(string $what = 'asset')
|
||||
{
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$subTitle = trans('firefly.make_new_' . $what . '_account');
|
||||
Session::flash('preFilled', []);
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
||||
$defaultCurrency = Amount::getDefaultCurrency();
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$subTitle = trans('firefly.make_new_' . $what . '_account');
|
||||
Session::flash(
|
||||
'preFilled',
|
||||
[
|
||||
'currency_id' => $defaultCurrency->id,
|
||||
]
|
||||
);
|
||||
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (session('accounts.create.fromStore') !== true) {
|
||||
@@ -77,7 +91,7 @@ class AccountController extends Controller
|
||||
Session::flash('gaEventCategory', 'accounts');
|
||||
Session::flash('gaEventAction', 'create-' . $what);
|
||||
|
||||
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
|
||||
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies'));
|
||||
|
||||
}
|
||||
|
||||
@@ -134,6 +148,9 @@ class AccountController extends Controller
|
||||
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (session('accounts.edit.fromUpdate') !== true) {
|
||||
@@ -157,12 +174,13 @@ class AccountController extends Controller
|
||||
'openingBalanceDate' => $openingBalanceDate,
|
||||
'openingBalance' => $openingBalanceAmount,
|
||||
'virtualBalance' => $account->virtual_balance,
|
||||
'currency_id' => $account->getMeta('currency_id'),
|
||||
];
|
||||
Session::flash('preFilled', $preFilled);
|
||||
Session::flash('gaEventCategory', 'accounts');
|
||||
Session::flash('gaEventAction', 'edit-' . $what);
|
||||
|
||||
return view('accounts.edit', compact('account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
|
||||
return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,13 +220,12 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $tasker
|
||||
* @param ARI $repository
|
||||
* @param Account $account
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account)
|
||||
public function show(JournalCollectorInterface $collector, Account $account)
|
||||
{
|
||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
@@ -217,88 +234,70 @@ class AccountController extends Controller
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$subTitle = $account->name;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$page = intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// grab those journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id);
|
||||
|
||||
// grouped other months thing:
|
||||
// oldest transaction in account:
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
// generate entries for each period (and cache those)
|
||||
$entries = $this->periodEntries($account);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('account-show');
|
||||
$cache->addProperty($account->id);
|
||||
|
||||
|
||||
if ($cache->has()) {
|
||||
$entries = $cache->get();
|
||||
|
||||
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle'));
|
||||
}
|
||||
|
||||
// only include asset accounts when this account is an asset:
|
||||
$assets = new Collection;
|
||||
if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) {
|
||||
$assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
||||
}
|
||||
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
$spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
||||
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $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('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle'));
|
||||
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $tasker
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
* @param ARI $repository
|
||||
* @param Account $account
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date)
|
||||
public function showAll(AccountRepositoryInterface $repository, Account $account)
|
||||
{
|
||||
$subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id . '/all');
|
||||
|
||||
// get oldest and newest journal for account:
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$end = $repository->newestJournalDate($account);
|
||||
|
||||
return view('accounts.show_with_date', compact('account', 'journals', 'subTitle', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showWithDate(Account $account, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')';
|
||||
$page = intval(Input::get('page'));
|
||||
$page = $page === 0 ? 1 : $page;
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
|
||||
|
||||
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon'));
|
||||
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon', 'start', 'end'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,6 +376,63 @@ class AccountController extends Controller
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range)
|
||||
* and for each period, the amount of money spent and earned. This is a complex operation which is cached for
|
||||
* performance reasons.
|
||||
*
|
||||
* @param Account $account The account involved.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function periodEntries(Account $account): Collection
|
||||
{
|
||||
/** @var ARI $repository */
|
||||
$repository = app(ARI::class);
|
||||
/** @var AccountTaskerInterface $tasker */
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
// properties for cache
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('account-show-period-entries');
|
||||
$cache->addProperty($account->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
Log::debug('Entries are cached, return cache.');
|
||||
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
// only include asset accounts when this account is an asset:
|
||||
$assets = new Collection;
|
||||
if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) {
|
||||
$assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
|
||||
}
|
||||
Log::debug('Going to get period expenses and incomes.');
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
$spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
|
||||
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $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 $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
|
@@ -14,7 +14,6 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers\Admin;
|
||||
|
||||
|
||||
use Config;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\ConfigurationRequest;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
@@ -59,9 +58,11 @@ class ConfigurationController extends Controller
|
||||
|
||||
// all available configuration and their default value in case
|
||||
// they don't exist yet.
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
|
||||
return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode'));
|
||||
return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite'));
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +78,8 @@ class ConfigurationController extends Controller
|
||||
|
||||
// store config values
|
||||
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
|
||||
FireflyConfig::set('must_confirm_account', $data['must_confirm_account']);
|
||||
FireflyConfig::set('is_demo_site', $data['is_demo_site']);
|
||||
|
||||
// flash message
|
||||
Session::flash('success', strval(trans('firefly.configuration_updated')));
|
||||
|
@@ -24,7 +24,7 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
class HomeController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return mixed
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
@@ -14,6 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers\Admin;
|
||||
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
@@ -46,30 +47,32 @@ class UserController extends Controller
|
||||
*/
|
||||
public function index(UserRepositoryInterface $repository)
|
||||
{
|
||||
$title = strval(trans('firefly.administration'));
|
||||
$mainTitleIcon = 'fa-hand-spock-o';
|
||||
$subTitle = strval(trans('firefly.user_administration'));
|
||||
$subTitleIcon = 'fa-users';
|
||||
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
|
||||
$users = $repository->all();
|
||||
$title = strval(trans('firefly.administration'));
|
||||
$mainTitleIcon = 'fa-hand-spock-o';
|
||||
$subTitle = strval(trans('firefly.user_administration'));
|
||||
$subTitleIcon = 'fa-users';
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
$users = $repository->all();
|
||||
|
||||
// add meta stuff.
|
||||
$users->each(
|
||||
function (User $user) use ($confirmAccount) {
|
||||
// is user activated?
|
||||
$isConfirmed = Preferences::getForUser($user, 'user_confirmed', false)->data;
|
||||
function (User $user) use ($mustConfirmAccount) {
|
||||
$list = ['user_confirmed', 'twoFactorAuthEnabled', 'twoFactorAuthSecret', 'registration_ip_address', 'confirmation_ip_address'];
|
||||
$preferences = Preferences::getArrayForUser($user, $list);
|
||||
|
||||
$user->activated = true;
|
||||
if ($isConfirmed === false && $confirmAccount === true) {
|
||||
if (!($preferences['user_confirmed'] === true) && $mustConfirmAccount === true) {
|
||||
$user->activated = false;
|
||||
}
|
||||
|
||||
$user->isAdmin = $user->hasRole('owner');
|
||||
$is2faEnabled = Preferences::getForUser($user, 'twoFactorAuthEnabled', false)->data;
|
||||
$has2faSecret = !is_null(Preferences::getForUser($user, 'twoFactorAuthSecret'));
|
||||
$is2faEnabled = $preferences['twoFactorAuthEnabled'] === true;
|
||||
$has2faSecret = !is_null($preferences['twoFactorAuthSecret']);
|
||||
$user->has2FA = false;
|
||||
if ($is2faEnabled && $has2faSecret) {
|
||||
$user->has2FA = true;
|
||||
}
|
||||
$user->prefs = $preferences;
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -57,7 +57,7 @@ class AttachmentController extends Controller
|
||||
/**
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function delete(Attachment $attachment)
|
||||
{
|
||||
|
@@ -75,7 +75,8 @@ class LoginController extends Controller
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
|
||||
$lockedOut = $this->hasTooManyLoginAttempts($request);
|
||||
if ($lockedOut) {
|
||||
$this->fireLockoutEvent($request);
|
||||
|
||||
return $this->sendLockoutResponse($request);
|
||||
@@ -188,7 +189,7 @@ class LoginController extends Controller
|
||||
];
|
||||
|
||||
Mail::send(
|
||||
['emails.blocked-login-html', 'emails.blocked-login'], $fields, function (Message $message) use ($email, $user) {
|
||||
['emails.blocked-login-html', 'emails.blocked-login-text'], $fields, function (Message $message) use ($email, $user) {
|
||||
$message->to($email, $email)->subject('Blocked a login attempt from ' . trim($user->email) . '.');
|
||||
}
|
||||
);
|
||||
|
@@ -123,7 +123,11 @@ class RegisterController extends Controller
|
||||
*/
|
||||
public function showRegistrationForm(Request $request)
|
||||
{
|
||||
$showDemoWarning = config('firefly.show-demo-warning', false);
|
||||
// is demo site?
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', Config::get('firefly.configuration.is_demo_site'))->data;
|
||||
|
||||
// activate account?
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', Config::get('firefly.configuration.must_confirm_account'))->data;
|
||||
|
||||
// is allowed to?
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
|
||||
@@ -136,7 +140,7 @@ class RegisterController extends Controller
|
||||
|
||||
$email = $request->old('email');
|
||||
|
||||
return view('auth.register', compact('showDemoWarning', 'email'));
|
||||
return view('auth.register', compact('isDemoSite', 'email', 'mustConfirmAccount'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,7 +221,7 @@ class RegisterController extends Controller
|
||||
];
|
||||
|
||||
Mail::send(
|
||||
['emails.blocked-registration-html', 'emails.blocked-registration'], $fields, function (Message $message) use ($email, $domain) {
|
||||
['emails.blocked-registration-html', 'emails.blocked-registration-text'], $fields, function (Message $message) use ($email, $domain) {
|
||||
$message->to($email, $email)->subject('Blocked a registration attempt with domain ' . $domain . '.');
|
||||
}
|
||||
);
|
||||
|
@@ -14,10 +14,12 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\BillFormRequest;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -200,10 +202,15 @@ class BillController extends Controller
|
||||
$year = $date->year;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$journals = $repository->getJournals($bill, $page, $pageSize);
|
||||
$yearAverage = $repository->getYearAverage($bill, $date);
|
||||
$overallAverage = $repository->getOverallAverage($bill);
|
||||
|
||||
// use collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/bills/show/' . $bill->id);
|
||||
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
|
||||
$hideBill = true;
|
||||
$subTitle = e($bill->name);
|
||||
|
@@ -17,13 +17,13 @@ use Amount;
|
||||
use Carbon\Carbon;
|
||||
use Config;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Log;
|
||||
@@ -247,31 +247,28 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function noBudget(BudgetRepositoryInterface $repository)
|
||||
public function noBudget()
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$journals = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$list = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans(
|
||||
'firefly.without_budget_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
$list->setPath('/budgets/list/noBudget');
|
||||
|
||||
return view('budgets.noBudget', compact('list', 'subTitle'));
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/list/noBudget');
|
||||
|
||||
return view('budgets.no-budget', compact('journals', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,14 +302,13 @@ class BudgetController extends Controller
|
||||
$start = session('first', Carbon::create()->startOfYear());
|
||||
$end = new Carbon;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
// collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id);
|
||||
|
||||
|
||||
@@ -330,33 +326,36 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param AccountRepositoryInterface $accountRepository
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
* @param Budget $budget
|
||||
* @param LimitRepetition $repetition
|
||||
*
|
||||
* @return View
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function showWithRepetition(
|
||||
BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget, LimitRepetition $repetition
|
||||
) {
|
||||
public function showWithRepetition(Budget $budget, LimitRepetition $repetition)
|
||||
{
|
||||
if ($repetition->budgetLimit->budget->id != $budget->id) {
|
||||
throw new FireflyException('This budget limit is not part of this budget.');
|
||||
}
|
||||
$start = $repetition->startdate;
|
||||
$end = $repetition->enddate;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget
|
||||
$count = $journals->count();
|
||||
$journals = $journals->slice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
|
||||
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$start = $repetition->startdate;
|
||||
$end = $repetition->enddate;
|
||||
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans(
|
||||
'firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]
|
||||
);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
|
||||
|
||||
|
||||
// collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
|
||||
|
||||
|
||||
|
@@ -14,13 +14,13 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\CategoryFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
use Navigation;
|
||||
@@ -147,23 +147,25 @@ class CategoryController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function noCategory(CRI $repository)
|
||||
public function noCategory()
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->startOfMonth());
|
||||
$list = $repository->journalsInPeriodWithoutCategory(new Collection(), [], $start, $end); // category
|
||||
$end = session('end', Carbon::now()->startOfMonth());
|
||||
|
||||
// new collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
|
||||
$journals = $collector->getJournals();
|
||||
$subTitle = trans(
|
||||
'firefly.without_category_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
|
||||
return view('categories.noCategory', compact('list', 'subTitle'));
|
||||
return view('categories.no-category', compact('journals', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,16 +182,17 @@ class CategoryController extends Controller
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = $category->name;
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
|
||||
// use journal collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id);
|
||||
|
||||
// oldest transaction in category:
|
||||
@@ -218,7 +221,7 @@ class CategoryController extends Controller
|
||||
|
||||
|
||||
$categoryCollection = new Collection([$category]);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
|
||||
while ($end >= $start) {
|
||||
$end = Navigation::startOfPeriod($end, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||
@@ -233,18 +236,16 @@ class CategoryController extends Controller
|
||||
}
|
||||
$cache->store($entries);
|
||||
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle'));
|
||||
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param Category $category
|
||||
*
|
||||
* @param $date
|
||||
* @param Category $category
|
||||
* @param $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function showWithDate(CRI $repository, Category $category, string $date)
|
||||
public function showWithDate(Category $category, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
@@ -252,15 +253,16 @@ class CategoryController extends Controller
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$subTitle = $category->name;
|
||||
$hideCategory = true; // used in list.
|
||||
$page = intval(Input::get('page'));
|
||||
$pageSize = Preferences::get('transactionPageSize', 50)->data;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category
|
||||
$count = $set->count();
|
||||
$subSet = $set->splice($offset, $pageSize);
|
||||
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// new collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id . '/' . $date);
|
||||
|
||||
|
||||
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
|
||||
}
|
||||
|
||||
|
@@ -17,10 +17,15 @@ use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
@@ -99,6 +104,87 @@ class AccountController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function expenseByBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expenseByBudget');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
|
||||
// grab all journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$transactions = $collector->getJournals();
|
||||
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$jrnlBudgetId = intval($transaction->transaction_journal_budget_id);
|
||||
$transBudgetId = intval($transaction->transaction_budget_id);
|
||||
$budgetId = max($jrnlBudgetId, $transBudgetId);
|
||||
|
||||
$result[$budgetId] = $result[$budgetId] ?? '0';
|
||||
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
|
||||
}
|
||||
$names = $this->getBudgetNames(array_keys($result));
|
||||
$data = $this->generator->pieChart($result, $names);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function expenseByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('expenseByCategory');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// grab all journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$transactions = $collector->getJournals();
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||
$transCatId = intval($transaction->transaction_category_id);
|
||||
$categoryId = max($jrnlCatId, $transCatId);
|
||||
|
||||
$result[$categoryId] = $result[$categoryId] ?? '0';
|
||||
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
|
||||
}
|
||||
$names = $this->getCategoryNames(array_keys($result));
|
||||
$data = $this->generator->pieChart($result, $names);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for all the user's frontpage accounts.
|
||||
*
|
||||
@@ -108,41 +194,52 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function frontpage(AccountRepositoryInterface $repository)
|
||||
{
|
||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
||||
$end = clone session('end', Carbon::now()->endOfMonth());
|
||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
||||
$end = clone session('end', Carbon::now()->endOfMonth());
|
||||
$frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray());
|
||||
$accounts = $repository->getAccountsById($frontPage->data);
|
||||
|
||||
return Response::json($this->accountBalanceChart($start, $end, $accounts));
|
||||
}
|
||||
|
||||
// chart properties for cache:
|
||||
/**
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function incomeByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('frontpage');
|
||||
$cache->addProperty('accounts');
|
||||
$cache->addProperty('incomeByCategory');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray());
|
||||
$accounts = $repository->getAccountsById($frontPage->data);
|
||||
// grab all journals:
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
|
||||
$transactions = $collector->getJournals();
|
||||
$result = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||
$transCatId = intval($transaction->transaction_category_id);
|
||||
$categoryId = max($jrnlCatId, $transCatId);
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$balances = [];
|
||||
$current = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||
$previous = round(array_values($range)[0], 2);
|
||||
while ($current <= $end) {
|
||||
$format = $current->format('Y-m-d');
|
||||
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
|
||||
$previous = $balance;
|
||||
$balances[] = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
$account->balances = $balances;
|
||||
$result[$categoryId] = $result[$categoryId] ?? '0';
|
||||
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
|
||||
}
|
||||
$data = $this->generator->frontpage($accounts, $start, $end);
|
||||
$names = $this->getCategoryNames(array_keys($result));
|
||||
$data = $this->generator->pieChart($result, $names);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,38 +253,7 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function report(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// 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());
|
||||
}
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$balances = [];
|
||||
$current = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||
$previous = round(array_values($range)[0], 2);
|
||||
while ($current <= $end) {
|
||||
$format = $current->format('Y-m-d');
|
||||
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
|
||||
$previous = $balance;
|
||||
$balances[] = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
$account->balances = $balances;
|
||||
}
|
||||
|
||||
// make chart:
|
||||
$data = $this->generator->frontpage($accounts, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
return Response::json($this->accountBalanceChart($start, $end, $accounts));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,7 +353,6 @@ class AccountController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param string $date
|
||||
@@ -340,4 +405,90 @@ class AccountController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function accountBalanceChart(Carbon $start, Carbon $end, Collection $accounts): array
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('account-balance-chart');
|
||||
$cache->addProperty($accounts);
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$balances = [];
|
||||
$current = clone $start;
|
||||
$range = Steam::balanceInRange($account, $start, clone $end);
|
||||
$previous = round(array_values($range)[0], 2);
|
||||
while ($current <= $end) {
|
||||
$format = $current->format('Y-m-d');
|
||||
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
|
||||
$previous = $balance;
|
||||
$balances[] = $balance;
|
||||
$current->addDay();
|
||||
}
|
||||
$account->balances = $balances;
|
||||
}
|
||||
$data = $this->generator->frontpage($accounts, $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $budgetIds
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBudgetNames(array $budgetIds): array
|
||||
{
|
||||
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$budgets = $repository->getBudgets();
|
||||
$grouped = $budgets->groupBy('id')->toArray();
|
||||
$return = [];
|
||||
foreach ($budgetIds as $budgetId) {
|
||||
if (isset($grouped[$budgetId])) {
|
||||
$return[$budgetId] = $grouped[$budgetId][0]['name'];
|
||||
}
|
||||
}
|
||||
$return[0] = trans('firefly.no_budget');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small helper function for some of the charts.
|
||||
*
|
||||
* @param array $categoryIds
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getCategoryNames(array $categoryIds): array
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$categories = $repository->getCategories();
|
||||
$grouped = $categories->groupBy('id')->toArray();
|
||||
$return = [];
|
||||
foreach ($categoryIds as $categoryId) {
|
||||
if (isset($grouped[$categoryId])) {
|
||||
$return[$categoryId] = $grouped[$categoryId][0]['name'];
|
||||
}
|
||||
}
|
||||
$return[0] = trans('firefly.noCategory');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -15,11 +15,13 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
@@ -64,12 +66,11 @@ class BillController extends Controller
|
||||
/**
|
||||
* Shows the overview for a bill. The min/max amount and matched journals.
|
||||
*
|
||||
* @param BillRepositoryInterface $repository
|
||||
* @param Bill $bill
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function single(BillRepositoryInterface $repository, Bill $bill)
|
||||
public function single(Bill $bill)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('single');
|
||||
@@ -80,12 +81,14 @@ class BillController extends Controller
|
||||
}
|
||||
|
||||
// get first transaction or today for start:
|
||||
$results = $repository->getJournals($bill, 1, 200);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]));
|
||||
$results = $collector->getJournals();
|
||||
|
||||
// resort:
|
||||
$results = $results->sortBy(
|
||||
function (TransactionJournal $journal) {
|
||||
return $journal->date->format('U');
|
||||
function (Transaction $transaction) {
|
||||
return $transaction->date->format('U');
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -15,14 +15,15 @@ namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
@@ -145,7 +146,6 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function frontpage(BudgetRepositoryInterface $repository)
|
||||
{
|
||||
Log::debug('Hello');
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
// chart properties for cache:
|
||||
@@ -175,7 +175,7 @@ class BudgetController extends Controller
|
||||
$allEntries = $allEntries->merge($collection);
|
||||
|
||||
}
|
||||
$entry = $this->spentInPeriodWithout($repository, $start, $end);
|
||||
$entry = $this->spentInPeriodWithout($start, $end);
|
||||
$allEntries->push($entry);
|
||||
$data = $this->generator->frontpage($allEntries);
|
||||
$cache->store($data);
|
||||
@@ -183,79 +183,6 @@ class BudgetController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection $budgets
|
||||
*
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function multiYear(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets)
|
||||
{
|
||||
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($budgets);
|
||||
$cache->addProperty('multiYearBudget');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
$budgetIds = $budgets->pluck('id')->toArray();
|
||||
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
$budgeted = [];
|
||||
$entries = new Collection;
|
||||
// filter budgets once:
|
||||
$repetitions = $repetitions->filter(
|
||||
function (LimitRepetition $repetition) use ($budgetIds) {
|
||||
if (in_array(strval($repetition->budget_id), $budgetIds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
/** @var LimitRepetition $repetition */
|
||||
foreach ($repetitions as $repetition) {
|
||||
$year = $repetition->startdate->year;
|
||||
if (isset($budgeted[$repetition->budget_id][$year])) {
|
||||
$budgeted[$repetition->budget_id][$year] = bcadd($budgeted[$repetition->budget_id][$year], $repetition->amount);
|
||||
continue;
|
||||
}
|
||||
$budgeted[$repetition->budget_id][$year] = $repetition->amount;
|
||||
}
|
||||
|
||||
foreach ($budgets as $budget) {
|
||||
$currentStart = clone $start;
|
||||
$entry = ['name' => $budget->name, 'spent' => [], 'budgeted' => []];
|
||||
while ($currentStart < $end) {
|
||||
// fix the date:
|
||||
$currentEnd = clone $currentStart;
|
||||
$year = $currentStart->year;
|
||||
$currentEnd->endOfYear();
|
||||
|
||||
$spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd);
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
|
||||
$entry['spent'][$year] = round($spent * -1, 2);
|
||||
$entry['budgeted'][$year] = isset($budgeted[$budget->id][$year]) ? round($budgeted[$budget->id][$year], 2) : 0;
|
||||
}
|
||||
$entries->push($entry);
|
||||
}
|
||||
$data = $this->generator->multiYear($entries);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
@@ -277,40 +204,49 @@ class BudgetController extends Controller
|
||||
$cache->addProperty('budget');
|
||||
$cache->addProperty('period');
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
// loop over period, add by users range:
|
||||
$current = clone $start;
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
$set = new Collection;
|
||||
|
||||
// the expenses:
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
$entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end);
|
||||
$budgeted = [];
|
||||
$key = Navigation::preferredCarbonFormat($start, $end);
|
||||
$range = Navigation::preferredRangeFormat($start, $end);
|
||||
|
||||
// get budgeted:
|
||||
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
|
||||
|
||||
|
||||
$current = clone $start;
|
||||
while ($current < $end) {
|
||||
$currentStart = clone $current;
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $viewRange);
|
||||
$reps = $repetitions->filter(
|
||||
function (LimitRepetition $repetition) use ($budget, $currentStart) {
|
||||
if ($repetition->budget_id === $budget->id && $repetition->startdate == $currentStart) {
|
||||
$currentStart = Navigation::startOfPeriod($current, $range);
|
||||
$currentEnd = Navigation::endOfPeriod($current, $range);
|
||||
$reps = $repetitions->filter(
|
||||
function (LimitRepetition $repetition) use ($budget, $currentStart, $currentEnd) {
|
||||
if ($repetition->budget_id === $budget->id && $repetition->startdate >= $currentStart && $repetition->enddate <= $currentEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$budgeted = $reps->sum('amount');
|
||||
$spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd);
|
||||
$entry = [
|
||||
'date' => clone $currentStart,
|
||||
'budgeted' => $budgeted,
|
||||
'spent' => $spent,
|
||||
];
|
||||
$set->push($entry);
|
||||
$index = $currentStart->format($key);
|
||||
$budgeted[$index] = $reps->sum('amount');
|
||||
$currentEnd->addDay();
|
||||
$current = clone $currentEnd;
|
||||
|
||||
}
|
||||
$data = $this->generator->period($set, $viewRange);
|
||||
|
||||
// join them:
|
||||
$result = [];
|
||||
foreach (array_keys($periods) as $period) {
|
||||
$nice = $periods[$period];
|
||||
$result[$nice] = [
|
||||
'spent' => isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0',
|
||||
'budgeted' => isset($entries[$period]) ? $budgeted[$period] : 0,
|
||||
];
|
||||
}
|
||||
|
||||
$data = $this->generator->period($result);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -393,19 +329,22 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetRepositoryInterface $repository
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function spentInPeriodWithout(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end):array
|
||||
private function spentInPeriodWithout(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$list = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget
|
||||
$sum = '0';
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($list as $entry) {
|
||||
$sum = bcadd(TransactionJournal::amount($entry), $sum);
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$types = [TransactionType::WITHDRAWAL];
|
||||
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
$sum = '0';
|
||||
/** @var Transaction $entry */
|
||||
foreach ($journals as $entry) {
|
||||
$sum = bcadd($entry->transaction_amount, $sum);
|
||||
}
|
||||
|
||||
return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0'];
|
||||
|
@@ -48,7 +48,6 @@ class CategoryController extends Controller
|
||||
$this->generator = app(CategoryChartGeneratorInterface::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show an overview for a category for all time, per month/week/year.
|
||||
*
|
||||
@@ -153,78 +152,6 @@ class CategoryController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function multiYear(Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
|
||||
{
|
||||
|
||||
/** @var CRI $repository */
|
||||
$repository = app(CRI::class);
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($categories);
|
||||
$cache->addProperty('multiYearCategory');
|
||||
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
$entries = new Collection;
|
||||
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
$entry = ['name' => '', 'spent' => [], 'earned' => []];
|
||||
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart < $end) {
|
||||
// fix the date:
|
||||
$year = $currentStart->year;
|
||||
$currentEnd = clone $currentStart;
|
||||
$currentEnd->endOfYear();
|
||||
|
||||
// get data:
|
||||
if (is_null($category->id)) {
|
||||
$entry['name'] = trans('firefly.noCategory');
|
||||
$entry['spent'][$year] = ($repository->spentInPeriodWithoutCategory($accounts, $currentStart, $currentEnd) * -1);
|
||||
$entry['earned'][$year] = $repository->earnedInPeriodWithoutCategory($accounts, $currentStart, $currentEnd);
|
||||
|
||||
// jump to next year.
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
continue;
|
||||
|
||||
}
|
||||
// alternative is a normal category:
|
||||
$entry['name'] = $category->name;
|
||||
$entry['spent'][$year] = ($repository->spentInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd) * -1);
|
||||
$entry['earned'][$year] = $repository->earnedInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd);
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param Category $category
|
||||
@@ -244,6 +171,7 @@ class CategoryController extends Controller
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param CRI $repository
|
||||
* @param Category $category
|
||||
|
397
app/Http/Controllers/Chart/CategoryReportController.php
Normal file
397
app/Http/Controllers/Chart/CategoryReportController.php
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
/**
|
||||
* CategoryReportController.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Chart;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface;
|
||||
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Response;
|
||||
|
||||
|
||||
/**
|
||||
* Separate controller because many helper functions are shared.
|
||||
*
|
||||
* Class CategoryReportController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Chart
|
||||
*/
|
||||
class CategoryReportController extends Controller
|
||||
{
|
||||
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepository;
|
||||
/** @var CategoryRepositoryInterface */
|
||||
private $categoryRepository;
|
||||
/** @var CategoryChartGeneratorInterface */
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->generator = app(CategoryChartGeneratorInterface::class);
|
||||
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $others
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getExpenses($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByOpposingAccount($set);
|
||||
|
||||
// show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $accountId => $amount) {
|
||||
if (!isset($names[$accountId])) {
|
||||
$account = $this->accountRepository->find(intval($accountId));
|
||||
$names[$accountId] = $account->name;
|
||||
}
|
||||
$amount = bcmul($amount, '-1');
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect all transactions NOT in these categories.
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
$sum = bcmul($sum, '-1');
|
||||
Log::debug(sprintf('Sum of others in accountExpense is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $others
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getIncome($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByOpposingAccount($set);
|
||||
|
||||
// loop and show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $accountId => $amount) {
|
||||
if (!isset($names[$accountId])) {
|
||||
$account = $this->accountRepository->find(intval($accountId));
|
||||
$names[$accountId] = $account->name;
|
||||
}
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect others?
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
Log::debug(sprintf('Sum of others in accountIncome is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $others
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getExpenses($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByCategory($set);
|
||||
|
||||
// show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $categoryId => $amount) {
|
||||
if (!isset($names[$categoryId])) {
|
||||
$category = $this->categoryRepository->find(intval($categoryId));
|
||||
$names[$categoryId] = $category->name;
|
||||
}
|
||||
$amount = bcmul($amount, '-1');
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect all transactions NOT in these categories.
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
$sum = bcmul($sum, '-1');
|
||||
Log::debug(sprintf('Sum of others in categoryExpense is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $others
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
|
||||
{
|
||||
/** @var bool $others */
|
||||
$others = intval($others) === 1;
|
||||
$names = [];
|
||||
|
||||
// collect journals (just like the category report does):
|
||||
$set = $this->getIncome($accounts, $categories, $start, $end);
|
||||
$grouped = $this->groupByCategory($set);
|
||||
|
||||
// loop and show the grouped results:
|
||||
$result = [];
|
||||
$total = '0';
|
||||
foreach ($grouped as $categoryId => $amount) {
|
||||
if (!isset($names[$categoryId])) {
|
||||
$category = $this->categoryRepository->find(intval($categoryId));
|
||||
$names[$categoryId] = $category->name;
|
||||
}
|
||||
$total = bcadd($total, $amount);
|
||||
$result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount];
|
||||
}
|
||||
|
||||
// also collect others?
|
||||
if ($others) {
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
|
||||
$journals = $collector->getJournals();
|
||||
$sum = strval($journals->sum('transaction_amount'));
|
||||
Log::debug(sprintf('Sum of others in categoryIncome is %f', $sum));
|
||||
$sum = bcsub($sum, $total);
|
||||
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
|
||||
}
|
||||
|
||||
$data = $this->generator->pieChart($result);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function mainChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
|
||||
{
|
||||
// determin optimal period:
|
||||
$period = '1D';
|
||||
$format = 'month_and_day';
|
||||
$function = 'endOfDay';
|
||||
if ($start->diffInMonths($end) > 1) {
|
||||
$period = '1M';
|
||||
$format = 'month';
|
||||
$function = 'endOfMonth';
|
||||
}
|
||||
if ($start->diffInMonths($end) > 13) {
|
||||
$period = '1Y';
|
||||
$format = 'year';
|
||||
$function = 'endOfYear';
|
||||
}
|
||||
Log::debug(sprintf('Period is %s', $period));
|
||||
$data = [];
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart < $end) {
|
||||
$currentEnd = clone $currentStart;
|
||||
Log::debug(sprintf('Function is %s', $function));
|
||||
$currentEnd = $currentEnd->$function();
|
||||
$expenses = $this->groupByCategory($this->getExpenses($accounts, $categories, $currentStart, $currentEnd));
|
||||
$income = $this->groupByCategory($this->getIncome($accounts, $categories, $currentStart, $currentEnd));
|
||||
$label = $currentStart->formatLocalized(strval(trans('config.' . $format)));
|
||||
|
||||
Log::debug(sprintf('Now grabbing CMC expenses between %s and %s', $currentStart->format('Y-m-d'), $currentEnd->format('Y-m-d')));
|
||||
|
||||
$data[$label] = [
|
||||
'in' => [],
|
||||
'out' => [],
|
||||
];
|
||||
|
||||
/** @var Category $category */
|
||||
foreach ($categories as $category) {
|
||||
// get sum, and get label:
|
||||
$categoryId = $category->id;
|
||||
$data[$label]['name'][$categoryId] = $category->name;
|
||||
$data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0';
|
||||
$data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0';
|
||||
}
|
||||
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
$data = $this->generator->mainReportChart($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
|
||||
->setCategories($categories)->withOpposingAccount()->disableFilter();
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Collection $categories
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
|
||||
->setCategories($categories)->withOpposingAccount();
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$transactions = $collector->getJournals();
|
||||
$set = MonthReportGenerator::filterIncome($transactions, $accountIds);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function groupByCategory(Collection $set): array
|
||||
{
|
||||
// group by category ID:
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$jrnlCatId = intval($transaction->transaction_journal_category_id);
|
||||
$transCatId = intval($transaction->transaction_category_id);
|
||||
$categoryId = max($jrnlCatId, $transCatId);
|
||||
$grouped[$categoryId] = $grouped[$categoryId] ?? '0';
|
||||
$grouped[$categoryId] = bcadd($transaction->transaction_amount, $grouped[$categoryId]);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function groupByOpposingAccount(Collection $set): array
|
||||
{
|
||||
$grouped = [];
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($set as $transaction) {
|
||||
$accountId = $transaction->opposing_account_id;
|
||||
$grouped[$accountId] = $grouped[$accountId] ?? '0';
|
||||
$grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
@@ -49,20 +49,18 @@ class ReportController extends Controller
|
||||
* This chart, by default, is shown on the multi-year and year report pages,
|
||||
* which means that giving it a 2 week "period" should be enough granularity.
|
||||
*
|
||||
* @param string $reportType
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function netWorth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
public function netWorth(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('netWorth');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($reportType);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
@@ -92,52 +90,36 @@ class ReportController extends Controller
|
||||
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $accountTasker
|
||||
* @param string $reportType
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @internal param AccountRepositoryInterface $repository
|
||||
*/
|
||||
public function yearInOut(AccountTaskerInterface $accountTasker, string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
public function yearInOut(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty('yearInOut');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($reportType);
|
||||
$cache->addProperty($accounts);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// always per month.
|
||||
$currentStart = clone $start;
|
||||
$spentArray = [];
|
||||
$earnedArray = [];
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
|
||||
$date = $currentStart->format('Y-m');
|
||||
$spent = $accountTasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$earned = $accountTasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$spentArray[$date] = bcmul($spent, '-1');
|
||||
$earnedArray[$date] = $earned;
|
||||
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
|
||||
}
|
||||
$chartSource = $this->getYearData($accounts, $start, $end);
|
||||
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// data = method X
|
||||
$data = $this->multiYearInOut($earnedArray, $spentArray, $start, $end);
|
||||
$data = $this->multiYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
|
||||
// data = method Y
|
||||
$data = $this->singleYearInOut($earnedArray, $spentArray, $start, $end);
|
||||
$data = $this->singleYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -146,16 +128,14 @@ class ReportController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountTaskerInterface $accountTasker
|
||||
* @param string $reportType
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @internal param AccountRepositoryInterface $repository
|
||||
*/
|
||||
public function yearInOutSummarized(AccountTaskerInterface $accountTasker, string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
public function yearInOutSummarized(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
// chart properties for cache:
|
||||
@@ -163,35 +143,21 @@ class ReportController extends Controller
|
||||
$cache->addProperty('yearInOutSummarized');
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($reportType);
|
||||
$cache->addProperty($accounts);
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
}
|
||||
|
||||
// always per month.
|
||||
$currentStart = clone $start;
|
||||
$spentArray = [];
|
||||
$earnedArray = [];
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
|
||||
$date = $currentStart->format('Y-m');
|
||||
$spent = $accountTasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$earned = $accountTasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$spentArray[$date] = bcmul($spent, '-1');
|
||||
$earnedArray[$date] = $earned;
|
||||
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
|
||||
}
|
||||
$chartSource = $this->getYearData($accounts, $start, $end);
|
||||
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// per year
|
||||
$data = $this->multiYearInOutSummarized($earnedArray, $spentArray, $start, $end);
|
||||
$data = $this->multiYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
// per month!
|
||||
$data = $this->singleYearInOutSummarized($earnedArray, $spentArray, $start, $end);
|
||||
$data = $this->singleYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end);
|
||||
$cache->store($data);
|
||||
|
||||
return Response::json($data);
|
||||
@@ -342,4 +308,33 @@ class ReportController extends Controller
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getYearData(Collection $accounts, Carbon $start, Carbon $end): array
|
||||
{
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
$currentStart = clone $start;
|
||||
$spentArray = [];
|
||||
$earnedArray = [];
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
|
||||
$date = $currentStart->format('Y-m');
|
||||
$spent = $tasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$earned = $tasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
|
||||
$spentArray[$date] = bcmul($spent, '-1');
|
||||
$earnedArray[$date] = $earned;
|
||||
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'spent' => $spentArray,
|
||||
'earned' => $earnedArray,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -13,11 +13,15 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
/**
|
||||
@@ -61,31 +65,37 @@ class Controller extends BaseController
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
|
||||
* and sum up everything in the array in the given range.
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
* @return bool
|
||||
*/
|
||||
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
|
||||
protected function isOpeningBalance(TransactionJournal $journal): bool
|
||||
{
|
||||
$sum = '0';
|
||||
$currentStart = clone $start; // to not mess with the original one
|
||||
$currentEnd = clone $end; // to not mess with the original one
|
||||
return TransactionJournal::transactionTypeStr($journal) === TransactionType::OPENING_BALANCE;
|
||||
}
|
||||
|
||||
while ($currentStart <= $currentEnd) {
|
||||
$date = $currentStart->format('Y-m-d');
|
||||
if (isset($array[$date])) {
|
||||
$sum = bcadd($sum, $array[$date]);
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
protected function redirectToAccount(TransactionJournal $journal)
|
||||
{
|
||||
$valid = [AccountType::DEFAULT, AccountType::ASSET];
|
||||
$transactions = $journal->transactions;
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
$account = $transaction->account;
|
||||
if (in_array($account->accountType->type, $valid)) {
|
||||
return redirect(route('accounts.show', [$account->id]));
|
||||
}
|
||||
$currentStart->addDay();
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
Session::flash('error', strval(trans('firefly.cannot_redirect_to_account')));
|
||||
|
||||
return redirect(route('index'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -129,7 +129,7 @@ class CurrencyController extends Controller
|
||||
|
||||
Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
|
||||
if (auth()->user()->hasRole('owner')) {
|
||||
$currency->delete();
|
||||
$currency->forceDelete();
|
||||
}
|
||||
|
||||
return redirect(session('currency.delete.url'));
|
||||
|
@@ -56,7 +56,7 @@ class ExportController extends Controller
|
||||
/**
|
||||
* @param ExportJob $job
|
||||
*
|
||||
* @return mixed
|
||||
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function download(ExportJob $job)
|
||||
|
@@ -42,9 +42,8 @@ class HelpController extends Controller
|
||||
public function show(HelpInterface $help, string $route)
|
||||
{
|
||||
|
||||
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
$alternative = false;
|
||||
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
|
||||
if (!$help->hasRoute($route)) {
|
||||
Log::error('No such route: ' . $route);
|
||||
@@ -54,7 +53,7 @@ class HelpController extends Controller
|
||||
|
||||
if ($help->inCache($route, $language)) {
|
||||
$content = $help->getFromCache($route, $language);
|
||||
Log::debug('Help text was in cache.');
|
||||
Log::debug(sprintf('Help text %s was in cache.', $language));
|
||||
|
||||
return Response::json($content);
|
||||
}
|
||||
@@ -63,19 +62,21 @@ class HelpController extends Controller
|
||||
|
||||
// get backup language content (try English):
|
||||
if (strlen($content) === 0) {
|
||||
$language = 'en_US';
|
||||
$content = $help->getFromGithub($language, $route);
|
||||
$alternative = true;
|
||||
}
|
||||
|
||||
if ($alternative && strlen($content) > 0) {
|
||||
$content = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>' . $content;
|
||||
$language = 'en_US';
|
||||
if ($help->inCache($route, $language)) {
|
||||
Log::debug(sprintf('Help text %s was in cache.', $language));
|
||||
$content = $help->getFromCache($route, $language);
|
||||
}
|
||||
if (!$help->inCache($route, $language)) {
|
||||
$content = $help->getFromGithub($language, $route);
|
||||
$content = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>' . $content;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($content) === 0) {
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
}
|
||||
|
||||
|
||||
$help->putInCache($route, $language, $content);
|
||||
|
||||
return Response::json($content);
|
||||
|
@@ -15,10 +15,11 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Artisan;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -115,14 +116,12 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ARI $repository
|
||||
* @param AccountTaskerInterface $tasker
|
||||
* @param ARI $repository
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function index(ARI $repository, AccountTaskerInterface $tasker)
|
||||
public function index(ARI $repository)
|
||||
{
|
||||
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
|
||||
@@ -143,17 +142,20 @@ class HomeController extends Controller
|
||||
$accounts = $repository->getAccountsById($frontPage->data);
|
||||
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$set = $set->splice(0, 10);
|
||||
// zero bills? Hide some elements from view.
|
||||
/** @var BillRepositoryInterface $billRepository */
|
||||
$billRepository = app(BillRepositoryInterface::class);
|
||||
$billCount = $billRepository->getBills()->count();
|
||||
|
||||
if (count($set) > 0) {
|
||||
$transactions[] = [$set, $account];
|
||||
}
|
||||
foreach ($accounts as $account) {
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1);
|
||||
$set = $collector->getJournals();
|
||||
$transactions[] = [$set, $account];
|
||||
}
|
||||
|
||||
return view(
|
||||
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage')
|
||||
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -169,8 +171,7 @@ class HomeController extends Controller
|
||||
'admin.users.domains.block-', 'import.json', 'help.',
|
||||
];
|
||||
$routes = Route::getRoutes();
|
||||
|
||||
echo '<pre>';
|
||||
$return = '<pre>';
|
||||
|
||||
/** @var \Illuminate\Routing\Route $route */
|
||||
foreach ($routes as $route) {
|
||||
@@ -178,15 +179,13 @@ class HomeController extends Controller
|
||||
$methods = $route->getMethods();
|
||||
|
||||
if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
|
||||
echo sprintf('touch %s.md', $name) . "\n";
|
||||
$return .= sprintf('touch %s.md', $name) . "\n";
|
||||
|
||||
}
|
||||
}
|
||||
echo '</pre>';
|
||||
$return .= '</pre><hr />';
|
||||
|
||||
echo '<hr />';
|
||||
|
||||
return ' ';
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,10 +207,7 @@ class HomeController extends Controller
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private
|
||||
function startsWithAny(
|
||||
array $array, string $needle
|
||||
): bool
|
||||
private function startsWithAny(array $array, string $needle): bool
|
||||
{
|
||||
foreach ($array as $entry) {
|
||||
if ((substr($needle, 0, strlen($entry)) === $entry)) {
|
||||
|
@@ -15,12 +15,12 @@ namespace FireflyIII\Http\Controllers;
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Input;
|
||||
@@ -270,17 +270,20 @@ class JsonController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalTaskerInterface $tasker
|
||||
* @param $what
|
||||
* @param $what
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function transactionJournals(JournalTaskerInterface $tasker, $what)
|
||||
public function transactionJournals($what)
|
||||
{
|
||||
$descriptions = [];
|
||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$types = [$type];
|
||||
$journals = $tasker->getJournals($types, 1, 50);
|
||||
|
||||
// use journal collector instead:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setTypes($types)->setLimit(100)->setPage(1);
|
||||
$journals = $collector->getJournals();
|
||||
foreach ($journals as $j) {
|
||||
$descriptions[] = $j->description;
|
||||
}
|
||||
|
@@ -78,14 +78,16 @@ class NewUserController extends Controller
|
||||
$this->createAssetAccount($request, $repository);
|
||||
|
||||
// create savings account
|
||||
if (strlen($request->get('savings_balance')) > 0) {
|
||||
$savingBalance = strval($request->get('savings_balance')) === '' ? '0' : strval($request->get('savings_balance'));
|
||||
if (bccomp($savingBalance, '0') !== 0) {
|
||||
$this->createSavingsAccount($request, $repository);
|
||||
$count++;
|
||||
}
|
||||
|
||||
|
||||
// create credit card.
|
||||
if (strlen($request->get('credit_card_limit')) > 0) {
|
||||
$limit = strval($request->get('credit_card_limit')) === '' ? '0' : strval($request->get('credit_card_limit'));
|
||||
if (bccomp($limit, '0') !== 0) {
|
||||
$this->storeCreditCard($request, $repository);
|
||||
$count++;
|
||||
}
|
||||
|
@@ -107,6 +107,12 @@ class PiggyBankController extends Controller
|
||||
$subTitle = trans('firefly.new_piggy_bank');
|
||||
$subTitleIcon = 'fa-plus';
|
||||
|
||||
if (count($accounts) === 0) {
|
||||
Session::flash('error', strval(trans('firefly.need_at_least_one_account')));
|
||||
|
||||
return redirect(route('new-user.index'));
|
||||
}
|
||||
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (session('piggy-banks.create.fromStore') !== true) {
|
||||
Session::put('piggy-banks.create.url', URL::previous());
|
||||
|
@@ -17,12 +17,12 @@ namespace FireflyIII\Http\Controllers\Popup;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collection\BalanceLine;
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\Binder\AccountList;
|
||||
@@ -98,20 +98,39 @@ class ReportController extends Controller
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL];
|
||||
|
||||
switch (true) {
|
||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
|
||||
$journals = $budgetRepository->journalsInPeriod(
|
||||
new Collection([$budget]), new Collection([$account]), $attributes['startDate'], $attributes['endDate']
|
||||
);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->setBudget($budget);
|
||||
$journals = $collector->getJournals();
|
||||
|
||||
break;
|
||||
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
|
||||
$budget->name = strval(trans('firefly.no_budget'));
|
||||
$journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
// collector
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
break;
|
||||
case ($role === BalanceLine::ROLE_DIFFROLE):
|
||||
// journals no budget, not corrected by a tag.
|
||||
$journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector
|
||||
->setAccounts(new Collection([$account]))
|
||||
->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withoutBudget();
|
||||
$journals = $collector->getJournals();
|
||||
|
||||
$budget->name = strval(trans('firefly.leftUnbalanced'));
|
||||
$journals = $journals->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
@@ -148,14 +167,21 @@ class ReportController extends Controller
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$budget = $repository->find(intval($attributes['budgetId']));
|
||||
if (is_null($budget->id)) {
|
||||
$journals = $repository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
} else {
|
||||
// get all expenses in budget in period:
|
||||
$journals = $repository->journalsInPeriod(new Collection([$budget]), $attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
}
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
|
||||
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
|
||||
$collector
|
||||
->setAccounts($attributes['accounts'])
|
||||
->setRange($attributes['startDate'], $attributes['endDate']);
|
||||
|
||||
if (is_null($budget->id)) {
|
||||
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
|
||||
}
|
||||
if (!is_null($budget->id)) {
|
||||
// get all expenses in budget in period:
|
||||
$collector->setBudget($budget);
|
||||
}
|
||||
$journals = $collector->getJournals();
|
||||
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
|
||||
|
||||
return $view;
|
||||
}
|
||||
@@ -174,10 +200,14 @@ class ReportController extends Controller
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$category = $repository->find(intval($attributes['categoryId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$journals = $repository->journalsInPeriod(
|
||||
new Collection([$category]), $attributes['accounts'], $types, $attributes['startDate'], $attributes['endDate']
|
||||
);
|
||||
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
||||
// get journal collector instead:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts($attributes['accounts'])->setTypes($types)
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->setCategory($category);
|
||||
$journals = $collector->getJournals(); // 7193
|
||||
|
||||
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
|
||||
|
||||
return $view;
|
||||
}
|
||||
@@ -192,14 +222,14 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function expenseEntry(array $attributes): string
|
||||
{
|
||||
/** @var AccountTaskerInterface $tasker */
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
||||
$journals = $collector->getJournals();
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
|
||||
// filter for transfers and withdrawals TO the given $account
|
||||
@@ -228,14 +258,14 @@ class ReportController extends Controller
|
||||
*/
|
||||
private function incomeEntry(array $attributes): string
|
||||
{
|
||||
/** @var AccountTaskerInterface $tasker */
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$account = $repository->find(intval($attributes['accountId']));
|
||||
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
|
||||
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
|
||||
$journals = $collector->getJournals();
|
||||
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
|
||||
|
||||
// filter the set so the destinations outside of $attributes['accounts'] are not included.
|
||||
$journals = $journals->filter(
|
||||
|
@@ -13,7 +13,6 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Events\DeletedUser;
|
||||
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
|
||||
use FireflyIII\Http\Requests\ProfileFormRequest;
|
||||
use FireflyIII\User;
|
||||
|
@@ -19,6 +19,7 @@ use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
|
||||
/**
|
||||
* Class BudgetController
|
||||
@@ -28,6 +29,34 @@ use Illuminate\Support\Collection;
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @param BudgetReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget-period-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
$budgets = $helper->getBudgetPeriodReport($start, $end, $accounts);
|
||||
$result = view('reports.partials.budget-period', compact('budgets', 'periods'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
@@ -57,35 +86,4 @@ class BudgetController extends Controller
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function budgetYearOverview(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('budget-year-overview');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$budgets = $helper->budgetYearOverview($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.budget-year-overview', compact('budgets'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -19,7 +19,6 @@ use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* Class InOutController
|
||||
@@ -37,29 +36,83 @@ class InOutController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function inOutReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
public function expenseReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('in-out-report');
|
||||
$cache->addProperty('expense-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return Response::json($cache->get());
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$expenses = $helper->getExpenseReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.expenses', compact('expenses'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function incExpReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('inc-exp-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$incomes = $helper->getIncomeReport($start, $end, $accounts);
|
||||
$expenses = $helper->getExpenseReport($start, $end, $accounts);
|
||||
|
||||
$result = [
|
||||
'income' => view('reports.partials.income', compact('incomes'))->render(),
|
||||
'expenses' => view('reports.partials.expenses', compact('expenses'))->render(),
|
||||
'incomes_expenses' => view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(),
|
||||
];
|
||||
$result = view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return Response::json($result);
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReportHelperInterface $helper
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function incomeReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('income-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
$incomes = $helper->getIncomeReport($start, $end, $accounts);
|
||||
|
||||
$result = view('reports.partials.income', compact('incomes'))->render();
|
||||
$cache->store($result);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
|
@@ -15,18 +15,17 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Generator\Report\ReportGeneratorFactory;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Http\Requests\ReportFormRequest;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
use Steam;
|
||||
use View;
|
||||
|
||||
/**
|
||||
@@ -51,6 +50,7 @@ class ReportController extends Controller
|
||||
function ($request, $next) {
|
||||
View::share('title', trans('firefly.reports'));
|
||||
View::share('mainTitleIcon', 'fa-line-chart');
|
||||
View::share('subTitleIcon', 'fa-calendar');
|
||||
|
||||
$this->helper = app(ReportHelperInterface::class);
|
||||
|
||||
@@ -60,6 +60,115 @@ class ReportController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function auditReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
if ($end < $start) {
|
||||
return view('error')->with('message', trans('firefly.end_after_start_date'));
|
||||
}
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
|
||||
View::share(
|
||||
'subTitle', trans(
|
||||
'firefly.report_audit',
|
||||
[
|
||||
'start' => $start->formatLocalized($this->monthFormat),
|
||||
'end' => $end->formatLocalized($this->monthFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$generator = ReportGeneratorFactory::reportGenerator('Audit', $start, $end);
|
||||
$generator->setAccounts($accounts);
|
||||
$result = $generator->generate();
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function categoryReport(Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
|
||||
{
|
||||
if ($end < $start) {
|
||||
return view('error')->with('message', trans('firefly.end_after_start_date'));
|
||||
}
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
|
||||
View::share(
|
||||
'subTitle', trans(
|
||||
'firefly.report_category',
|
||||
[
|
||||
'start' => $start->formatLocalized($this->monthFormat),
|
||||
'end' => $end->formatLocalized($this->monthFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$generator = ReportGeneratorFactory::reportGenerator('Category', $start, $end);
|
||||
$generator->setAccounts($accounts);
|
||||
$generator->setCategories($categories);
|
||||
$result = $generator->generate();
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function defaultReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
if ($end < $start) {
|
||||
return view('error')->with('message', trans('firefly.end_after_start_date'));
|
||||
}
|
||||
|
||||
if ($start < session('first')) {
|
||||
$start = session('first');
|
||||
}
|
||||
|
||||
View::share(
|
||||
'subTitle', trans(
|
||||
'firefly.report_default',
|
||||
[
|
||||
'start' => $start->formatLocalized($this->monthFormat),
|
||||
'end' => $end->formatLocalized($this->monthFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
|
||||
$generator->setAccounts($accounts);
|
||||
$result = $generator->generate();
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
@@ -67,40 +176,59 @@ class ReportController extends Controller
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository)
|
||||
{
|
||||
|
||||
/** @var Carbon $start */
|
||||
$start = clone session('first');
|
||||
$months = $this->helper->listOfMonths($start);
|
||||
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
|
||||
|
||||
// does the user have shared accounts?
|
||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
// get id's for quick links:
|
||||
$accountIds = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$accountIds [] = $account->id;
|
||||
}
|
||||
$accountList = join(',', $accountIds);
|
||||
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$accountList = join(',', $accounts->pluck('id')->toArray());
|
||||
|
||||
|
||||
return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $reportType
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param string $reportType
|
||||
*
|
||||
* @return View
|
||||
* @return mixed
|
||||
*/
|
||||
public function options(string $reportType)
|
||||
{
|
||||
switch ($reportType) {
|
||||
default:
|
||||
$result = $this->noReportOptions();
|
||||
break;
|
||||
case 'category':
|
||||
$result = $this->categoryReportOptions();
|
||||
break;
|
||||
}
|
||||
|
||||
return Response::json(['html' => $result]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReportFormRequest $request
|
||||
*
|
||||
* @return RedirectResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
public function postIndex(ReportFormRequest $request): RedirectResponse
|
||||
{
|
||||
// throw an error if necessary.
|
||||
// report type:
|
||||
$reportType = $request->get('report_type');
|
||||
$start = $request->getStartDate()->format('Ymd');
|
||||
$end = $request->getEndDate()->format('Ymd');
|
||||
$accounts = join(',', $request->getAccountList()->pluck('id')->toArray());
|
||||
$categories = join(',', $request->getCategoryList()->pluck('id')->toArray());
|
||||
|
||||
if ($request->getAccountList()->count() === 0) {
|
||||
Session::flash('error', trans('firefly.select_more_than_one_account'));
|
||||
|
||||
return redirect(route('reports.index'));
|
||||
}
|
||||
|
||||
if ($end < $start) {
|
||||
throw new FireflyException('End date cannot be before start date, silly!');
|
||||
return view('error')->with('message', trans('firefly.end_after_start_date'));
|
||||
}
|
||||
|
||||
// lower threshold
|
||||
@@ -108,193 +236,42 @@ class ReportController extends Controller
|
||||
$start = session('first');
|
||||
}
|
||||
|
||||
View::share(
|
||||
'subTitle', trans(
|
||||
'firefly.report_' . $reportType,
|
||||
[
|
||||
'start' => $start->formatLocalized($this->monthFormat),
|
||||
'end' => $end->formatLocalized($this->monthFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
View::share('subTitleIcon', 'fa-calendar');
|
||||
|
||||
switch ($reportType) {
|
||||
default:
|
||||
throw new FireflyException('Unfortunately, reports of the type "' . e($reportType) . '" are not available at this time.');
|
||||
throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType));
|
||||
case 'category':
|
||||
$uri = route('reports.report.category', [$start, $end, $accounts, $categories]);
|
||||
break;
|
||||
case 'default':
|
||||
|
||||
// more than one year date difference means year report.
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
return $this->defaultMultiYear($reportType, $start, $end, $accounts);
|
||||
}
|
||||
// more than two months date difference means year report.
|
||||
if ($start->diffInMonths($end) > 1) {
|
||||
return $this->defaultYear($reportType, $start, $end, $accounts);
|
||||
}
|
||||
|
||||
// otherwise default
|
||||
return $this->defaultMonth($reportType, $start, $end, $accounts);
|
||||
$uri = route('reports.report.default', [$start, $end, $accounts]);
|
||||
break;
|
||||
case 'audit':
|
||||
// always default
|
||||
return $this->auditReport($start, $end, $accounts);
|
||||
$uri = route('reports.report.audit', [$start, $end, $accounts]);
|
||||
break;
|
||||
}
|
||||
|
||||
return redirect($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function categoryReportOptions(): string
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$categories = $repository->getCategories();
|
||||
$result = view('reports.options.category', compact('categories'))->render();
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return View
|
||||
* @return string
|
||||
*/
|
||||
private function auditReport(Carbon $start, Carbon $end, Collection $accounts)
|
||||
private function noReportOptions(): string
|
||||
{
|
||||
/** @var AccountTaskerInterface $tasker */
|
||||
$tasker = app(AccountTaskerInterface::class);
|
||||
$auditData = [];
|
||||
$dayBefore = clone $start;
|
||||
$dayBefore->subDay();
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
// balance the day before:
|
||||
$id = $account->id;
|
||||
$dayBeforeBalance = Steam::balance($account, $dayBefore);
|
||||
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
|
||||
$journals = $journals->reverse();
|
||||
$startBalance = $dayBeforeBalance;
|
||||
|
||||
|
||||
/** @var Transaction $journal */
|
||||
foreach ($journals as $transaction) {
|
||||
$transaction->before = $startBalance;
|
||||
$transactionAmount = $transaction->transaction_amount;
|
||||
$newBalance = bcadd($startBalance, $transactionAmount);
|
||||
$transaction->after = $newBalance;
|
||||
$startBalance = $newBalance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse set again.
|
||||
*/
|
||||
$auditData[$id]['journals'] = $journals->reverse();
|
||||
$auditData[$id]['exists'] = $journals->count() > 0;
|
||||
$auditData[$id]['end'] = $end->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$auditData[$id]['endBalance'] = Steam::balance($account, $end);
|
||||
$auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
|
||||
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
|
||||
}
|
||||
|
||||
$reportType = 'audit';
|
||||
$accountIds = join(',', $accounts->pluck('id')->toArray());
|
||||
|
||||
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
|
||||
'interest_date', 'book_date', 'process_date',
|
||||
// three new optional fields.
|
||||
'due_date', 'payment_date', 'invoice_date',
|
||||
'from', 'to', 'budget', 'category', 'bill',
|
||||
// more new optional fields
|
||||
'internal_reference', 'notes',
|
||||
|
||||
'create_date', 'update_date',
|
||||
];
|
||||
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
|
||||
|
||||
return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$bills = $this->helper->getBillReport($start, $end, $accounts);
|
||||
$tags = $this->helper->tagReport($start, $end, $accounts);
|
||||
|
||||
// and some id's, joined:
|
||||
$accountIds = join(',', $accounts->pluck('id')->toArray());
|
||||
|
||||
// continue!
|
||||
return view(
|
||||
'reports.default.month',
|
||||
compact(
|
||||
'start', 'end',
|
||||
'tags',
|
||||
'bills',
|
||||
'accountIds',
|
||||
'reportType'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param $start
|
||||
* @param $end
|
||||
* @param $accounts
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
|
||||
$budgets = app(BudgetRepositoryInterface::class)->getActiveBudgets();
|
||||
$categories = app(CategoryRepositoryInterface::class)->getCategories();
|
||||
$tags = $this->helper->tagReport($start, $end, $accounts);
|
||||
|
||||
// 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', 'reportType', 'tags'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
|
||||
{
|
||||
$tags = $this->helper->tagReport($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.default.year',
|
||||
compact(
|
||||
'start', 'reportType',
|
||||
'accountIds', 'end', 'tags'
|
||||
)
|
||||
);
|
||||
return view('reports.options.no-options')->render();
|
||||
}
|
||||
}
|
||||
|
@@ -306,7 +306,7 @@ class RuleController extends Controller
|
||||
}
|
||||
|
||||
// Return json response
|
||||
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
|
||||
$view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render();
|
||||
|
||||
return Response::json(['html' => $view, 'warning' => $warning]);
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Input;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class SearchController
|
||||
@@ -30,31 +30,31 @@ class SearchController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Results always come in the form of an array [results, count, fullCount]
|
||||
*
|
||||
* @param Request $request
|
||||
* @param SearchInterface $searcher
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function index(SearchInterface $searcher)
|
||||
public function index(Request $request, SearchInterface $searcher)
|
||||
{
|
||||
|
||||
$minSearchLen = 1;
|
||||
$subTitle = null;
|
||||
$query = null;
|
||||
$result = [];
|
||||
$title = trans('firefly.search');
|
||||
$limit = 20;
|
||||
$mainTitleIcon = 'fa-search';
|
||||
if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) {
|
||||
$query = trim(Input::get('q'));
|
||||
|
||||
// set limit for search:
|
||||
$searcher->setLimit($limit);
|
||||
|
||||
if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) {
|
||||
$query = trim(strtolower($request->get('q')));
|
||||
$words = explode(' ', $query);
|
||||
$subTitle = trans('firefly.search_results_for', ['query' => $query]);
|
||||
|
||||
@@ -67,7 +67,7 @@ class SearchController extends Controller
|
||||
|
||||
}
|
||||
|
||||
return view('search.index', compact('title', 'subTitle', 'mainTitleIcon', 'query', 'result'));
|
||||
return view('search.index', compact('title', 'subTitle', 'limit', 'mainTitleIcon', 'query', 'result'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,10 +13,11 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Http\Requests\TagFormRequest;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Input;
|
||||
@@ -226,19 +227,27 @@ class TagController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
* @param TagRepositoryInterface $repository
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(Tag $tag, TagRepositoryInterface $repository)
|
||||
public function show(Tag $tag)
|
||||
{
|
||||
$subTitle = $tag->tag;
|
||||
$subTitleIcon = 'fa-tag';
|
||||
$journals = $repository->getJournals($tag);
|
||||
$sum = $journals->sum(
|
||||
function (TransactionJournal $journal) {
|
||||
return TransactionJournal::amount($journal);
|
||||
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// use collector:
|
||||
// replace with journal collector:
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('tags/show/' . $tag->id);
|
||||
|
||||
$sum = $journals->sum(
|
||||
function (Transaction $transaction) {
|
||||
return $transaction->transaction_amount;
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -64,6 +64,10 @@ class ConvertController extends Controller
|
||||
*/
|
||||
public function convert(TransactionType $destinationType, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$positiveAmount = TransactionJournal::amountPositive($journal);
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
||||
$sourceType = $journal->transactionType;
|
||||
@@ -113,6 +117,10 @@ class ConvertController extends Controller
|
||||
*/
|
||||
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$data = $request->all();
|
||||
|
||||
// cannot convert to its own type.
|
||||
@@ -160,7 +168,6 @@ class ConvertController extends Controller
|
||||
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
|
||||
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
|
||||
$sourceType = $journal->transactionType;
|
||||
$destination = null;
|
||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
||||
switch ($joined) {
|
||||
default:
|
||||
@@ -206,7 +213,6 @@ class ConvertController extends Controller
|
||||
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
|
||||
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
|
||||
$sourceType = $journal->transactionType;
|
||||
$source = new Account;
|
||||
$joined = $sourceType->type . '-' . $destinationType->type;
|
||||
switch ($joined) {
|
||||
default:
|
||||
|
@@ -196,35 +196,33 @@ class MassController extends Controller
|
||||
$journal = $repository->find(intval($journalId));
|
||||
if ($journal) {
|
||||
// get optional fields:
|
||||
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
|
||||
|
||||
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
|
||||
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
|
||||
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? '';
|
||||
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
|
||||
$destAccountName = $request->get('destination_account_name')[$journal->id] ?? '';
|
||||
|
||||
$budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0;
|
||||
$category = $request->get('category')[$journal->id];
|
||||
$tags = $journal->tags->pluck('tag')->toArray();
|
||||
$budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0;
|
||||
$category = $request->get('category')[$journal->id];
|
||||
$tags = $journal->tags->pluck('tag')->toArray();
|
||||
|
||||
// build data array
|
||||
$data = [
|
||||
'id' => $journal->id,
|
||||
'what' => $what,
|
||||
'description' => $request->get('description')[$journal->id],
|
||||
'source_account_id' => intval($sourceAccountId),
|
||||
'source_account_name' => $sourceAccountName,
|
||||
'destination_account_id' => intval($destAccountId),
|
||||
'destination_account_name' => $destAccountName,
|
||||
'amount' => round($request->get('amount')[$journal->id], 4),
|
||||
'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
|
||||
'date' => new Carbon($request->get('date')[$journal->id]),
|
||||
'interest_date' => $journal->interest_date,
|
||||
'book_date' => $journal->book_date,
|
||||
'process_date' => $journal->process_date,
|
||||
'budget_id' => $budgetId,
|
||||
'category' => $category,
|
||||
'tags' => $tags,
|
||||
'id' => $journal->id,
|
||||
'what' => $what,
|
||||
'description' => $request->get('description')[$journal->id],
|
||||
'source_account_id' => intval($sourceAccountId),
|
||||
'source_account_name' => $sourceAccountName,
|
||||
'destination_account_id' => intval($destAccountId),
|
||||
'destination_account_name' => $destAccountName,
|
||||
'amount' => round($request->get('amount')[$journal->id], 4),
|
||||
'currency_id' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
|
||||
'date' => new Carbon($request->get('date')[$journal->id]),
|
||||
'interest_date' => $journal->interest_date,
|
||||
'book_date' => $journal->book_date,
|
||||
'process_date' => $journal->process_date,
|
||||
'budget_id' => $budgetId,
|
||||
'category' => $category,
|
||||
'tags' => $tags,
|
||||
|
||||
];
|
||||
// call repository update function.
|
||||
|
@@ -125,6 +125,10 @@ class SingleController extends Controller
|
||||
*/
|
||||
public function delete(TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
$subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]);
|
||||
|
||||
@@ -146,8 +150,12 @@ class SingleController extends Controller
|
||||
*/
|
||||
public function destroy(JournalRepositoryInterface $repository, TransactionJournal $transactionJournal)
|
||||
{
|
||||
if ($this->isOpeningBalance($transactionJournal)) {
|
||||
return $this->redirectToAccount($transactionJournal);
|
||||
}
|
||||
|
||||
$type = TransactionJournal::transactionTypeStr($transactionJournal);
|
||||
Session::flash('success', strval(trans('firefly.deleted_' . $type, ['description' => e($transactionJournal->description)])));
|
||||
Session::flash('success', strval(trans('firefly.deleted_' . strtolower($type), ['description' => e($transactionJournal->description)])));
|
||||
|
||||
$repository->delete($transactionJournal);
|
||||
|
||||
@@ -164,18 +172,23 @@ class SingleController extends Controller
|
||||
*/
|
||||
public function edit(TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$count = $journal->transactions()->count();
|
||||
|
||||
if ($count > 2) {
|
||||
return redirect(route('transactions.edit-split', [$journal->id]));
|
||||
}
|
||||
|
||||
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
||||
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks());
|
||||
|
||||
// view related code
|
||||
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
||||
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
|
||||
|
||||
|
||||
// journal related code
|
||||
$sourceAccounts = TransactionJournal::sourceAccountList($journal);
|
||||
@@ -188,12 +201,11 @@ class SingleController extends Controller
|
||||
'process_date' => TransactionJournal::dateAsString($journal, 'process_date'),
|
||||
'category' => TransactionJournal::categoryAsString($journal),
|
||||
'budget_id' => TransactionJournal::budgetId($journal),
|
||||
'piggy_bank_id' => TransactionJournal::piggyBankId($journal),
|
||||
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
|
||||
'source_account_id' => $sourceAccounts->first()->id,
|
||||
'source_account_name' => $sourceAccounts->first()->name,
|
||||
'source_account_name' => $sourceAccounts->first()->edit_name,
|
||||
'destination_account_id' => $destinationAccounts->first()->id,
|
||||
'destination_account_name' => $destinationAccounts->first()->name,
|
||||
'destination_account_name' => $destinationAccounts->first()->edit_name,
|
||||
'amount' => TransactionJournal::amountPositive($journal),
|
||||
|
||||
// new custom fields:
|
||||
@@ -225,7 +237,7 @@ class SingleController extends Controller
|
||||
|
||||
return view(
|
||||
'transactions.edit',
|
||||
compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle')
|
||||
compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'subTitle')
|
||||
)->with('data', $preFilled);
|
||||
}
|
||||
|
||||
@@ -292,6 +304,10 @@ class SingleController extends Controller
|
||||
*/
|
||||
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$data = $request->getJournalData();
|
||||
$journal = $repository->update($journal, $data);
|
||||
$this->attachments->saveAttachmentsForModel($journal);
|
||||
@@ -326,6 +342,4 @@ class SingleController extends Controller
|
||||
return redirect(session('transactions.edit.url'));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -89,6 +89,10 @@ class SplitController extends Controller
|
||||
*/
|
||||
public function edit(Request $request, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||
$currencies = ExpandedForm::makeSelectList($this->currencies->get());
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
|
||||
@@ -127,6 +131,10 @@ class SplitController extends Controller
|
||||
*/
|
||||
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$data = $this->arrayFromInput($request);
|
||||
$journal = $repository->updateSplitJournal($journal, $data);
|
||||
|
||||
@@ -187,6 +195,7 @@ class SplitController extends Controller
|
||||
'transactions' => $this->getTransactionDataFromRequest($request),
|
||||
];
|
||||
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
@@ -283,4 +292,5 @@ class SplitController extends Controller
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -14,10 +14,14 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use View;
|
||||
@@ -49,24 +53,127 @@ class TransactionController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param JournalTaskerInterface $tasker
|
||||
* @param string $what
|
||||
* @param Request $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param string $what
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request, JournalTaskerInterface $tasker, string $what)
|
||||
public function index(Request $request, JournalRepositoryInterface $repository, string $what)
|
||||
{
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$subTitle = trans('firefly.title_' . $what);
|
||||
$page = intval($request->get('page'));
|
||||
$journals = $tasker->getJournals($types, $page, $pageSize);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
// to make sure we only grab a subset, based on the current date (in session):
|
||||
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
|
||||
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
|
||||
$collector->setRange($start, $end);
|
||||
|
||||
// do not filter transfers if $what = transfer.
|
||||
if (!in_array($what, ['transfer', 'transfers'])) {
|
||||
Log::debug('Also get opposing account info.');
|
||||
$collector->withOpposingAccount();
|
||||
}
|
||||
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('transactions/' . $what);
|
||||
|
||||
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals'));
|
||||
unset($start, $end);
|
||||
|
||||
// then also show a list of periods where the user can click on, based on the
|
||||
// user's range and the oldest journal the user has:
|
||||
$first = $repository->first();
|
||||
$blockStart = is_null($first->id) ? new Carbon : $first->date;
|
||||
$blockStart = Navigation::startOfPeriod($blockStart, $range);
|
||||
$blockEnd = Navigation::endOfX(new Carbon, $range);
|
||||
$entries = new Collection;
|
||||
|
||||
while ($blockEnd >= $blockStart) {
|
||||
Log::debug(sprintf('Now at blockEnd: %s', $blockEnd->format('Y-m-d')));
|
||||
$blockEnd = Navigation::startOfPeriod($blockEnd, $range);
|
||||
$dateStr = $blockEnd->format('Y-m-d');
|
||||
$dateName = Navigation::periodShow($blockEnd, $range);
|
||||
$entries->push([$dateStr, $dateName]);
|
||||
$blockEnd = Navigation::subtractPeriod($blockEnd, $range, 1);
|
||||
}
|
||||
|
||||
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'entries'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param string $what
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function indexAll(Request $request, string $what)
|
||||
{
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$subTitle = sprintf('%s (%s)', trans('firefly.title_' . $what), strtolower(trans('firefly.everything')));
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
|
||||
|
||||
// do not filter transfers if $what = transfer.
|
||||
if (!in_array($what, ['transfer', 'transfers'])) {
|
||||
Log::debug('Also get opposing account info.');
|
||||
$collector->withOpposingAccount();
|
||||
}
|
||||
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('transactions/' . $what . '/all');
|
||||
|
||||
return view('transactions.index-all', compact('subTitle', 'what', 'subTitleIcon', 'journals'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param string $what
|
||||
*
|
||||
* @param string $date
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function indexDate(Request $request, string $what, string $date)
|
||||
{
|
||||
$carbon = new Carbon($date);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($carbon, $range);
|
||||
$end = Navigation::endOfPeriod($carbon, $range);
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$subTitle = trans('firefly.title_' . $what) . ' (' . Navigation::periodShow($carbon, $range) . ')';
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
|
||||
Log::debug(sprintf('Transaction index by date will show between %s and %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
|
||||
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
|
||||
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
|
||||
$collector->setRange($start, $end);
|
||||
|
||||
// do not filter transfers if $what = transfer.
|
||||
if (!in_array($what, ['transfer', 'transfers'])) {
|
||||
Log::debug('Also get opposing account info.');
|
||||
$collector->withOpposingAccount();
|
||||
}
|
||||
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('transactions/' . $what . '/' . $date);
|
||||
|
||||
return view('transactions.index-date', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'carbon'));
|
||||
|
||||
}
|
||||
|
||||
@@ -106,6 +213,10 @@ class TransactionController extends Controller
|
||||
*/
|
||||
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
|
||||
{
|
||||
if ($this->isOpeningBalance($journal)) {
|
||||
return $this->redirectToAccount($journal);
|
||||
}
|
||||
|
||||
$events = $tasker->getPiggyBankEvents($journal);
|
||||
$transactions = $tasker->getTransactionsOverview($journal);
|
||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
@@ -115,4 +226,5 @@ class TransactionController extends Controller
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use FireflyConfig;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Preferences;
|
||||
@@ -45,11 +46,11 @@ class IsConfirmed
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
// must the user be confirmed in the first place?
|
||||
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
// user must be logged in, then continue:
|
||||
$isConfirmed = Preferences::get('user_confirmed', false)->data;
|
||||
|
||||
if ($isConfirmed === false && $confirmAccount === true) {
|
||||
if ($isConfirmed === false && $mustConfirmAccount === true) {
|
||||
|
||||
// user account is not confirmed, redirect to
|
||||
// confirmation page:
|
||||
|
@@ -14,6 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use FireflyConfig;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Preferences;
|
||||
@@ -45,10 +46,10 @@ class IsNotConfirmed
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
// must the user be confirmed in the first place?
|
||||
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
|
||||
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
|
||||
// user must be logged in, then continue:
|
||||
$isConfirmed = Preferences::get('user_confirmed', false)->data;
|
||||
if ($isConfirmed || $confirmAccount === false) {
|
||||
if ($isConfirmed || $mustConfirmAccount === false) {
|
||||
// user account is confirmed, simply send them home.
|
||||
return redirect(route('home'));
|
||||
}
|
||||
|
@@ -57,13 +57,13 @@ class Range
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param Closure $theNext
|
||||
* @param Closure $next
|
||||
* @param string|null $guard
|
||||
*
|
||||
* @return mixed
|
||||
* @internal param Closure $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $theNext, $guard = null)
|
||||
public function handle(Request $request, Closure $next, $guard = null)
|
||||
{
|
||||
if (!Auth::guard($guard)->guest()) {
|
||||
|
||||
@@ -80,7 +80,7 @@ class Range
|
||||
$this->configureList();
|
||||
}
|
||||
|
||||
return $theNext($request);
|
||||
return $next($request);
|
||||
|
||||
}
|
||||
|
||||
@@ -130,13 +130,30 @@ class Range
|
||||
*/
|
||||
private function datePicker()
|
||||
{
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Session::get('start');
|
||||
$end = Session::get('end');
|
||||
$prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period
|
||||
$prevEnd = Navigation::endOfPeriod($prevStart, $viewRange);
|
||||
$nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period
|
||||
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
/** @var Carbon $start */
|
||||
$start = Session::get('start');
|
||||
/** @var Carbon $end */
|
||||
$end = Session::get('end');
|
||||
|
||||
$prevStart = clone $start;
|
||||
$prevEnd = clone $start;
|
||||
$nextStart = clone $end;
|
||||
$nextEnd = clone $end;
|
||||
if ($viewRange === 'custom') {
|
||||
$days = $start->diffInDays($end);
|
||||
$prevStart->subDays($days);
|
||||
$nextEnd->addDays($days);
|
||||
unset($days);
|
||||
}
|
||||
|
||||
if ($viewRange !== 'custom') {
|
||||
$prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period
|
||||
$prevEnd = Navigation::endOfPeriod($prevStart, $viewRange);
|
||||
$nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period
|
||||
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
|
||||
}
|
||||
|
||||
$ranges = [];
|
||||
$ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')];
|
||||
$ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')];
|
||||
@@ -146,6 +163,7 @@ class Range
|
||||
default:
|
||||
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".');
|
||||
case '1D':
|
||||
case 'custom':
|
||||
$format = (string)trans('config.month_and_day');
|
||||
break;
|
||||
case '3M':
|
||||
|
@@ -42,6 +42,7 @@ class AccountFormRequest extends Request
|
||||
'name' => trim($this->input('name')),
|
||||
'active' => intval($this->input('active')) === 1,
|
||||
'accountType' => $this->input('what'),
|
||||
'currency_id' => intval($this->input('currency_id')),
|
||||
'virtualBalance' => round($this->input('virtualBalance'), 2),
|
||||
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
|
||||
'iban' => trim($this->input('iban')),
|
||||
@@ -80,6 +81,7 @@ class AccountFormRequest extends Request
|
||||
'iban' => 'iban',
|
||||
'virtualBalance' => 'numeric',
|
||||
'openingBalanceDate' => 'date',
|
||||
'currency_id' => 'exists:transaction_currencies,id',
|
||||
'accountNumber' => 'between:1,255|uniqueAccountNumberForUser',
|
||||
'accountRole' => 'in:' . $accountRoles,
|
||||
'active' => 'boolean',
|
||||
|
@@ -36,7 +36,9 @@ class ConfigurationRequest extends Request
|
||||
public function getConfigurationData(): array
|
||||
{
|
||||
return [
|
||||
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
|
||||
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
|
||||
'must_confirm_account' => intval($this->get('must_confirm_account')) === 1,
|
||||
'is_demo_site' => intval($this->get('is_demo_site')) === 1,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -46,7 +48,9 @@ class ConfigurationRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'single_user_mode' => 'between:0,1|numeric',
|
||||
'single_user_mode' => 'between:0,1|numeric',
|
||||
'must_confirm_account' => 'between:0,1|numeric',
|
||||
'is_demo_site' => 'between:0,1|numeric',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
|
@@ -160,6 +160,7 @@ class JournalFormRequest extends Request
|
||||
*/
|
||||
private function getFieldOrEmptyString(string $field): string
|
||||
{
|
||||
return $this->get($field) ?? '';
|
||||
$string = $this->get($field) ?? '';
|
||||
return trim($string);
|
||||
}
|
||||
}
|
||||
|
132
app/Http/Requests/ReportFormRequest.php
Normal file
132
app/Http/Requests/ReportFormRequest.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportFormRequest.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class CategoryFormRequest
|
||||
*
|
||||
*
|
||||
* @package FireflyIII\Http\Requests
|
||||
*/
|
||||
class ReportFormRequest extends Request
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
// Only allow logged in users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccountList():Collection
|
||||
{
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$set = $this->get('accounts');
|
||||
$collection = new Collection;
|
||||
if (is_array($set)) {
|
||||
foreach ($set as $accountId) {
|
||||
$account = $repository->find(intval($accountId));
|
||||
if (!is_null($account->id)) {
|
||||
$collection->push($account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getCategoryList(): Collection
|
||||
{
|
||||
/** @var CategoryRepositoryInterface $repository */
|
||||
$repository = app(CategoryRepositoryInterface::class);
|
||||
$set = $this->get('category');
|
||||
$collection = new Collection;
|
||||
if (is_array($set)) {
|
||||
foreach ($set as $categoryId) {
|
||||
$category = $repository->find(intval($categoryId));
|
||||
if (!is_null($category->id)) {
|
||||
$collection->push($category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getEndDate(): Carbon
|
||||
{
|
||||
$date = new Carbon;
|
||||
$range = $this->get('daterange');
|
||||
$parts = explode(' - ', strval($range));
|
||||
if (count($parts) === 2) {
|
||||
try {
|
||||
$date = new Carbon($parts[1]);
|
||||
} catch (Exception $e) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid date range.', $range));
|
||||
}
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Carbon
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getStartDate(): Carbon
|
||||
{
|
||||
$date = new Carbon;
|
||||
$range = $this->get('daterange');
|
||||
$parts = explode(' - ', strval($range));
|
||||
if (count($parts) === 2) {
|
||||
try {
|
||||
$date = new Carbon($parts[0]);
|
||||
} catch (Exception $e) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid date range.', $range));
|
||||
}
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'report_type' => 'in:audit,default,category',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -90,11 +90,12 @@ class SplitJournalFormRequest extends Request
|
||||
$return = [];
|
||||
// description is leading because it is one of the mandatory fields.
|
||||
foreach ($this->get('description') as $index => $description) {
|
||||
$category = $this->get('category')[$index] ?? '';
|
||||
$transaction = [
|
||||
'description' => $description,
|
||||
'amount' => round($this->get('amount')[$index], 2),
|
||||
'budget_id' => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0,
|
||||
'category' => $this->get('category')[$index] ?? '',
|
||||
'category' => trim($category),
|
||||
'source_account_id' => isset($this->get('source_account_id')[$index])
|
||||
? intval($this->get('source_account_id')[$index])
|
||||
: intval(
|
||||
|
@@ -83,6 +83,17 @@ Breadcrumbs::register(
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'accounts.show.all', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
|
||||
$breadcrumbs->parent('accounts.show', $account);
|
||||
|
||||
$title = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
|
||||
|
||||
$breadcrumbs->push($title, route('accounts.show.all', [$account->id]));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Breadcrumbs::register(
|
||||
'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
|
||||
$breadcrumbs->parent('accounts.show', $account);
|
||||
@@ -573,6 +584,28 @@ Breadcrumbs::register(
|
||||
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', [$what]));
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'transactions.index.all', function (BreadCrumbGenerator $breadcrumbs, string $what) {
|
||||
$breadcrumbs->parent('transactions.index', $what);
|
||||
|
||||
$title = sprintf('%s (%s)', trans('breadcrumbs.' . $what . '_list'), strtolower(trans('firefly.everything')));
|
||||
|
||||
$breadcrumbs->push($title, route('transactions.index.all', [$what]));
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'transactions.index.date', function (BreadCrumbGenerator $breadcrumbs, string $what, Carbon $date) {
|
||||
$breadcrumbs->parent('transactions.index', $what);
|
||||
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$title = trans('breadcrumbs.' . $what . '_list') . ' (' . Navigation::periodShow($date, $range) . ')';
|
||||
|
||||
$breadcrumbs->push($title, route('transactions.index.date', [$what, $date->format('Y-m-d')]));
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'transactions.create', function (BreadCrumbGenerator $breadcrumbs, string $what) {
|
||||
$breadcrumbs->parent('transactions.index', $what);
|
||||
|
85
app/Import/Converter/TagSplit.php
Normal file
85
app/Import/Converter/TagSplit.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* TagSplit.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Import\Converter;
|
||||
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class TagSplit
|
||||
*
|
||||
* @package FireflyIII\Import\Converter
|
||||
*/
|
||||
class TagSplit
|
||||
{
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param array $mapping
|
||||
* @param array $parts
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public static function createSetFromSplits(User $user, array $mapping, array $parts): Collection
|
||||
{
|
||||
$set = new Collection;
|
||||
Log::debug('Exploded parts.', $parts);
|
||||
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class, [$user]);
|
||||
|
||||
|
||||
/** @var string $part */
|
||||
foreach ($parts as $part) {
|
||||
if (isset($mapping[$part])) {
|
||||
Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $mapping[$part]]);
|
||||
$tag = $repository->find(intval($mapping[$part]));
|
||||
if (!is_null($tag->id)) {
|
||||
Log::debug('Found tag by ID', ['id' => $tag->id]);
|
||||
|
||||
$set->push($tag);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// not mapped? Still try to find it first:
|
||||
$tag = $repository->findByTag($part);
|
||||
if (!is_null($tag->id)) {
|
||||
Log::debug('Found tag by name ', ['id' => $tag->id]);
|
||||
|
||||
$set->push($tag);
|
||||
}
|
||||
if (is_null($tag->id)) {
|
||||
// create new tag
|
||||
$tag = $repository->store(
|
||||
[
|
||||
'tag' => $part,
|
||||
'date' => null,
|
||||
'description' => $part,
|
||||
'latitude' => null,
|
||||
'longitude' => null,
|
||||
'zoomLevel' => null,
|
||||
'tagMode' => 'nothing',
|
||||
]
|
||||
);
|
||||
Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]);
|
||||
$set->push($tag);
|
||||
}
|
||||
}
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
}
|
@@ -13,7 +13,6 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Import\Converter;
|
||||
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
@@ -41,49 +40,7 @@ class TagsComma extends BasicConverter implements ConverterInterface
|
||||
return new Collection;
|
||||
}
|
||||
$parts = array_unique(explode(',', $value));
|
||||
$set = new Collection;
|
||||
Log::debug('Exploded parts.', $parts);
|
||||
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class, [$this->user]);
|
||||
|
||||
|
||||
/** @var string $part */
|
||||
foreach ($parts as $part) {
|
||||
if (isset($this->mapping[$part])) {
|
||||
Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]);
|
||||
$tag = $repository->find(intval($this->mapping[$part]));
|
||||
if (!is_null($tag->id)) {
|
||||
Log::debug('Found tag by ID', ['id' => $tag->id]);
|
||||
|
||||
$set->push($tag);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// not mapped? Still try to find it first:
|
||||
$tag = $repository->findByTag($part);
|
||||
if (!is_null($tag->id)) {
|
||||
Log::debug('Found tag by name ', ['id' => $tag->id]);
|
||||
|
||||
$set->push($tag);
|
||||
}
|
||||
if (is_null($tag->id)) {
|
||||
// create new tag
|
||||
$tag = $repository->store(
|
||||
[
|
||||
'tag' => $part,
|
||||
'date' => null,
|
||||
'description' => $part,
|
||||
'latitude' => null,
|
||||
'longitude' => null,
|
||||
'zoomLevel' => null,
|
||||
'tagMode' => 'nothing',
|
||||
]
|
||||
);
|
||||
Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]);
|
||||
$set->push($tag);
|
||||
}
|
||||
}
|
||||
$set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts);
|
||||
$this->setCertainty(100);
|
||||
|
||||
return $set;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user