Compare commits

..

248 Commits
4.0.0 ... 4.1.4

Author SHA1 Message Date
James Cole
0a6f299ae6 Merge branch 'release/4.1.4' 2016-10-30 08:56:35 +01:00
James Cole
73f87e30c2 Changelog.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 08:52:09 +01:00
James Cole
838ece2c89 Merge pull request #368 from JC5/l10n_develop
New Crowdin translations
2016-10-30 07:32:23 +01:00
James Cole
d8b88ea2c0 New translations 2016-10-30 07:20:17 +01:00
James Cole
5908951b75 New translations 2016-10-30 07:20:17 +01:00
James Cole
0b41f4c4d2 New translations 2016-10-30 07:20:16 +01:00
James Cole
35439d4fbc New translations 2016-10-30 07:20:16 +01:00
James Cole
fdce40310f New translations 2016-10-30 07:20:15 +01:00
James Cole
6b4785ae32 New translations 2016-10-30 07:20:15 +01:00
James Cole
f74e8e9cb7 New translations 2016-10-30 07:20:13 +01:00
James Cole
5a4eb7e09e New translations 2016-10-30 07:20:12 +01:00
James Cole
3bc4df03cc New translations 2016-10-30 07:20:11 +01:00
James Cole
5d585132fb New translations 2016-10-30 07:20:11 +01:00
James Cole
eff4905883 New translations 2016-10-30 07:20:10 +01:00
James Cole
8923ac4fe3 New translations 2016-10-30 07:20:09 +01:00
James Cole
073535e5ed New translations 2016-10-30 07:20:09 +01:00
James Cole
d304b90ca6 New translations 2016-10-30 07:20:08 +01:00
James Cole
816c26e14e New translations 2016-10-30 07:20:07 +01:00
James Cole
b1244ffa01 New translations 2016-10-30 07:20:07 +01:00
James Cole
fc1342bff9 New translations 2016-10-30 07:20:06 +01:00
James Cole
d7b95194b5 New translations 2016-10-30 07:20:04 +01:00
James Cole
b58bdeccd2 New translations 2016-10-30 07:20:04 +01:00
James Cole
f260b9bdee New translations 2016-10-30 07:20:02 +01:00
James Cole
fb1bdc9ec5 New translations 2016-10-30 07:20:01 +01:00
James Cole
697eff48fc New translations 2016-10-30 07:20:00 +01:00
James Cole
c05019339a Approved. Step name: Proofread 2016-10-30 07:20:00 +01:00
James Cole
8438efaf41 Approved. Step name: Proofread 2016-10-30 07:19:59 +01:00
James Cole
81c019cc99 Translated 2016-10-30 07:19:59 +01:00
James Cole
c773fdc435 Approved. Step name: Proofread 2016-10-30 07:19:58 +01:00
James Cole
c1406f51f1 Approved. Step name: Proofread 2016-10-30 07:19:58 +01:00
James Cole
92affd3440 Approved. Step name: Proofread 2016-10-30 07:19:57 +01:00
James Cole
d3da0652ef Approved. Step name: Proofread 2016-10-30 07:19:57 +01:00
James Cole
e3fbbd6cf1 Translated 2016-10-30 07:19:56 +01:00
James Cole
fcff13470c Approved. Step name: Proofread 2016-10-30 07:19:56 +01:00
James Cole
e3061ee7e7 Approved. Step name: Proofread 2016-10-30 07:19:55 +01:00
James Cole
0ee305fc4a Approved. Step name: Proofread 2016-10-30 07:19:55 +01:00
James Cole
58b93fd0c4 Approved. Step name: Proofread 2016-10-30 07:19:53 +01:00
James Cole
b30217fa2d Approved. Step name: Proofread 2016-10-30 07:19:52 +01:00
James Cole
ae48eec3a2 Translated 2016-10-30 07:19:52 +01:00
James Cole
948233ba27 Translated 2016-10-30 07:19:52 +01:00
James Cole
c2db9b183a New translations 2016-10-30 07:19:51 +01:00
James Cole
6d2b88fa0b New translations 2016-10-30 07:19:51 +01:00
James Cole
1d5da825c5 New translations 2016-10-30 07:19:50 +01:00
James Cole
330c9b53d6 New translations 2016-10-30 07:19:50 +01:00
James Cole
751fe7d4fb New translations 2016-10-30 07:19:49 +01:00
James Cole
9df1fc6e5d New translations 2016-10-30 07:19:48 +01:00
James Cole
8d660f1701 New translations 2016-10-30 07:19:46 +01:00
James Cole
4d61d3c4aa New translations 2016-10-30 07:19:46 +01:00
James Cole
0457088c99 New translations 2016-10-30 07:19:45 +01:00
James Cole
8e575da74e New translations 2016-10-30 07:19:45 +01:00
James Cole
48ed28888e Translated 2016-10-30 07:19:43 +01:00
James Cole
4084b1124e Translated 2016-10-30 07:19:42 +01:00
James Cole
60ba607027 Translated 2016-10-30 07:19:42 +01:00
James Cole
3df2c11b4a Translated 2016-10-30 07:19:41 +01:00
James Cole
c93221923a Translated 2016-10-30 07:19:41 +01:00
James Cole
375317e932 New translations 2016-10-30 07:19:40 +01:00
James Cole
7ce527957a New translations 2016-10-30 07:19:40 +01:00
James Cole
6946521199 New translations 2016-10-30 07:19:39 +01:00
James Cole
18ee20e680 Update crowdin file [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 06:37:00 +01:00
James Cole
c53da15219 Update composer.lock in anticipation of new release.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 06:20:18 +01:00
James Cole
d4995e342f Fixed #330 2016-10-30 06:14:07 +01:00
James Cole
c9f14da294 Translations. 2016-10-29 17:30:55 +02:00
James Cole
e9c2446cba Debug log for #366 2016-10-29 16:16:10 +02:00
James Cole
35f179625c New queries for #366 2016-10-29 16:11:54 +02:00
James Cole
39749aa113 First code set for #330 2016-10-29 15:14:33 +02:00
James Cole
ba65e982fd Forgot to include model [skip ci] 2016-10-29 11:42:04 +02:00
James Cole
b50e5d7e59 Can also change destination in new rule. 2016-10-29 09:22:51 +02:00
James Cole
a3148dc172 Code for #321 2016-10-29 09:03:14 +02:00
James Cole
73f1491d2d Updates for translations. 2016-10-29 07:44:46 +02:00
James Cole
28eb54dc96 Initial split for report options. 2016-10-28 19:01:52 +02:00
James Cole
21fb426524 Make sure date is localised. 2016-10-28 18:16:30 +02:00
James Cole
d5710ca809 Update Crowdin configuration file 2016-10-28 14:06:41 +02:00
James Cole
0ba6cdda17 Merge pull request #364 from schoentoon/develop
Added dockerfile
2016-10-27 19:40:27 +02:00
James Cole
afdcfa8525 Tweak reports. 2016-10-26 19:45:10 +02:00
James Cole
5db4f8512b Tweak reports. 2016-10-26 19:37:19 +02:00
James Cole
dc0c1b73bc Better view for expenses. 2016-10-26 19:32:19 +02:00
James Cole
f999257095 New verify database routine. 2016-10-26 19:32:07 +02:00
James Cole
7182909e28 Keep the box [skip ci] 2016-10-26 16:54:52 +02:00
James Cole
fe3f015171 Add more stuff to ajax controllers, making report controller simpler. 2016-10-26 16:46:43 +02:00
Toon Schoenmakers
5bb668be63 Added dockerfile 2016-10-26 15:07:36 +02:00
James Cole
01de147900 Display message about common error. 2016-10-26 06:41:50 +02:00
James Cole
a7e5fcc806 Move some stuff over to AJAX thing. 2016-10-25 18:53:54 +02:00
James Cole
e2d187d74b Various small bug fixes. 2016-10-24 18:01:15 +02:00
James Cole
48b0620629 New help thing. 2016-10-23 17:33:53 +02:00
James Cole
19e9f382e4 Add some rounding to make forms more neat. 2016-10-23 16:56:18 +02:00
James Cole
446eaf6588 Some code cleanup [skip ci] 2016-10-23 14:58:39 +02:00
James Cole
78deb1420d Some fixes for bills. 2016-10-23 14:56:05 +02:00
James Cole
e092515dff Better export. 2016-10-23 12:55:07 +02:00
James Cole
81f6fef978 Add new line to files [skip ci] 2016-10-23 12:42:44 +02:00
James Cole
6a2f8fa9ee No use models directly. 2016-10-23 12:41:54 +02:00
James Cole
a79a8c8874 Various small upgrades. 2016-10-23 12:37:12 +02:00
James Cole
c39659b064 Remove a lot of references to user id. 2016-10-23 12:19:32 +02:00
James Cole
9a30fbd05a Move stuff to request classes for #339 2016-10-23 12:10:22 +02:00
James Cole
83f48418f6 Small updates [skip ci] 2016-10-23 09:57:04 +02:00
James Cole
bcd7b41c91 Simplified export. 2016-10-23 09:44:14 +02:00
James Cole
cefb7d12bc Merge branch 'release/4.1.3' 2016-10-22 22:45:27 +02:00
James Cole
3c0c15103e This fixes #361 2016-10-22 22:44:57 +02:00
James Cole
a8a8afc2be More for #339 2016-10-22 22:03:00 +02:00
James Cole
49e32abd3f Move some code for #339 2016-10-22 21:40:31 +02:00
James Cole
7977eefaca Merge branch 'release/4.1.2' 2016-10-22 20:52:54 +02:00
James Cole
f1fa6c3108 Fixed a bug in the store transaction routine. 2016-10-22 20:50:20 +02:00
James Cole
2fa0d55f39 Merge branch 'release/4.1.1' 2016-10-22 12:03:34 +02:00
James Cole
5bff509346 New translations.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 12:03:11 +02:00
James Cole
a147e9b74a Fix edit screen.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 11:53:34 +02:00
James Cole
0d87f7c4ca Better implementation of markdown.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 11:50:33 +02:00
James Cole
8c675615df Support markdown in notes.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 11:47:47 +02:00
James Cole
7edd1bff40 Merge branch 'release/4.1.0' 2016-10-22 10:21:15 +02:00
James Cole
3bfcb1f3ab New change log. [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 10:20:25 +02:00
James Cole
7b6c63e6a8 New version number [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-22 10:15:50 +02:00
James Cole
5500e5b0aa Remove debug classes 2016-10-22 10:13:56 +02:00
James Cole
e4d249e73c Piggy bank supports notes (#350) 2016-10-22 10:13:49 +02:00
James Cole
091f6e918b Fix some reported issues. 2016-10-22 09:44:47 +02:00
James Cole
5d9b68c3e7 Various code cleanup [skip ci] 2016-10-22 09:39:31 +02:00
James Cole
12a6a61100 Various code cleanup [skip ci] 2016-10-22 09:33:03 +02:00
James Cole
7ce3b8d4ef Updated events, fixes #345 2016-10-22 09:31:27 +02:00
James Cole
3d9b855849 Force larval 5.3.18 2016-10-22 07:52:17 +02:00
James Cole
2346d2ec05 Fine tuning split edit screens. 2016-10-22 07:28:31 +02:00
James Cole
a4c081c8a5 Fix unset variable. [skip ci] 2016-10-21 22:01:42 +02:00
James Cole
316980efbd Fix unset variable. [skip ci] 2016-10-21 22:00:45 +02:00
James Cole
a05bc0eed0 Fix route [skip ci] 2016-10-21 21:57:10 +02:00
James Cole
4d1c271da6 Renamed a route [skip ci] 2016-10-21 21:54:57 +02:00
James Cole
0dd7ecbfbe Remove code no longer used. 2016-10-21 21:43:12 +02:00
James Cole
0dc188b083 Removed old code 2016-10-21 21:41:50 +02:00
James Cole
6a553f77f3 Large update to fix split journals. 2016-10-21 21:41:31 +02:00
James Cole
a74cef439b For simplicity, split controller. 2016-10-21 19:20:03 +02:00
James Cole
9a3cd27700 Many updates to get split transactions and normal transactions working side by side. 2016-10-21 19:06:22 +02:00
James Cole
801c7c0ab6 Remove unused function. 2016-10-21 13:22:45 +02:00
James Cole
a95a4e783a Fix and simplify bill repos 2016-10-21 13:20:51 +02:00
James Cole
af1ee9db93 This fixes bills unpaid. 2016-10-21 07:29:25 +02:00
James Cole
fcdb6fd2a7 Do loop instead of while loop [skip ci] 2016-10-21 06:41:33 +02:00
James Cole
97c0fb389d More logs [skip ci] 2016-10-21 06:38:00 +02:00
James Cole
a9c3992331 Back to old method [skip ci] 2016-10-21 06:33:56 +02:00
James Cole
a38e057fa7 Rewrote some methods to fix #341 2016-10-21 06:26:12 +02:00
James Cole
f83aaf77f1 Improve bill things for issue #341 2016-10-20 21:40:45 +02:00
James Cole
d92768ecbf This code fixes #349 2016-10-20 19:10:43 +02:00
James Cole
b9308cd74a Test flash messages. 2016-10-20 16:51:05 +02:00
James Cole
78b577bc9d Better ip info [skip ci] 2016-10-18 06:50:35 +02:00
James Cole
7d247897ed Should correctly show user info. [skip ci] 2016-10-18 06:47:48 +02:00
James Cole
5dcbdec491 Update composer file and update routine. [skip ci] 2016-10-15 18:52:21 +02:00
James Cole
9bf980431e Remove unused methods. 2016-10-15 14:07:51 +02:00
James Cole
da60bfbcff Better text [skip ci] 2016-10-15 14:05:56 +02:00
James Cole
92553cbc7e Add icon, missing translation [skip ci] 2016-10-15 12:41:45 +02:00
James Cole
8e48e53f17 Restucturing some code. 2016-10-15 12:39:34 +02:00
James Cole
2f9a4bb79a Better text 2016-10-15 12:39:11 +02:00
James Cole
ac968dd6cd Extended the user admin. 2016-10-15 07:11:53 +02:00
James Cole
6e4f2c0c8a Small script to upgrade transactions. 2016-10-15 06:19:21 +02:00
James Cole
d662c18ed7 Fix sorting in chart. [skip ci] 2016-10-14 20:07:15 +02:00
James Cole
e4ea234707 New revenue accounts chart. 2016-10-14 20:01:17 +02:00
James Cole
0b526c0168 New revenue chart 2016-10-14 19:59:10 +02:00
James Cole
2acde5c72a Option to show deposit accounts on the front page. 2016-10-14 19:52:30 +02:00
James Cole
ec8cf2c459 New preferences screen. 2016-10-14 19:48:19 +02:00
James Cole
3598780d54 This should at least catch #357 2016-10-14 19:18:00 +02:00
James Cole
35dd8ac6e6 Revert "This should at least catch #357"
This reverts commit 5ff7c7ffab.
2016-10-14 19:16:39 +02:00
James Cole
5ff7c7ffab This should at least catch #357 2016-10-14 19:16:28 +02:00
James Cole
399db47826 Merge branch 'release/4.0.2' 2016-10-14 17:15:47 +02:00
James Cole
148956a60d Some code reformatting. 2016-10-14 17:14:54 +02:00
James Cole
3670053a58 Some code reformatting [skip ci] 2016-10-14 17:14:28 +02:00
James Cole
e8e2b9704f New translations. 2016-10-14 17:14:04 +02:00
James Cole
fcdeebcc06 Some last minute updated texts. 2016-10-14 16:51:38 +02:00
James Cole
586ed82e88 New changelog. 2016-10-14 16:26:05 +02:00
James Cole
cc400d1e2e Merge pull request #356 from telyn/require-intl
Require PHP intl extension
2016-10-12 10:32:28 +02:00
Telyn
6edbfb27aa Add ext-intl to dependencies 2016-10-12 09:22:21 +01:00
James Cole
8fc9251b93 Fix name of language. [skip ci] 2016-10-10 19:08:09 +02:00
James Cole
10af888a97 Expand view [skip ci] 2016-10-10 13:27:35 +02:00
James Cole
89f2328846 Forgot include [skip ci] 2016-10-10 13:25:27 +02:00
James Cole
48e8cd20b4 Removed unused Twig methods. 2016-10-10 13:08:02 +02:00
James Cole
394ef23eda New local names [skip ci] 2016-10-10 08:27:08 +02:00
James Cole
62aa1eb487 Updated translations [skip ci] 2016-10-10 08:19:00 +02:00
James Cole
1500018ccc A new language arrives! [skip ci] 2016-10-10 08:13:27 +02:00
James Cole
23fad62d46 Completely removed account crud class. 2016-10-10 08:03:03 +02:00
James Cole
3cbf00734f Remove storeMeta 2016-10-10 07:53:25 +02:00
James Cole
1dc17dd59d Move getActiveAccountsByType 2016-10-10 07:53:12 +02:00
James Cole
f8935c92ea Unrelated code cleanup. 2016-10-10 07:49:55 +02:00
James Cole
de6f838413 Moved getAccountsByType 2016-10-10 07:49:39 +02:00
James Cole
e8a095e543 Moved getAccountsById 2016-10-10 07:25:27 +02:00
James Cole
717c1d080e Copied (not yet removed) findByName 2016-10-10 07:20:49 +02:00
James Cole
0ae9afd325 Move findByIban 2016-10-10 07:16:05 +02:00
James Cole
d1b56c2afa Moved findByAccountNumber 2016-10-10 07:14:01 +02:00
James Cole
8ef7c5ac33 Moved find() method to new class. 2016-10-10 07:12:39 +02:00
James Cole
7180a40cd8 Refactored some methods surrounding the opening balance of an account. 2016-10-10 07:01:14 +02:00
James Cole
71804af624 Updated some model code. 2016-10-10 06:50:24 +02:00
James Cole
85dc7f3643 Moved another method. 2016-10-10 06:49:50 +02:00
James Cole
a866d13b75 Forgot to add argument 2016-10-10 06:49:39 +02:00
James Cole
fcb5e4eabc Moved leftOnAccount() 2016-10-10 06:47:42 +02:00
James Cole
ade1cf9c19 Fixed wrong listing. 2016-10-10 06:40:50 +02:00
James Cole
0f1ec7d003 Removed double method. 2016-10-09 21:49:31 +02:00
James Cole
7e038afece Must be unsigned 2016-10-09 21:36:22 +02:00
James Cole
9bb8e182fa Forgot a translation 2016-10-09 21:36:15 +02:00
James Cole
e94ae126fd Refactored accountRepository::getJournals > accountTasker > getJournals 2016-10-09 21:36:03 +02:00
James Cole
5bb8c6a366 This should fix #355 2016-10-09 20:18:46 +02:00
James Cole
30844df5d4 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Fixed issue causing all imported transactions to have unknown opposing account with ABN AMRO specific
2016-10-09 20:18:14 +02:00
James Cole
63e4a410a7 Merge pull request #354 from roberthorlings/bugfix/abn-amro-specific
Bugfix for issue with ABN AMRO specific
2016-10-09 20:00:48 +02:00
Robert Horlings
ee9a5d91e2 Merge branch 'develop' into bugfix/abn-amro-specific 2016-10-09 19:53:11 +02:00
Robert Horlings
171ab8a4c3 Fixed issue causing all imported transactions to have unknown opposing account with ABN AMRO specific 2016-10-09 17:19:00 +02:00
James Cole
96740aaac4 Extend transaction model for #351 2016-10-09 17:06:52 +02:00
James Cole
2017720096 Experimental new getJournals method. 2016-10-09 16:22:08 +02:00
James Cole
b77ea6d316 Add some phpdoc 2016-10-09 11:21:41 +02:00
James Cole
f5adb4047f Remove methods no longer used. 2016-10-09 10:59:28 +02:00
James Cole
b082858866 Removed unused blocks from the front page. Savings and piggy banks. 2016-10-09 10:58:54 +02:00
James Cole
a8a014189d Removed method that was already there under another name. 2016-10-09 10:57:06 +02:00
James Cole
39ea9e85a7 Various code cleanup and refactoring. Restored cache. 2016-10-09 10:53:37 +02:00
James Cole
a4d2ed74fc Make sure all journals are included. 2016-10-09 09:41:03 +02:00
James Cole
90f2e27f1f Refactoring income and expense reports. 2016-10-09 09:32:12 +02:00
James Cole
a3359ba47a Moved destroy() method from CRUD to Account repos. 2016-10-09 08:20:29 +02:00
James Cole
1d2d3523d6 Move CRUD method count() to account repository 2016-10-09 08:18:47 +02:00
James Cole
3f40751a1a Forgot to exclude a method. 2016-10-09 08:06:49 +02:00
James Cole
b5b55e862c Clean up code. 2016-10-09 07:59:14 +02:00
James Cole
c64771b76b Move some methods around, refactoring. 2016-10-09 07:58:27 +02:00
James Cole
ea7ee7ee9a Moved a report out of the controller. 2016-10-08 16:24:07 +02:00
James Cole
a1f797c4d1 Moved a method around. 2016-10-08 16:04:05 +02:00
James Cole
d0c92a2244 Clean up account report helper. 2016-10-08 15:59:58 +02:00
James Cole
6e90c033b1 Start of some remodelling. 2016-10-08 14:54:32 +02:00
James Cole
24f62b8fce Some minor refactoring. 2016-10-08 10:02:33 +02:00
James Cole
d43936155c Removed some unused code. 2016-10-07 16:33:17 +02:00
James Cole
39dab4fdd9 Remove unused class. 2016-10-07 12:28:14 +02:00
James Cole
c0fdf44ad2 Small cleaning up. 2016-10-07 11:40:03 +02:00
James Cole
4d91f7d23a This fixes #344 2016-10-07 09:40:50 +02:00
James Cole
49af6522a8 Some code cleanup. 2016-10-07 05:44:21 +02:00
James Cole
3c5f9487a8 Prep change log for next version [skip ci] 2016-10-07 05:43:47 +02:00
James Cole
f5cb87f5c3 Merge pull request #348 from SanderKleykens/feature/postgres-compatibility
PostgreSQL compatibility
2016-10-07 05:32:10 +02:00
Sander Kleykens
cf543613c9 Fix the CSV importer failing when using PostgreSQL
Add ordered column to the list of columns that are selected so PostgreSQL doesn't throw an error
2016-10-06 23:49:33 +02:00
Sander Kleykens
5c239c91db Convert raw XORs to a construct compatible with Laravel's query builder 2016-10-06 22:27:10 +02:00
James Cole
9920504232 Fixes #346 2016-10-06 05:26:38 +02:00
James Cole
5540697dbd Removed a method no longer necessary. 2016-10-05 16:09:37 +02:00
James Cole
b355c18e0c Some code cleanup and copyright cleanup. [skip ci] 2016-10-05 06:52:15 +02:00
James Cole
1e90485c5f Merge branch 'master' into develop
* master:
  added some comments
  removed some debug commands
  Better descriptions for ING accounts
2016-10-05 06:31:47 +02:00
James Cole
dc784c53b5 Fix for #343 2016-10-04 21:51:46 +02:00
James Cole
5a47391a64 Merge branch 'release/4.0.1' 2016-10-04 20:13:50 +02:00
James Cole
8a106bd16a Merge branch 'release/4.0.1' into develop 2016-10-04 20:13:50 +02:00
James Cole
a31ac79173 New version. 2016-10-04 20:13:40 +02:00
James Cole
0d0a604254 Changelog for 4.0.1 [skip ci] 2016-10-04 20:13:09 +02:00
James Cole
724d25f2c2 Merge branch 'develop'
* develop:
  Fixed some rare bugs.
  Extra clear button to reapply rules #307
  Fix trim when null [skip ci]
  Fixed a bug where incoming transactions would not be properly filtered in several reports.
  Removed for #334
  Fix #337 [skip ci]
  Fix #335
  Remove account extra text #336 [skip ci]
  Fixes bug #338
  Refer to correct page [skip ci]
  Catch unset row.
2016-10-04 20:08:35 +02:00
James Cole
8ed22d452d Merge pull request #342 from tomwerf/master
ING Import
2016-10-04 20:02:34 +02:00
Tom van der Werf
d7fef45a56 added some comments 2016-10-04 17:50:01 +00:00
Tom van der Werf
dc22802dec removed some debug commands 2016-10-04 17:30:49 +00:00
Tom van der Werf
ce5af7b1d9 Better descriptions for ING accounts 2016-10-03 17:08:24 +00:00
James Cole
0a147e5c9c Fixed some rare bugs. 2016-10-02 15:09:43 +02:00
James Cole
7d21255f7f Extra clear button to reapply rules #307 2016-10-02 08:14:11 +02:00
James Cole
13f952f182 Fix trim when null [skip ci] 2016-10-01 09:41:16 +02:00
James Cole
b494be228b Fixed a bug where incoming transactions would not be properly filtered in several reports. 2016-10-01 09:37:18 +02:00
James Cole
0fdaac53d0 Removed for #334 2016-10-01 08:49:33 +02:00
James Cole
e1b3a08878 Fix #337 [skip ci] 2016-10-01 08:49:02 +02:00
James Cole
dc893588b0 Fix #335 2016-10-01 08:48:13 +02:00
James Cole
b9fcc443ec Remove account extra text #336 [skip ci] 2016-10-01 08:45:14 +02:00
James Cole
d8586c8043 Fixes bug #338 2016-09-29 19:17:24 +02:00
James Cole
4252a3e53b Refer to correct page [skip ci] 2016-09-29 07:04:58 +02:00
James Cole
dbb5cdb9cf Catch unset row. 2016-09-29 07:02:47 +02:00
James Cole
3ec8a8c375 Merge branch 'release/4.0.0' into develop 2016-09-26 18:37:33 +02:00
586 changed files with 19047 additions and 13629 deletions

View File

@@ -2,7 +2,105 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [4.0.0] - 2015-05-25 ## [4.1.4] - 2016-10-30
### Added
- New Dockerfile thanks to @schoentoon
- Added changing the destination account as rule action.
- Added changing the source account as rule action.
- Can convert transactions into different types.
### Changed
- Changed the export routine to be more future-proof.
- Improved help routine.
- Integrated CrowdIn translations.
- Simplified reports
- Change error message to refer to solution.
### Fixed
- #367 thanks to @HungryFeline
- #366 thanks to @3mz3t
- #362 and #341 thanks to @bnw
- #355 thanks to @roberthorlings
## [4.1.3] - 2016-10-22
### Fixed
- Some event handlers called the wrong method.
## [4.1.2] - 2016-10-22
### Fixed
- A bug is fixed in the journal event handler that prevented Firefly III from actually storing journals.
## [4.1.1] - 2016-10-22
### Added
- Option to show deposit accounts on the front page.
- Script to upgrade split transactions
- Can now save notes on piggy banks.
- Extend user admin options.
- Run import jobs from the command line
### Changed
- New preferences screen layout.
### Deprecated
- ``firefly:import`` is now ``firefly:start-import``
### Removed
- Lots of old code
### Fixed
- #357, where non utf-8 files would break Firefly.
- Tab delimiter is not properly loaded from import configuration (@roberthorlings)
- System response to yearly bills
## [4.0.2] - 2016-10-14
### Added
- Added ``intl`` dependency to composer file to ease installation (thanks @telyn)
- Added support for Croatian.
### Changed
- Updated all copyright notices to refer to the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/)
- Fixed #344
- Fixed #346, thanks to @SanderKleykens
- #351
- Did some internal remodelling.
### Fixed
- PostgreSQL compatibility thanks to @SanderKleykens
- @RobertHorlings fixed a bug in the ABN Amro import specific.
## [4.0.1] - 2016-10-04
### Added
- New ING import specific by @tomwerf
- New Presidents Choice specific to fix #307
- Added some trimming (#335)
### Changed
- Initial release.
### Deprecated
- Initial release.
### Removed
- Initial release.
### Fixed
- Fixed a bug where incoming transactions would not be properly filtered in several reports.
- #334 by @cyberkov
- #337
- #336
- #338 found by @roberthorlings
### Security
- Initial release.
## [4.0.0] - 2015-09-26
### Added ### Added
- Upgraded to Laravel 5.3, most other libraries upgraded as well. - Upgraded to Laravel 5.3, most other libraries upgraded as well.
- Added GBP as currency, thanks to @Mortalife - Added GBP as currency, thanks to @Mortalife

42
Dockerfile Normal file
View File

@@ -0,0 +1,42 @@
FROM php:7-apache
RUN apt-get update -y && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
zlib1g-dev \
libjpeg62-turbo-dev \
libpng12-dev \
libicu-dev \
libmcrypt-dev \
libedit-dev \
libtidy-dev \
libxml2-dev \
libsqlite3-dev \
libbz2-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
# Enable apache mod rewrite..
RUN a2enmod rewrite
# Setup the Composer installer
RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer && \
curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig && \
php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \
chmod +x /tmp/composer-setup.php && \
php /tmp/composer-setup.php && \
mv composer.phar /usr/local/bin/composer && \
rm -f /tmp/composer-setup.{php,sig}
ADD . /var/www/firefly-iii
RUN chown -R www-data:www-data /var/www/
ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
USER www-data
WORKDIR /var/www/firefly-iii
RUN composer install --no-scripts --no-dev
USER root

View File

@@ -3,8 +3,10 @@
* ConfigureLogging.php * ConfigureLogging.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -0,0 +1,152 @@
<?php
/**
* CreateImport.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 Artisan;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command;
use Log;
/**
* Class CreateImport
*
* @package FireflyIII\Console\Commands
*/
class CreateImport extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Use this command to create a new import. Your user ID can be found on the /profile page.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:create-import {file} {configuration} {--user=1} {--type=csv} {--start}';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// find the file
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');
$configuration = $this->argument('configuration');
$user = $userRepository->find(intval($this->option('user')));
$cwd = getcwd();
$type = strtolower($this->option('type'));
if (!$this->validArguments()) {
return;
}
// try to parse configuration data:
$configurationData = json_decode(file_get_contents($configuration));
if (is_null($configurationData)) {
$this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return;
}
$this->info(sprintf('Going to create a job to import file: %s', $file));
$this->info(sprintf('Using configuration file: %s', $configuration));
$this->info(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
$this->info(sprintf('Type of import: %s', $type));
/** @var ImportJobRepositoryInterface $jobRepository */
$jobRepository = app(ImportJobRepositoryInterface::class, [$user]);
$job = $jobRepository->create($type);
$this->line(sprintf('Created job "%s"...', $job->key));
// put the file in the proper place:
Artisan::call('firefly:encrypt', ['file' => $file, 'key' => $job->key]);
$this->line('Stored import data...');
// store the configuration in the job:
$job->configuration = $configurationData;
$job->status = 'settings_complete';
$job->save();
$this->line('Stored configuration...');
// if user wants to run it, do!
if ($this->option('start') === true) {
$this->line('The import will start in a moment. This process is not visible...');
Log::debug('Go for import!');
Artisan::call('firefly:start-import', ['key' => $job->key]);
$this->line('Done!');
}
return;
}
/**
* @return bool
*/
private function validArguments(): bool
{
// find the file
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = $this->argument('file');
$configuration = $this->argument('configuration');
$user = $userRepository->find(intval($this->option('user')));
$cwd = getcwd();
$validTypes = array_keys(config('firefly.import_formats'));
$type = strtolower($this->option('type'));
if (is_null($user->id)) {
$this->error(sprintf('There is no user with ID %d.', $this->option('user')));
return false;
}
if (!in_array($type, $validTypes)) {
$this->error(sprintf('Cannot import file of type "%s"', $type));
return false;
}
if (!file_exists($file)) {
$this->error(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
return false;
}
if (!file_exists($configuration)) {
$this->error(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;
}
return true;
}
}

View File

@@ -3,8 +3,10 @@
* EncryptFile.php * EncryptFile.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Import.php * Import.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -30,14 +32,14 @@ class Import extends Command
* *
* @var string * @var string
*/ */
protected $description = 'Import stuff into Firefly III.'; protected $description = 'This will start a new import.';
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
* @var string * @var string
*/ */
protected $signature = 'firefly:import {key}'; protected $signature = 'firefly:start-import {key}';
/** /**
* Create a new command instance. * Create a new command instance.
@@ -55,9 +57,12 @@ class Import extends Command
*/ */
public function handle() public function handle()
{ {
Log::debug('Start start-import command');
$jobKey = $this->argument('key'); $jobKey = $this->argument('key');
$job = ImportJob::whereKey($jobKey)->first(); $job = ImportJob::whereKey($jobKey)->first();
if (!$this->isValid($job)) { if (!$this->isValid($job)) {
Log::error('Job is not valid for some reason. Exit.');
return; return;
} }

View File

@@ -3,8 +3,10 @@
* ScanAttachments.php * ScanAttachments.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -0,0 +1,121 @@
<?php
/**
* UpgradeDatabase.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 DB;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Database\QueryException;
use Log;
/**
* Class UpgradeDatabase
*
* @package FireflyIII\Console\Commands
*/
class UpgradeDatabase extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Will run various commands to update database records.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:upgrade-database';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->setTransactionIdentifier();
}
/**
* This is strangely complex, because the HAVING modifier is a no-no. And subqueries in Laravel are weird.
*/
private function setTransactionIdentifier()
{
$subQuery = TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id'])
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
$result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived'))
->mergeBindings($subQuery->getQuery())
->where('t_count', '>', 2)
->select(['id', 't_count']);
$journalIds = array_unique($result->pluck('id')->toArray());
foreach ($journalIds as $journalId) {
// grab all positive transactiosn from this journal that are not deleted.
// for each one, grab the negative opposing one which has 0 as an identifier and give it the same identifier.
$identifier = 0;
$processed = [];
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
// find opposing:
$amount = bcmul(strval($transaction->amount), '-1');
try {
/** @var Transaction $opposing */
$opposing = Transaction
::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed)
->first();
} 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('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;
}
if (!is_null($opposing)) {
// give both a new identifier:
$transaction->identifier = $identifier;
$transaction->save();
$opposing->identifier = $identifier;
$opposing->save();
$processed[] = $transaction->id;
$processed[] = $opposing->id;
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
}
$identifier++;
}
}
}
}

View File

@@ -3,8 +3,10 @@
* UpgradeFireflyInstructions.php * UpgradeFireflyInstructions.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* VerifyDatabase.php * VerifyDatabase.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -13,6 +15,7 @@ namespace FireflyIII\Console\Commands;
use Crypt; use Crypt;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
@@ -82,6 +85,9 @@ class VerifyDatabase extends Command
// transfers with budgets. // transfers with budgets.
$this->reportTransfersBudgets(); $this->reportTransfersBudgets();
// report on journals with the wrong types of accounts.
$this->reportIncorrectJournals();
} }
/** /**
@@ -195,11 +201,50 @@ class VerifyDatabase extends Command
$date = is_null($entry->transaction_deleted_at) ? $entry->journal_deleted_at : $entry->transaction_deleted_at; $date = is_null($entry->transaction_deleted_at) ? $entry->journal_deleted_at : $entry->transaction_deleted_at;
$this->error( $this->error(
'Error: Account #' . $entry->account_id . ' should have been deleted, but has not.' . 'Error: Account #' . $entry->account_id . ' should have been deleted, but has not.' .
' Find it in the table called `accounts` and change the `deleted_at` field to: "' . $date . '"' ' Find it in the table called "accounts" and change the "deleted_at" field to: "' . $date . '"'
); );
} }
} }
private function reportIncorrectJournals()
{
$configuration = [
// a withdrawal can not have revenue account:
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
// deposit cannot have an expense account:
TransactionType::DEPOSIT => [AccountType::EXPENSE],
// transfer cannot have either:
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
];
foreach ($configuration as $transactionType => $accountTypes) {
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
->leftJoin('users', 'users.id', '=', 'transaction_journals.user_id')
->where('transaction_types.type', $transactionType)
->whereIn('account_types.type', $accountTypes)
->whereNull('transaction_journals.deleted_at')
->get(['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type', 'transaction_types.type']);
foreach ($set as $entry) {
$this->error(
sprintf(
'Transaction journal #%d (user #%d, %s) is of type "%s" but ' .
'is linked to a "%s". The transaction journal should be recreated.',
$entry->id,
$entry->user_id,
$entry->email,
$entry->type,
$entry->a_type
)
);
}
}
}
/** /**
* Any deleted transaction journals that have transactions that are NOT deleted: * Any deleted transaction journals that have transactions that are NOT deleted:
*/ */
@@ -222,7 +267,7 @@ class VerifyDatabase extends Command
foreach ($set as $entry) { foreach ($set as $entry) {
$this->error( $this->error(
'Error: Transaction #' . $entry->transaction_id . ' should have been deleted, but has not.' . 'Error: Transaction #' . $entry->transaction_id . ' should have been deleted, but has not.' .
' Find it in the table called `transactions` and change the `deleted_at` field to: "' . $entry->journal_deleted . '"' ' Find it in the table called "transactions" and change the "deleted_at" field to: "' . $entry->journal_deleted . '"'
); );
} }
} }
@@ -240,7 +285,7 @@ class VerifyDatabase extends Command
foreach ($set as $entry) { foreach ($set as $entry) {
$this->error( $this->error(
'Error: Journal #' . $entry->id . ' has zero transactions. Open table `transaction_journals` and delete the entry with id #' . $entry->id 'Error: Journal #' . $entry->id . ' has zero transactions. Open table "transaction_journals" and delete the entry with id #' . $entry->id
); );
} }
@@ -301,7 +346,7 @@ class VerifyDatabase extends Command
foreach ($set as $entry) { foreach ($set as $entry) {
$this->error( $this->error(
'Error: Transaction journal #' . $entry->journal_id . ' should have been deleted, but has not.' . 'Error: Transaction journal #' . $entry->journal_id . ' should have been deleted, but has not.' .
' Find it in the table called `transaction_journals` and change the `deleted_at` field to: "' . $entry->transaction_deleted . '"' ' Find it in the table called "transaction_journals" and change the "deleted_at" field to: "' . $entry->transaction_deleted . '"'
); );
} }
} }

View File

@@ -3,17 +3,21 @@
* Kernel.php * Kernel.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Console; namespace FireflyIII\Console;
use FireflyIII\Console\Commands\CreateImport;
use FireflyIII\Console\Commands\EncryptFile; use FireflyIII\Console\Commands\EncryptFile;
use FireflyIII\Console\Commands\Import; use FireflyIII\Console\Commands\Import;
use FireflyIII\Console\Commands\ScanAttachments; use FireflyIII\Console\Commands\ScanAttachments;
use FireflyIII\Console\Commands\UpgradeDatabase;
use FireflyIII\Console\Commands\UpgradeFireflyInstructions; use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
use FireflyIII\Console\Commands\VerifyDatabase; use FireflyIII\Console\Commands\VerifyDatabase;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;
@@ -55,8 +59,10 @@ class Kernel extends ConsoleKernel
UpgradeFireflyInstructions::class, UpgradeFireflyInstructions::class,
VerifyDatabase::class, VerifyDatabase::class,
Import::class, Import::class,
CreateImport::class,
EncryptFile::class, EncryptFile::class,
ScanAttachments::class, ScanAttachments::class,
UpgradeDatabase::class,
]; ];

View File

@@ -1,534 +0,0 @@
<?php
/**
* AccountCrud.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Crud\Account;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
use Log;
/**
* Class AccountCrud
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
*
* @package FireflyIII\Crud\Account
*/
class AccountCrud implements AccountCrudInterface
{
/** @var User */
private $user;
/** @var array */
private $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType', 'accountNumber'];
/**
* AccountCrud constructor.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* @param Account $account
* @param Account $moveTo
*
* @return bool
*/
public function destroy(Account $account, Account $moveTo): bool
{
if (!is_null($moveTo->id)) {
DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]);
}
if (!is_null($account)) {
$account->delete();
}
return true;
}
/**
* @param $accountId
*
* @return Account
*/
public function find(int $accountId): Account
{
$account = $this->user->accounts()->find($accountId);
if (is_null($account)) {
return new Account;
}
return $account;
}
/**
* @param string $number
* @param array $types
*
* @return Account
*/
public function findByAccountNumber(string $number, array $types): Account
{
$query = $this->user->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', 'accountNumber')
->where('account_meta.data', json_encode($number));
if (count($types) > 0) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
/** @var Collection $accounts */
$accounts = $query->get(['accounts.*']);
if ($accounts->count() > 0) {
return $accounts->first();
}
return new Account;
}
/**
* @param string $iban
* @param array $types
*
* @return Account
*/
public function findByIban(string $iban, array $types): Account
{
$query = $this->user->accounts()->where('iban', '!=', '');
if (count($types) > 0) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
$accounts = $query->get(['accounts.*']);
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->iban === $iban) {
return $account;
}
}
return new Account;
}
/**
* @param string $name
* @param array $types
*
* @return Account
*/
public function findByName(string $name, array $types): Account
{
$query = $this->user->accounts();
if (count($types) > 0) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
$query->whereIn('account_types.type', $types);
}
Log::debug(sprintf('Searching for account named %s of the following type(s)', $name), ['types' => $types]);
$accounts = $query->get(['accounts.*']);
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->name === $name) {
Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id));
return $account;
}
}
Log::debug('Found nothing.');
return new Account;
}
/**
* @param array $accountIds
*
* @return Collection
*/
public function getAccountsById(array $accountIds): Collection
{
/** @var Collection $result */
$query = $this->user->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
);
if (count($accountIds) > 0) {
$query->whereIn('accounts.id', $accountIds);
}
$result = $query->get(['accounts.*']);
$result = $result->sortBy(
function (Account $account) {
return strtolower($account->name);
}
);
return $result;
}
/**
* @param array $types
*
* @return Collection
*/
public function getAccountsByType(array $types): Collection
{
/** @var Collection $result */
$query = $this->user->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
);
if (count($types) > 0) {
$query->accountTypeIn($types);
}
$result = $query->get(['accounts.*']);
$result = $result->sortBy(
function (Account $account) {
return strtolower($account->name);
}
);
return $result;
}
/**
* @param array $data
*
* @return Account
*/
public function store(array $data): Account
{
$newAccount = $this->storeAccount($data);
if (!is_null($newAccount->id)) {
$this->storeMetadata($newAccount, $data);
}
if ($data['openingBalance'] != 0) {
$this->storeInitialBalance($newAccount, $data);
}
return $newAccount;
}
/**
* @param $account
* @param $name
* @param $value
*
* @return AccountMeta
*/
public function storeMeta(Account $account, string $name, $value): AccountMeta
{
return AccountMeta::create(['name' => $name, 'data' => $value, 'account_id' => $account->id,]);
}
/**
* @param Account $account
* @param array $data
*
* @return Account
*/
public function update(Account $account, array $data): Account
{
// update the account:
$account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false;
$account->virtual_balance = $data['virtualBalance'];
$account->iban = $data['iban'];
$account->save();
$this->updateMetadata($account, $data);
$this->updateInitialBalance($account, $data);
return $account;
}
/**
* @param Account $account
* @param string $type
*
* @return Account
*/
public function updateAccountType(Account $account, string $type): Account
{
$type = AccountType::whereType($type)->first();
if (!is_null($type)) {
$account->accountType()->associate($type);
$account->save();
}
return $this->find($account->id);
}
/**
* @param array $data
*
* @return Account
*/
protected function storeAccount(array $data): Account
{
$type = config('firefly.accountTypeByIdentifier.' . $data['accountType']);
$accountType = AccountType::whereType($type)->first();
$newAccount = new Account(
[
'user_id' => $data['user'],
'account_type_id' => $accountType->id,
'name' => $data['name'],
'virtual_balance' => $data['virtualBalance'],
'active' => $data['active'] === true ? true : false,
'iban' => $data['iban'],
]
);
if (!$newAccount->isValid()) {
// does the account already exist?
$searchData = [
'user_id' => $data['user'],
'account_type_id' => $accountType->id,
'virtual_balance' => $data['virtualBalance'],
'name' => $data['name'],
'iban' => $data['iban'],
];
$existingAccount = Account::firstOrNullEncrypted($searchData);
if (!$existingAccount) {
Log::error('Account create error', $newAccount->getErrors()->toArray());
return new Account;
}
$newAccount = $existingAccount;
}
$newAccount->save();
return $newAccount;
}
/**
* @param Account $account
* @param array $data
*
* @return TransactionJournal
*/
protected function storeInitialBalance(Account $account, array $data): TransactionJournal
{
$amount = $data['openingBalance'];
$user = $data['user'];
$name = $data['name'];
$opposing = $this->storeOpposingAccount($amount, $user, $name);
$transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
$journal = TransactionJournal::create(
[
'user_id' => $data['user'],
'transaction_type_id' => $transactionType->id,
'transaction_currency_id' => $data['openingBalanceCurrency'],
'description' => 'Initial balance for "' . $account->name . '"',
'completed' => true,
'date' => $data['openingBalanceDate'],
'encrypted' => true,
]
);
$firstAccount = $account;
$secondAccount = $opposing;
$firstAmount = $amount;
$secondAmount = $amount * -1;
if ($data['openingBalance'] < 0) {
$firstAccount = $opposing;
$secondAccount = $account;
$firstAmount = $amount * -1;
$secondAmount = $amount;
}
$one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]);
$one->save();// first transaction: from
$two = new Transaction(['account_id' => $secondAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $secondAmount]);
$two->save(); // second transaction: to
return $journal;
}
/**
* @param Account $account
* @param array $data
*/
protected function storeMetadata(Account $account, array $data)
{
foreach ($this->validFields as $field) {
if (isset($data[$field])) {
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => $field,
'data' => $data[$field],
]
);
$metaData->save();
}
}
}
/**
* @param Account $account
* @param array $data
*
* @return bool
*/
protected function updateInitialBalance(Account $account, array $data): bool
{
$openingBalance = $this->openingBalanceTransaction($account);
if ($data['openingBalance'] != 0) {
if (!is_null($openingBalance->id)) {
$date = $data['openingBalanceDate'];
$amount = $data['openingBalance'];
return $this->updateJournal($account, $openingBalance, $date, $amount);
}
$this->storeInitialBalance($account, $data);
return true;
}
// else, delete it:
if ($openingBalance) { // opening balance is zero, should we delete it?
$openingBalance->delete(); // delete existing opening balance.
}
return true;
}
/**
* @param Account $account
* @param array $data
*
*/
protected function updateMetadata(Account $account, array $data)
{
foreach ($this->validFields as $field) {
$entry = $account->accountMeta()->where('name', $field)->first();
if (isset($data[$field])) {
// update if new data is present:
if (!is_null($entry)) {
$entry->data = $data[$field];
$entry->save();
continue;
}
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => $field,
'data' => $data[$field],
]
);
$metaData->save();
}
}
}
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
private function openingBalanceTransaction(Account $account): TransactionJournal
{
$journal = TransactionJournal
::sortCorrectly()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)
->transactionTypes([TransactionType::OPENING_BALANCE])
->first(['transaction_journals.*']);
if (is_null($journal)) {
return new TransactionJournal;
}
return $journal;
}
/**
* @param float $amount
* @param int $user
* @param string $name
*
* @return Account
*/
private function storeOpposingAccount(float $amount, int $user, string $name):Account
{
$type = $amount < 0 ? 'expense' : 'revenue';
$opposingData = [
'user' => $user,
'accountType' => $type,
'name' => $name . ' initial balance',
'active' => false,
'iban' => '',
'virtualBalance' => 0,
];
return $this->storeAccount($opposingData);
}
/**
* @param Account $account
* @param TransactionJournal $journal
* @param Carbon $date
* @param float $amount
*
* @return bool
*/
private function updateJournal(Account $account, TransactionJournal $journal, Carbon $date, float $amount): bool
{
// update date:
$journal->date = $date;
$journal->save();
// update transactions:
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($account->id == $transaction->account_id) {
$transaction->amount = $amount;
$transaction->save();
}
if ($account->id != $transaction->account_id) {
$transaction->amount = $amount * -1;
$transaction->save();
}
}
return true;
}
}

View File

@@ -1,109 +0,0 @@
<?php
/**
* AccountCrudInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Crud\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use Illuminate\Support\Collection;
/**
* Interface AccountCrudInterface
*
* @package FireflyIII\Crud\Account
*/
interface AccountCrudInterface
{
/**
* @param Account $account
* @param Account $moveTo
*
* @return bool
*/
public function destroy(Account $account, Account $moveTo): bool;
/**
* @param int $accountId
*
* @return Account
*/
public function find(int $accountId): Account;
/**
* @param string $number
* @param array $types
*
* @return Account
*/
public function findByAccountNumber(string $number, array $types): Account;
/**
* @param string $iban
* @param array $types
*
* @return Account
*/
public function findByIban(string $iban, array $types): Account;
/**
* @param string $name
* @param array $types
*
* @return Account
*/
public function findByName(string $name, array $types): Account;
/**
* @param array $accountIds
*
* @return Collection
*/
public function getAccountsById(array $accountIds): Collection;
/**
* @param array $types
*
* @return Collection
*/
public function getAccountsByType(array $types): Collection;
/**
* @param array $data
*
* @return Account
*/
public function store(array $data) : Account;
/**
* @param $account
* @param $name
* @param $value
*
* @return AccountMeta
*/
public function storeMeta(Account $account, string $name, $value): AccountMeta;
/**
* @param Account $account
* @param array $data
*
* @return Account
*/
public function update(Account $account, array $data): Account;
/**
* @param Account $account
* @param string $type
*
* @return Account
*/
public function updateAccountType(Account $account, string $type): Account;
}

View File

@@ -1,216 +0,0 @@
<?php
/**
* Journal.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Crud\Split;
use FireflyIII\Events\TransactionStored;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Class Journal
*
* @package FireflyIII\Crud\Split
*/
class Journal implements JournalInterface
{
/** @var User */
private $user;
/**
* AttachmentRepository constructor.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* @param $journal
*
* @return bool
*/
public function markAsComplete(TransactionJournal $journal)
{
$journal->completed = 1;
$journal->save();
return true;
}
/**
* @param TransactionJournal $journal
* @param array $transaction
*
* @return Collection
*/
public function storeTransaction(TransactionJournal $journal, array $transaction): Collection
{
// store accounts (depends on type)
list($sourceAccount, $destinationAccount) = $this->storeAccounts($journal->transactionType->type, $transaction);
// store transaction one way:
/** @var Transaction $one */
$one = Transaction::create(
['account_id' => $sourceAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $transaction['amount'] * -1,
'description' => $transaction['description']]
);
$two = Transaction::create(
['account_id' => $destinationAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $transaction['amount'],
'description' => $transaction['description']]
);
if (strlen($transaction['category']) > 0) {
$category = Category::firstOrCreateEncrypted(['name' => $transaction['category'], 'user_id' => $journal->user_id]);
$one->categories()->save($category);
$two->categories()->save($category);
}
if (intval($transaction['budget_id']) > 0) {
$budget = Budget::find($transaction['budget_id']);
$one->budgets()->save($budget);
$two->budgets()->save($budget);
}
if ($transaction['piggy_bank_id'] > 0) {
$transaction['date'] = $journal->date->format('Y-m-d');
event(new TransactionStored($transaction));
}
return new Collection([$one, $two]);
}
/**
* @param TransactionJournal $journal
* @param array $data
*
* @return TransactionJournal
*/
public function updateJournal(TransactionJournal $journal, array $data): TransactionJournal
{
$journal->description = $data['journal_description'];
$journal->transaction_currency_id = $data['journal_currency_id'];
$journal->date = $data['date'];
$journal->interest_date = $data['interest_date'];
$journal->book_date = $data['book_date'];
$journal->process_date = $data['process_date'];
$journal->save();
// delete original transactions, and recreate them.
$journal->transactions()->delete();
foreach ($data['transactions'] as $transaction) {
$this->storeTransaction($journal, $transaction);
}
$journal->completed = true;
$journal->save();
return $journal;
}
/**
* @param string $type
* @param array $transaction
*
* @return array
* @throws FireflyException
*/
private function storeAccounts(string $type, array $transaction): array
{
$sourceAccount = null;
$destinationAccount = null;
switch ($type) {
case TransactionType::WITHDRAWAL:
list($sourceAccount, $destinationAccount) = $this->storeWithdrawalAccounts($transaction);
break;
case TransactionType::DEPOSIT:
list($sourceAccount, $destinationAccount) = $this->storeDepositAccounts($transaction);
break;
case TransactionType::TRANSFER:
$sourceAccount = Account::where('user_id', $this->user->id)->where('id', $transaction['source_account_id'])->first();
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $transaction['destination_account_id'])->first();
break;
default:
throw new FireflyException('Cannot handle ' . e($type));
}
return [$sourceAccount, $destinationAccount];
}
/**
* @param array $data
*
* @return array
*/
private function storeDepositAccounts(array $data): array
{
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
if (isset($data['source_account_name']) && strlen($data['source_account_name']) > 0) {
$sourceType = AccountType::where('type', 'Revenue account')->first();
$sourceAccount = Account::firstOrCreateEncrypted(
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
);
return [$sourceAccount, $destinationAccount];
}
$sourceType = AccountType::where('type', 'Cash account')->first();
$sourceAccount = Account::firstOrCreateEncrypted(
['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
);
return [$sourceAccount, $destinationAccount];
}
/**
* @param array $data
*
* @return array
*/
private function storeWithdrawalAccounts(array $data): array
{
$sourceAccount = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first(['accounts.*']);
if (strlen($data['destination_account_name']) > 0) {
$destinationType = AccountType::where('type', 'Expense account')->first();
$destinationAccount = Account::firstOrCreateEncrypted(
[
'user_id' => $this->user->id,
'account_type_id' => $destinationType->id,
'name' => $data['destination_account_name'],
'active' => 1,
]
);
return [$sourceAccount, $destinationAccount];
}
$destinationType = AccountType::where('type', 'Cash account')->first();
$destinationAccount = Account::firstOrCreateEncrypted(
['user_id' => $this->user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
);
return [$sourceAccount, $destinationAccount];
}
}

View File

@@ -1,47 +0,0 @@
<?php
/**
* JournalInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Crud\Split;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Interface JournalInterface
*
* @package FireflyIII\Crud\Split
*/
interface JournalInterface
{
/**
* @param $journal
*
* @return bool
*/
public function markAsComplete(TransactionJournal $journal);
/**
* @param TransactionJournal $journal
* @param array $transaction
*
* @return Collection
*/
public function storeTransaction(TransactionJournal $journal, array $transaction): Collection;
/**
* @param TransactionJournal $journal
* @param array $data
*
* @return TransactionJournal
*/
public function updateJournal(TransactionJournal $journal, array $data): TransactionJournal;
}

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* UserRegistration.php * ConfirmedUser.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -15,11 +17,11 @@ use FireflyIII\User;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class UserRegistration * Class ConfirmedUser
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class UserRegistration extends Event class ConfirmedUser extends Event
{ {
use SerializesModels; use SerializesModels;
@@ -27,7 +29,7 @@ class UserRegistration extends Event
public $user; public $user;
/** /**
* Create a new event instance. * Create a new event instance. This event is triggered when a user confirms their new account.
* *
* @param User $user * @param User $user
* @param string $ipAddress * @param string $ipAddress

View File

@@ -3,8 +3,10 @@
* Event.php * Event.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* UserIsDeleted.php * RegisteredUser.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -15,11 +17,11 @@ use FireflyIII\User;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class UserIsDeleted * Class RegisteredUser
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class UserIsDeleted extends Event class RegisteredUser extends Event
{ {
use SerializesModels; use SerializesModels;
@@ -27,7 +29,7 @@ class UserIsDeleted extends Event
public $user; public $user;
/** /**
* Create a new event instance. * Create a new event instance. This event is triggered when a new user registers.
* *
* @param User $user * @param User $user
* @param string $ipAddress * @param string $ipAddress

View File

@@ -1,40 +0,0 @@
<?php
/**
* ResendConfirmation.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class ResendConfirmation
*
* @package FireflyIII\Events
*/
class ResendConfirmation extends Event
{
use SerializesModels;
public $ipAddress;
public $user;
/**
* Create a new event instance.
*
* @param User $user
* @param string $ipAddress
*/
public function __construct(User $user, string $ipAddress)
{
$this->user = $user;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* UserIsConfirmed.php * ResentConfirmation.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -15,11 +17,11 @@ use FireflyIII\User;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class UserIsConfirmed * Class ResentConfirmation
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class UserIsConfirmed extends Event class ResentConfirmation extends Event
{ {
use SerializesModels; use SerializesModels;
@@ -27,7 +29,7 @@ class UserIsConfirmed extends Event
public $user; public $user;
/** /**
* Create a new event instance. * Create a new event instance. This event is triggered when a users wants a new confirmation.
* *
* @param User $user * @param User $user
* @param string $ipAddress * @param string $ipAddress

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* BudgetLimitStored.php * StoredBudgetLimit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -16,11 +18,11 @@ use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class BudgetLimitStored * Class StoredBudgetLimit
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class BudgetLimitStored extends Event class StoredBudgetLimit extends Event
{ {
use SerializesModels; use SerializesModels;

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* TransactionJournalStored.php * StoredTransactionJournal.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -15,11 +17,11 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class TransactionJournalStored * Class StoredTransactionJournal
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class TransactionJournalStored extends Event class StoredTransactionJournal extends Event
{ {
use SerializesModels; use SerializesModels;

View File

@@ -1,39 +0,0 @@
<?php
/**
* TransactionStored.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use Illuminate\Queue\SerializesModels;
/**
* Class TransactionJournalStored
*
* @package FireflyIII\Events
*/
class TransactionStored extends Event
{
use SerializesModels;
public $transaction = [];
/**
* Create a new event instance.
*
* @param array $transaction
*/
public function __construct(array $transaction)
{
//
$this->transaction = $transaction;
}
}

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* BudgetLimitUpdated.php * UpdatedBudgetLimit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -16,11 +18,11 @@ use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class BudgetLimitUpdated * Class UpdatedBudgetLimit
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class BudgetLimitUpdated extends Event class UpdatedBudgetLimit extends Event
{ {
use SerializesModels; use SerializesModels;

View File

@@ -1,10 +1,12 @@
<?php <?php
/** /**
* TransactionJournalUpdated.php * UpdatedTransactionJournal.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -15,11 +17,11 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
* Class TransactionJournalUpdated * Class UpdatedTransactionJournal
* *
* @package FireflyIII\Events * @package FireflyIII\Events
*/ */
class TransactionJournalUpdated extends Event class UpdatedTransactionJournal extends Event
{ {
use SerializesModels; use SerializesModels;

View File

@@ -3,8 +3,10 @@
* FireflyException.php * FireflyException.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Handler.php * Handler.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -106,7 +108,8 @@ class Handler extends ExceptionHandler
/** /**
* Convert an authentication exception into an unauthenticated response. * Convert an authentication exception into an unauthenticated response.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
protected function unauthenticated($request) protected function unauthenticated($request)

View File

@@ -3,8 +3,10 @@
* NotImplementedException.php * NotImplementedException.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ValidationException.php * ValidationException.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,19 +3,20 @@
* AttachmentCollector.php * AttachmentCollector.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Export\Collector; namespace FireflyIII\Export\Collector;
use Amount; use Carbon\Carbon;
use Crypt; use Crypt;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -29,12 +30,14 @@ use Storage;
*/ */
class AttachmentCollector extends BasicCollector implements CollectorInterface class AttachmentCollector extends BasicCollector implements CollectorInterface
{ {
/** @var string */ /** @var Carbon */
private $explanationString = ''; private $end;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */ /** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk; private $exportDisk;
/** @var AttachmentRepositoryInterface */ /** @var AttachmentRepositoryInterface */
private $repository; private $repository;
/** @var Carbon */
private $start;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */ /** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk; private $uploadDisk;
@@ -67,34 +70,17 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$this->exportAttachment($attachment); $this->exportAttachment($attachment);
} }
// put the explanation string in a file and attach it as well.
$file = $this->job->key . '-Source of all your attachments explained.txt';
$this->exportDisk->put($file, $this->explanationString);
$this->getFiles()->push($file);
return true; return true;
} }
/** /**
* @param Attachment $attachment * @param Carbon $start
* @param Carbon $end
*/ */
private function explain(Attachment $attachment) public function setDates(Carbon $start, Carbon $end)
{ {
/** @var TransactionJournal $journal */ $this->start = $start;
$journal = $attachment->attachable; $this->end = $end;
$args = [
'attachment_name' => e($attachment->filename),
'attachment_id' => $attachment->id,
'type' => strtolower($journal->transactionType->type),
'description' => e($journal->description),
'journal_id' => $journal->id,
'date' => $journal->date->formatLocalized(strval(trans('config.month_and_day'))),
'amount' => Amount::formatJournal($journal, false),
];
$string = trans('firefly.attachment_explanation', $args) . "\n";
Log::debug('Appended explanation string', ['string' => $string]);
$this->explanationString .= $string;
} }
/** /**
@@ -110,10 +96,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$decrypted = Crypt::decrypt($this->uploadDisk->get($file)); $decrypted = Crypt::decrypt($this->uploadDisk->get($file));
$exportFile = $this->exportFileName($attachment); $exportFile = $this->exportFileName($attachment);
$this->exportDisk->put($exportFile, $decrypted); $this->exportDisk->put($exportFile, $decrypted);
$this->getFiles()->push($exportFile); $this->getEntries()->push($exportFile);
// explain:
$this->explain($attachment);
} catch (DecryptException $e) { } catch (DecryptException $e) {
Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage()); Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage());
} }
@@ -141,7 +125,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
*/ */
private function getAttachments(): Collection private function getAttachments(): Collection
{ {
$attachments = $this->repository->get(); $attachments = $this->repository->getBetween($this->start, $this->end);
return $attachments; return $attachments;
} }

View File

@@ -3,8 +3,10 @@
* BasicCollector.php * BasicCollector.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -25,7 +27,7 @@ class BasicCollector
/** @var ExportJob */ /** @var ExportJob */
protected $job; protected $job;
/** @var Collection */ /** @var Collection */
private $files; private $entries;
/** /**
* BasicCollector constructor. * BasicCollector constructor.
@@ -34,24 +36,24 @@ class BasicCollector
*/ */
public function __construct(ExportJob $job) public function __construct(ExportJob $job)
{ {
$this->files = new Collection; $this->entries = new Collection;
$this->job = $job; $this->job = $job;
} }
/** /**
* @return Collection * @return Collection
*/ */
public function getFiles(): Collection public function getEntries(): Collection
{ {
return $this->files; return $this->entries;
} }
/** /**
* @param Collection $files * @param Collection $entries
*/ */
public function setFiles(Collection $files) public function setEntries(Collection $entries)
{ {
$this->files = $files; $this->entries = $entries;
} }

View File

@@ -3,8 +3,10 @@
* CollectorInterface.php * CollectorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -23,7 +25,7 @@ interface CollectorInterface
/** /**
* @return Collection * @return Collection
*/ */
public function getFiles(): Collection; public function getEntries(): Collection;
/** /**
* @return bool * @return bool
@@ -31,9 +33,9 @@ interface CollectorInterface
public function run(): bool; public function run(): bool;
/** /**
* @param Collection $files * @param Collection $entries
* *
*/ */
public function setFiles(Collection $files); public function setEntries(Collection $entries);
} }

View File

@@ -0,0 +1,348 @@
<?php
/**
* JournalCollector.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\Export\Collector;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Models\Transaction;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class JournalCollector
*
* @package FireflyIII\Export\Collector
*/
class JournalCollector extends BasicCollector implements CollectorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/** @var Collection */
private $workSet;
/**
* @return bool
*/
public function run(): bool
{
/*
* Instead of collecting journals we collect transactions for the given accounts.
* We left join the OPPOSING transaction AND some journal data.
* After that we complement this info with budgets, categories, etc.
*
* This is way more efficient and will also work on split journals.
*/
$this->getWorkSet();
/*
* Extract:
* possible budget ids for journals
* possible category ids journals
* possible budget ids for transactions
* possible category ids for transactions
*
* possible IBAN and account numbers?
*
*/
$journals = $this->extractJournalIds();
$transactions = $this->extractTransactionIds();
// extend work set with category data from journals:
$this->categoryDataForJournals($journals);
// extend work set with category cate from transactions (overrules journals):
$this->categoryDataForTransactions($transactions);
// same for budgets:
$this->budgetDataForJournals($journals);
$this->budgetDataForTransactions($transactions);
$this->setEntries($this->workSet);
return true;
}
/**
* @param Collection $accounts
*/
public function setAccounts(Collection $accounts)
{
$this->accounts = $accounts;
}
/**
* @param Carbon $start
* @param Carbon $end
*/
public function setDates(Carbon $start, Carbon $end)
{
$this->start = $start;
$this->end = $end;
}
/**
* @param array $journals
*
* @return bool
*/
private function budgetDataForJournals(array $journals): bool
{
$set = DB::table('budget_transaction_journal')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->whereIn('budget_transaction_journal.transaction_journal_id', $journals)
->get(
[
'budget_transaction_journal.budget_id',
'budget_transaction_journal.transaction_journal_id',
'budgets.name',
'budgets.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_journal_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
if (isset($array[$obj->transaction_journal_id])) {
$obj->budget_id = $array[$obj->transaction_journal_id]['id'];
$obj->budget_name = $array[$obj->transaction_journal_id]['name'];
}
}
);
return true;
}
/**
* @param array $transactions
*
* @return bool
*/
private function budgetDataForTransactions(array $transactions): bool
{
$set = DB::table('budget_transaction')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction.budget_id')
->whereIn('budget_transaction.transaction_id', $transactions)
->get(
[
'budget_transaction.budget_id',
'budget_transaction.transaction_id',
'budgets.name',
'budgets.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
// first transaction
if (isset($array[$obj->id])) {
$obj->budget_id = $array[$obj->id]['id'];
$obj->budget_name = $array[$obj->id]['name'];
}
}
);
return true;
}
/**
* @param array $journals
*
* @return bool
*/
private function categoryDataForJournals(array $journals): bool
{
$set = DB::table('category_transaction_journal')
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->whereIn('category_transaction_journal.transaction_journal_id', $journals)
->get(
[
'category_transaction_journal.category_id',
'category_transaction_journal.transaction_journal_id',
'categories.name',
'categories.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_journal_id] = ['id' => $obj->category_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
if (isset($array[$obj->transaction_journal_id])) {
$obj->category_id = $array[$obj->transaction_journal_id]['id'];
$obj->category_name = $array[$obj->transaction_journal_id]['name'];
}
}
);
return true;
}
/**
* @param array $transactions
*
* @return bool
*/
private function categoryDataForTransactions(array $transactions): bool
{
$set = DB::table('category_transaction')
->leftJoin('categories', 'categories.id', '=', 'category_transaction.category_id')
->whereIn('category_transaction.transaction_id', $transactions)
->get(
[
'category_transaction.category_id',
'category_transaction.transaction_id',
'categories.name',
'categories.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_id] = ['id' => $obj->category_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
// first transaction
if (isset($array[$obj->id])) {
$obj->category_id = $array[$obj->id]['id'];
$obj->category_name = $array[$obj->id]['name'];
}
}
);
return true;
}
/**
* @return array
*/
private function extractJournalIds(): array
{
return $this->workSet->pluck('transaction_journal_id')->toArray();
}
/**
* @return array
*/
private function extractTransactionIds()
{
$set = $this->workSet->pluck('id')->toArray();
$opposing = $this->workSet->pluck('opposing_id')->toArray();
$complete = $set + $opposing;
return array_unique($complete);
}
/**
*
*/
private function getWorkSet()
{
$accountIds = $this->accounts->pluck('id')->toArray();
$this->workSet = Transaction
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'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');
}
)
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
->whereIn('transactions.account_id', $accountIds)
->where('transaction_journals.user_id', $this->job->user_id)
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->end->format('Y-m-d'))
->where('transaction_journals.completed', 1)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->whereNull('opposing.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transactions.identifier', 'ASC')
->get(
[
'transactions.id',
'transactions.amount',
'transactions.description',
'transactions.account_id',
'accounts.name as account_name',
'accounts.encrypted as account_name_encrypted',
'transactions.identifier',
'opposing.id as opposing_id',
'opposing.amount AS opposing_amount',
'opposing.description as opposing_description',
'opposing.account_id as opposing_account_id',
'opposing_accounts.name as opposing_account_name',
'opposing_accounts.encrypted as opposing_account_encrypted',
'opposing.identifier as opposing_identifier',
'transaction_journals.id as transaction_journal_id',
'transaction_journals.date',
'transaction_journals.description as journal_description',
'transaction_journals.encrypted as journal_encrypted',
'transaction_journals.transaction_type_id',
'transaction_types.type as transaction_type',
'transaction_journals.transaction_currency_id',
'transaction_currencies.code AS transaction_currency_code',
]
);
}
}

View File

@@ -3,8 +3,10 @@
* UploadCollector.php * UploadCollector.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -24,16 +26,14 @@ use Storage;
*/ */
class UploadCollector extends BasicCollector implements CollectorInterface class UploadCollector extends BasicCollector implements CollectorInterface
{ {
/** @var string */
private $expected;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */ /** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk; private $exportDisk;
private $importKeys = [];
/** @var \Illuminate\Contracts\Filesystem\Filesystem */ /** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk; private $uploadDisk;
/** @var string */
private $vintageFormat;
/** /**
*
* AttachmentCollector constructor. * AttachmentCollector constructor.
* *
* @param ExportJob $job * @param ExportJob $job
@@ -49,50 +49,74 @@ class UploadCollector extends BasicCollector implements CollectorInterface
$this->exportDisk = Storage::disk('export'); $this->exportDisk = Storage::disk('export');
// file names associated with the old import routine. // file names associated with the old import routine.
$this->expected = 'csv-upload-' . auth()->user()->id . '-'; $this->vintageFormat = sprintf('csv-upload-%d-', auth()->user()->id);
// for the new import routine:
$this->getImportKeys();
} }
/** /**
* Is called from the outside to actually start the export.
*
* @return bool * @return bool
*/ */
public function run(): bool public function run(): bool
{ {
// grab upload directory. // collect old upload files (names beginning with "csv-upload".
$files = $this->uploadDisk->files(); $this->collectVintageUploads();
foreach ($files as $entry) { // then collect current upload files:
$this->processUpload($entry); $this->collectModernUploads();
return true;
}
/**
* This method collects all the uploads that are uploaded using the new importer. So after the summer of 2016.
*
* @return bool
*/
private function collectModernUploads(): bool
{
$set = $this->job->user->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
$keys = [];
if ($set->count() > 0) {
$keys = $set->pluck('key')->toArray();
}
foreach ($keys as $key) {
$this->processModernUpload($key);
} }
return true; return true;
} }
/** /**
* This method collects all the uploads that are uploaded using the "old" importer. So from before the summer of 2016.
* *
* @return bool
*/ */
private function getImportKeys() private function collectVintageUploads():bool
{ {
$set = auth()->user()->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']); // grab upload directory.
if ($set->count() > 0) { $files = $this->uploadDisk->files();
$keys = $set->pluck('key')->toArray();
$this->importKeys = $keys;
foreach ($files as $entry) {
$this->processVintageUpload($entry);
} }
Log::debug('Valid import keys are ', $this->importKeys);
return true;
} }
/** /**
* This method tells you when the vintage upload file was actually uploaded.
*
* @param string $entry * @param string $entry
* *
* @return string * @return string
*/ */
private function getOriginalUploadDate(string $entry): string private function getVintageUploadDate(string $entry): string
{ {
// this is an original upload. // this is an original upload.
$parts = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry)); $parts = explode('-', str_replace(['.csv.encrypted', $this->vintageFormat], '', $entry));
$originalUpload = intval($parts[1]); $originalUpload = intval($parts[1]);
$date = date('Y-m-d \a\t H-i-s', $originalUpload); $date = date('Y-m-d \a\t H-i-s', $originalUpload);
@@ -100,33 +124,17 @@ class UploadCollector extends BasicCollector implements CollectorInterface
} }
/** /**
* Tells you if a file name is a vintage upload.
*
* @param string $entry * @param string $entry
* *
* @return bool * @return bool
*/ */
private function isImportFile(string $entry): bool private function isVintageImport(string $entry): bool
{ {
$name = str_replace('.upload', '', $entry); $len = strlen($this->vintageFormat);
if (in_array($name, $this->importKeys)) {
Log::debug(sprintf('Import file "%s" is in array', $name), $this->importKeys);
return true;
}
Log::debug(sprintf('Import file "%s" is NOT in array', $name), $this->importKeys);
return false;
}
/**
* @param string $entry
*
* @return bool
*/
private function isOldImport(string $entry): bool
{
$len = strlen($this->expected);
// file is part of the old import routine: // file is part of the old import routine:
if (substr($entry, 0, $len) === $this->expected) { if (substr($entry, 0, $len) === $this->vintageFormat) {
return true; return true;
} }
@@ -135,49 +143,62 @@ class UploadCollector extends BasicCollector implements CollectorInterface
} }
/** /**
* @param $entry * @param string $key
*
* @return bool
*/ */
private function processUpload(string $entry) private function processModernUpload(string $key): bool
{
// file is old import:
if ($this->isOldImport($entry)) {
$this->saveOldImportFile($entry);
}
// file is current import.
if ($this->isImportFile($entry)) {
$this->saveImportFile($entry);
}
}
/**
* @param string $entry
*/
private function saveImportFile(string $entry)
{ {
// find job associated with import file: // find job associated with import file:
$name = str_replace('.upload', '', $entry); $job = $this->job->user->importJobs()->where('key', $key)->first();
$job = auth()->user()->importJobs()->where('key', $name)->first(); if (is_null($job)) {
$content = ''; return false;
try {
$content = Crypt::decrypt($this->uploadDisk->get($entry));
} catch (DecryptException $e) {
Log::error('Could not decrypt old import file ' . $entry . '. Skipped because ' . $e->getMessage());
} }
if (!is_null($job) && strlen($content) > 0) { // find the file for this import:
$content = '';
try {
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key)));
} catch (DecryptException $e) {
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
}
if (strlen($content) > 0) {
// add to export disk. // add to export disk.
$date = $job->created_at->format('Y-m-d'); $date = $job->created_at->format('Y-m-d');
$file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type); $file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type);
$this->exportDisk->put($file, $content); $this->exportDisk->put($file, $content);
$this->getFiles()->push($file); $this->getEntries()->push($file);
} }
return true;
} }
/** /**
* If the file is a vintage upload, process it.
*
* @param string $entry
*
* @return bool
*/
private function processVintageUpload(string $entry): bool
{
if ($this->isVintageImport($entry)) {
$this->saveVintageImportFile($entry);
return true;
}
return false;
}
/**
* This will store the content of the old vintage upload somewhere.
*
* @param string $entry * @param string $entry
*/ */
private function saveOldImportFile(string $entry) private function saveVintageImportFile(string $entry)
{ {
$content = ''; $content = '';
try { try {
@@ -188,10 +209,10 @@ class UploadCollector extends BasicCollector implements CollectorInterface
if (strlen($content) > 0) { if (strlen($content) > 0) {
// add to export disk. // add to export disk.
$date = $this->getOriginalUploadDate($entry); $date = $this->getVintageUploadDate($entry);
$file = $this->job->key . '-Old import dated ' . $date . '.csv'; $file = $this->job->key . '-Old import dated ' . $date . '.csv';
$this->exportDisk->put($file, $content); $this->exportDisk->put($file, $content);
$this->getFiles()->push($file); $this->getEntries()->push($file);
} }
} }

View File

@@ -1,66 +0,0 @@
<?php
/**
* ConfigurationFile.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Models\ExportJob;
use Storage;
/**
* Class ConfigurationFile
*
* @package FireflyIII\Export
*/
class ConfigurationFile
{
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var ExportJob */
private $job;
/**
* ConfigurationFile constructor.
*
* @param ExportJob $job
*/
public function __construct(ExportJob $job)
{
$this->job = $job;
$this->exportDisk = Storage::disk('export');
}
/**
* @return string
*/
public function make(): string
{
$fields = array_keys(Entry::getFieldsAndTypes());
$types = Entry::getFieldsAndTypes();
$configuration = [
'date-format' => 'Y-m-d', // unfortunately, this is hard-coded.
'has-headers' => true,
'map' => [], // we could build a map if necessary for easy re-import.
'roles' => [],
'mapped' => [],
'specifix' => [],
];
foreach ($fields as $field) {
$configuration['roles'][] = $types[$field];
}
$file = $this->job->key . '-configuration.json';
$this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT));
return $file;
}
}

View File

@@ -3,16 +3,17 @@
* Entry.php * Entry.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Export\Entry; namespace FireflyIII\Export\Entry;
use FireflyIII\Models\TransactionJournal; use Crypt;
use Illuminate\Support\Collection;
/** /**
* To extend the exported object, in case of new features in Firefly III for example, * To extend the exported object, in case of new features in Firefly III for example,
@@ -33,98 +34,77 @@ use Illuminate\Support\Collection;
*/ */
final class Entry final class Entry
{ {
/** @var string */ // @formatter:off
public $amount; public $journal_id;
/** @var EntryBill */
public $bill;
/** @var EntryBudget */
public $budget;
/** @var EntryCategory */
public $category;
/** @var string */
public $date; public $date;
/** @var string */
public $description; public $description;
/** @var EntryAccount */
public $destinationAccount; public $currency_code;
/** @var Collection */ public $amount;
public $destinationAccounts;
/** @var EntryAccount */ public $transaction_type;
public $sourceAccount;
/** @var Collection */ public $source_account_id;
public $sourceAccounts; public $source_account_name;
public $destination_account_id;
public $destination_account_name;
public $budget_id;
public $budget_name;
public $category_id;
public $category_name;
// @formatter:on
/** /**
* Entry constructor. * Entry constructor.
*/ */
private function __construct() private function __construct()
{ {
$this->sourceAccounts = new Collection;
$this->destinationAccounts = new Collection;
} }
/** /**
* @param TransactionJournal $journal * @param $object
* *
* @return Entry * @return Entry
*/ */
public static function fromJournal(TransactionJournal $journal) public static function fromObject($object): Entry
{ {
$entry = new self;
$entry = new self; // journal information:
$entry->description = $journal->description; $entry->journal_id = $object->transaction_journal_id;
$entry->date = $journal->date->format('Y-m-d'); $entry->description = $object->journal_encrypted === 1 ? Crypt::decrypt($object->journal_description) : $object->journal_description;
$entry->amount = TransactionJournal::amount($journal); $entry->amount = round($object->amount, 2); // always positive
$entry->date = $object->date;
$entry->transaction_type = $object->transaction_type;
$entry->currency_code = $object->transaction_currency_code;
$entry->budget = new EntryBudget($journal->budgets->first()); // source information:
$entry->category = new EntryCategory($journal->categories->first()); $entry->source_account_id = $object->account_id;
$entry->bill = new EntryBill($journal->bill); $entry->source_account_name = $object->account_name_encrypted === 1 ? Crypt::decrypt($object->account_name) : $object->account_name;
$sources = TransactionJournal::sourceAccountList($journal);
$destinations = TransactionJournal::destinationAccountList($journal);
$entry->sourceAccount = new EntryAccount($sources->first());
$entry->destinationAccount = new EntryAccount($destinations->first());
foreach ($sources as $source) { // destination information
$entry->sourceAccounts->push(new EntryAccount($source)); $entry->destination_account_id = $object->opposing_account_id;
} $entry->destination_account_name = $object->opposing_account_encrypted === 1 ? Crypt::decrypt($object->opposing_account_name)
: $object->opposing_account_name;
foreach ($destinations as $destination) {
$entry->destinationAccounts->push(new EntryAccount($destination)); // category and budget
$entry->category_id = $object->category_id ?? '';
$entry->category_name = $object->category_name ?? '';
$entry->budget_id = $object->budget_id ?? '';
$entry->budget_name = $object->budget_name ?? '';
// update description when transaction description is different:
if (!is_null($object->description) && $object->description != $entry->description) {
$entry->description = $entry->description . ' (' . $object->description . ')';
} }
return $entry; return $entry;
}
/**
* @return array
*/
public static function getFieldsAndTypes(): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
return [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
'source_account_id' => 'account-id',
'source_account_name' => 'account-name',
'source_account_iban' => 'account-iban',
'source_account_type' => '_ignore',
'source_account_number' => 'account-number',
'destination_account_id' => 'opposing-id',
'destination_account_name' => 'opposing-name',
'destination_account_iban' => 'opposing-iban',
'destination_account_type' => '_ignore',
'destination_account_number' => 'account-number',
'budget_id' => 'budget-id',
'budget_name' => 'budget-name',
'category_id' => 'category-id',
'category_name' => 'category-name',
'bill_id' => 'bill-id',
'bill_name' => 'bill-name',
];
} }
} }

View File

@@ -1,47 +0,0 @@
<?php
/**
* EntryAccount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Account;
/**
* Class EntryAccount
*
* @package FireflyIII\Export\Entry
*/
class EntryAccount
{
/** @var int */
public $accountId;
/** @var string */
public $iban;
/** @var string */
public $name;
/** @var string */
public $number;
/** @var string */
public $type;
/**
* EntryAccount constructor.
*
* @param Account $account
*/
public function __construct(Account $account)
{
$this->accountId = $account->id;
$this->name = $account->name;
$this->iban = $account->iban;
$this->type = $account->accountType->type;
$this->number = $account->getMeta('accountNumber');
}
}

View File

@@ -1,41 +0,0 @@
<?php
/**
* EntryBill.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Bill;
/**
* Class EntryBill
*
* @package FireflyIII\Export\Entry
*/
class EntryBill
{
/** @var int */
public $billId = '';
/** @var string */
public $name = '';
/**
* EntryBill constructor.
*
* @param Bill $bill
*/
public function __construct(Bill $bill = null)
{
if (!is_null($bill)) {
$this->billId = $bill->id;
$this->name = $bill->name;
}
}
}

View File

@@ -1,41 +0,0 @@
<?php
/**
* EntryBudget.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Budget;
/**
* Class EntryBudget
*
* @package FireflyIII\Export\Entry
*/
class EntryBudget
{
/** @var int */
public $budgetId = '';
/** @var string */
public $name = '';
/**
* EntryBudget constructor.
*
* @param Budget $budget
*/
public function __construct(Budget $budget = null)
{
if (!is_null($budget)) {
$this->budgetId = $budget->id;
$this->name = $budget->name;
}
}
}

View File

@@ -1,40 +0,0 @@
<?php
/**
* EntryCategory.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\Category;
/**
* Class EntryCategory
*
* @package FireflyIII\Export\Entry
*/
class EntryCategory
{
/** @var int */
public $categoryId = '';
/** @var string */
public $name = '';
/**
* EntryCategory constructor.
*
* @param Category $category
*/
public function __construct(Category $category = null)
{
if (!is_null($category)) {
$this->categoryId = $category->id;
$this->name = $category->name;
}
}
}

View File

@@ -3,8 +3,10 @@
* BasicExporter.php * BasicExporter.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* CsvExporter.php * CsvExporter.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -14,7 +16,6 @@ namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry; use FireflyIII\Export\Entry\Entry;
use FireflyIII\Export\Entry\EntryAccount; use FireflyIII\Export\Entry\EntryAccount;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use Illuminate\Support\Collection;
use League\Csv\Writer; use League\Csv\Writer;
use SplFileObject; use SplFileObject;
@@ -60,110 +61,24 @@ class CsvExporter extends BasicExporter implements ExporterInterface
$writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w'); $writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w');
$rows = []; $rows = [];
// Count the maximum number of sources and destinations each entry has. May need to expand the number of export fields: // get field names for header row:
$maxSourceAccounts = 1; $first = $this->getEntries()->first();
$maxDestAccounts = 1; $headers = array_keys(get_object_vars($first));
/** @var Entry $entry */ $rows[] = $headers;
foreach ($this->getEntries() as $entry) {
$sources = $entry->sourceAccounts->count();
$destinations = $entry->destinationAccounts->count();
$maxSourceAccounts = max($maxSourceAccounts, $sources);
$maxDestAccounts = max($maxDestAccounts, $destinations);
}
$rows[] = array_keys($this->getFieldsAndTypes($maxSourceAccounts, $maxDestAccounts));
/** @var Entry $entry */ /** @var Entry $entry */
foreach ($this->getEntries() as $entry) { foreach ($this->getEntries() as $entry) {
// order is defined in Entry::getFieldsAndTypes. $line = [];
$current = [$entry->description, $entry->amount, $entry->date]; foreach ($headers as $header) {
$sourceData = $this->getAccountData($maxSourceAccounts, $entry->sourceAccounts); $line[] = $entry->$header;
$current = array_merge($current, $sourceData); }
$destData = $this->getAccountData($maxDestAccounts, $entry->destinationAccounts); $rows[] = $line;
$current = array_merge($current, $destData);
$rest = [$entry->budget->budgetId, $entry->budget->name, $entry->category->categoryId, $entry->category->name, $entry->bill->billId,
$entry->bill->name];
$current = array_merge($current, $rest);
$rows[] = $current;
} }
$writer->insertAll($rows); $writer->insertAll($rows);
return true; return true;
} }
/**
* @param int $max
* @param Collection $accounts
*
* @return array
*/
private function getAccountData(int $max, Collection $accounts): array
{
$current = [];
for ($i = 0; $i < $max; $i++) {
/** @var EntryAccount $source */
$source = $accounts->get($i);
$currentId = '';
$currentName = '';
$currentIban = '';
$currentType = '';
$currentNumber = '';
if ($source) {
$currentId = $source->accountId;
$currentName = $source->name;
$currentIban = $source->iban;
$currentType = $source->type;
$currentNumber = $source->number;
}
$current[] = $currentId;
$current[] = $currentName;
$current[] = $currentIban;
$current[] = $currentType;
$current[] = $currentNumber;
}
unset($source);
return $current;
}
/**
* @param int $sources
* @param int $destinations
*
* @return array
*/
private function getFieldsAndTypes(int $sources, int $destinations): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
$array = [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
];
for ($i = 0; $i < $sources; $i++) {
$array['source_account_' . $i . '_id'] = 'account-id';
$array['source_account_' . $i . '_name'] = 'account-name';
$array['source_account_' . $i . '_iban'] = 'account-iban';
$array['source_account_' . $i . '_type'] = '_ignore';
$array['source_account_' . $i . '_number'] = 'account-number';
}
for ($i = 0; $i < $destinations; $i++) {
$array['destination_account_' . $i . '_id'] = 'account-id';
$array['destination_account_' . $i . '_name'] = 'account-name';
$array['destination_account_' . $i . '_iban'] = 'account-iban';
$array['destination_account_' . $i . '_type'] = '_ignore';
$array['destination_account_' . $i . '_number'] = 'account-number';
}
$array['budget_id'] = 'budget-id';
$array['budget_name'] = 'budget-name';
$array['category_id'] = 'category-id';
$array['category_name'] = 'category-name';
$array['bill_id'] = 'bill-id';
$array['bill_name'] = 'bill-name';
return $array;
}
private function tempFile() private function tempFile()
{ {

View File

@@ -3,8 +3,10 @@
* ExporterInterface.php * ExporterInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Processor.php * Processor.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -13,13 +15,13 @@ namespace FireflyIII\Export;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\Collector\AttachmentCollector; use FireflyIII\Export\Collector\AttachmentCollector;
use FireflyIII\Export\Collector\JournalCollector;
use FireflyIII\Export\Collector\UploadCollector; use FireflyIII\Export\Collector\UploadCollector;
use FireflyIII\Export\Entry\Entry; use FireflyIII\Export\Entry\Entry;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Storage; use Storage;
use ZipArchive; use ZipArchive;
@@ -38,8 +40,6 @@ class Processor
/** @var bool */ /** @var bool */
public $includeAttachments; public $includeAttachments;
/** @var bool */ /** @var bool */
public $includeConfig;
/** @var bool */
public $includeOldUploads; public $includeOldUploads;
/** @var ExportJob */ /** @var ExportJob */
public $job; public $job;
@@ -66,7 +66,6 @@ class Processor
$this->accounts = $settings['accounts']; $this->accounts = $settings['accounts'];
$this->exportFormat = $settings['exportFormat']; $this->exportFormat = $settings['exportFormat'];
$this->includeAttachments = $settings['includeAttachments']; $this->includeAttachments = $settings['includeAttachments'];
$this->includeConfig = $settings['includeConfig'];
$this->includeOldUploads = $settings['includeOldUploads']; $this->includeOldUploads = $settings['includeOldUploads'];
$this->job = $settings['job']; $this->job = $settings['job'];
$this->journals = new Collection; $this->journals = new Collection;
@@ -82,8 +81,9 @@ class Processor
{ {
/** @var AttachmentCollector $attachmentCollector */ /** @var AttachmentCollector $attachmentCollector */
$attachmentCollector = app(AttachmentCollector::class, [$this->job]); $attachmentCollector = app(AttachmentCollector::class, [$this->job]);
$attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']);
$attachmentCollector->run(); $attachmentCollector->run();
$this->files = $this->files->merge($attachmentCollector->getFiles()); $this->files = $this->files->merge($attachmentCollector->getEntries());
return true; return true;
} }
@@ -93,9 +93,13 @@ class Processor
*/ */
public function collectJournals(): bool public function collectJournals(): bool
{ {
/** @var JournalRepositoryInterface $repository */ /** @var JournalCollector $collector */
$repository = app(JournalRepositoryInterface::class); $collector = app(JournalCollector::class, [$this->job]);
$this->journals = $repository->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']); $collector->setDates($this->settings['startDate'], $this->settings['endDate']);
$collector->setAccounts($this->settings['accounts']);
$collector->run();
$this->journals = $collector->getEntries();
Log::debug(sprintf('Count %d journals in collectJournals() ', $this->journals->count()));
return true; return true;
} }
@@ -109,7 +113,7 @@ class Processor
$uploadCollector = app(UploadCollector::class, [$this->job]); $uploadCollector = app(UploadCollector::class, [$this->job]);
$uploadCollector->run(); $uploadCollector->run();
$this->files = $this->files->merge($uploadCollector->getFiles()); $this->files = $this->files->merge($uploadCollector->getEntries());
return true; return true;
} }
@@ -120,22 +124,11 @@ class Processor
public function convertJournals(): bool public function convertJournals(): bool
{ {
$count = 0; $count = 0;
/** @var TransactionJournal $journal */ foreach ($this->journals as $object) {
foreach ($this->journals as $journal) { $this->exportEntries->push(Entry::fromObject($object));
$this->exportEntries->push(Entry::fromJournal($journal));
$count++; $count++;
} }
Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count()));
return true;
}
/**
* @return bool
*/
public function createConfigFile(): bool
{
$this->configurationMaker = app(ConfigurationFile::class, [$this->job]);
$this->files->push($this->configurationMaker->make());
return true; return true;
} }

View File

@@ -3,8 +3,10 @@
* AccountChartGeneratorInterface.php * AccountChartGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -22,7 +24,6 @@ use Illuminate\Support\Collection;
*/ */
interface AccountChartGeneratorInterface interface AccountChartGeneratorInterface
{ {
/** /**
* @param Collection $accounts * @param Collection $accounts
* @param Carbon $start * @param Carbon $start
@@ -41,6 +42,15 @@ interface AccountChartGeneratorInterface
*/ */
public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array; public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
* @param Account $account * @param Account $account
* @param array $labels * @param array $labels

View File

@@ -3,8 +3,10 @@
* ChartJsAccountChartGenerator.php * ChartJsAccountChartGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -81,6 +83,30 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
return $data; return $data;
} }
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.earned'),
'data' => []]]];
foreach ($accounts as $account) {
if ($account->difference > 0) {
$data['labels'][] = $account->name;
$data['datasets'][0]['data'][] = $account->difference;
}
}
return $data;
}
/** /**
* @param Account $account * @param Account $account
* @param array $labels * @param array $labels
@@ -103,5 +129,4 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
return $data; return $data;
} }
} }

View File

@@ -3,8 +3,10 @@
* BillChartGeneratorInterface.php * BillChartGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ChartJsBillChartGenerator.php * ChartJsBillChartGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BudgetChartGeneratorInterface.php * BudgetChartGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ChartJsBudgetChartGenerator.php * ChartJsBudgetChartGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* CategoryChartGeneratorInterface.php * CategoryChartGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ChartJsCategoryChartGenerator.php * ChartJsCategoryChartGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ChartJsPiggyBankChartGenerator.php * ChartJsPiggyBankChartGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* PiggyBankChartGeneratorInterface.php * PiggyBankChartGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ChartJsReportChartGenerator.php * ChartJsReportChartGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ReportChartGeneratorInterface.php * ReportChartGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -1,50 +0,0 @@
<?php
/**
* AttachUserRole.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UserRegistration;
use FireflyIII\Repositories\User\UserRepositoryInterface;
/**
* Class AttachUserRole
*
* @package FireflyIII\Handlers\Events
*/
class AttachUserRole
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserRegistration $event
*/
public function handle(UserRegistration $event)
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
// first user ever?
if ($repository->count() == 1) {
$repository->attachRole($event->user, 'owner');
}
}
}

View File

@@ -1,46 +1,42 @@
<?php <?php
/** /**
* BudgetLimitEventHandler.php * BudgetEventHandler.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Handlers\Events; namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\BudgetLimitStored;
use FireflyIII\Events\BudgetLimitUpdated; use FireflyIII\Events\StoredBudgetLimit;
use FireflyIII\Events\UpdatedBudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Log; use Log;
/** /**
* Class BudgetLimitEventHandler * Handles budget related events.
*
* Class BudgetEventHandler
* *
* @package FireflyIII\Handlers\Events * @package FireflyIII\Handlers\Events
*/ */
class BudgetLimitEventHandler class BudgetEventHandler
{ {
/** /**
* Create the event listener. * This method creates a new budget limit repetition when a new budget limit has been created.
* *
*/ * @param StoredBudgetLimit $event
public function __construct()
{
}
/**
* In a perfect world, the store() routine should be different from the update()
* routine. It would not have to check count() == 0 because there could be NO
* limit repetitions at this point. However, the database can be wrong so we check.
* *
* @param BudgetLimitStored $event * @return bool
*/ */
public function store(BudgetLimitStored $event) public function storeRepetition(StoredBudgetLimit $event):bool
{ {
$budgetLimit = $event->budgetLimit; $budgetLimit = $event->budgetLimit;
$end = $event->end; $end = $event->end;
@@ -69,12 +65,18 @@ class BudgetLimitEventHandler
} }
return true;
} }
/** /**
* @param BudgetLimitUpdated $event * Updates, if present the budget limit repetition part of a budget limit.
*
* @param UpdatedBudgetLimit $event
*
* @return bool
*/ */
public function update(BudgetLimitUpdated $event) public function updateRepetition(UpdatedBudgetLimit $event): bool
{ {
$budgetLimit = $event->budgetLimit; $budgetLimit = $event->budgetLimit;
$end = $event->end; $end = $event->end;
@@ -102,6 +104,7 @@ class BudgetLimitEventHandler
$repetition->save(); $repetition->save();
} }
}
return true;
}
} }

View File

@@ -1,70 +0,0 @@
<?php
/**
* ConnectJournalToPiggyBank.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionJournal;
/**
* Class ConnectJournalToPiggyBank
*
* @package FireflyIII\Handlers\Events
*/
class ConnectJournalToPiggyBank
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionJournalStored $event
*
* @return bool
*/
public function handle(TransactionJournalStored $event): bool
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
/** @var PiggyBank $piggyBank */
$piggyBank = auth()->user()->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
return true;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return true;
}
$amount = TransactionJournal::amountPositive($journal);
// 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');
}
$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]);
return true;
}
}

View File

@@ -1,68 +0,0 @@
<?php
/**
* ConnectTransactionToPiggyBank.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionStored;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
/**
* Class ConnectTransactionToPiggyBank
*
* @package FireflyIII\Handlers\Events
*/
class ConnectTransactionToPiggyBank
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionStored $event
*
* @return bool
*/
public function handle(TransactionStored $event): bool
{
/** @var PiggyBankRepositoryInterface $repository */
$repository = app(PiggyBankRepositoryInterface::class);
$transaction = $event->transaction;
$piggyBank = $repository->find($transaction['piggy_bank_id']);
// valid piggy:
if (is_null($piggyBank->id)) {
return true;
}
$amount = strval($transaction['amount']);
// piggy bank account something with amount:
if ($transaction['source_account_id'] == $piggyBank->account_id) {
// if the source of this transaction is the same as the piggy bank,
// the money is being removed from the piggy bank. So the
// amount must be negative:
$amount = bcmul($amount, '-1');
}
$repetition = $piggyBank->currentRelevantRep();
// add or remove the money from the piggy bank:
$newAmount = bcadd(strval($repetition->currentamount), $amount);
$repetition->currentamount = $newAmount;
$repetition->save();
// now generate a piggy bank event:
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'date' => $transaction['date'], 'amount' => $newAmount]);
return true;
}
}

View File

@@ -1,66 +0,0 @@
<?php
/**
* FireRulesForStore.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
/**
* Class FireRulesForStore
*
* @package FireflyIII\Handlers\Events
*/
class FireRulesForStore
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionJournalStored $event
*
* @return bool
*/
public function handle(TransactionJournalStored $event): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
/** @var User $user */
$user = auth()->user();
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($event->journal);
if ($rule->stop_processing) {
return true;
}
}
}
return true;
}
}

View File

@@ -1,63 +0,0 @@
<?php
/**
* FireRulesForUpdate.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
/**
* Class FireRulesForUpdate
*
* @package FireflyIII\Handlers\Events
*/
class FireRulesForUpdate
{
/**
* Handle the event.
*
* @param TransactionJournalUpdated $event
*
* @return bool
*/
public function handle(TransactionJournalUpdated $event): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
/** @var User $user */
$user = auth()->user();
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'update-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($event->journal);
if ($rule->stop_processing) {
break;
}
}
}
return true;
}
}

View File

@@ -1,40 +0,0 @@
<?php
/**
* ScanForBillsAfterStore.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Support\Events\BillScanner;
/**
* Class RescanJournal
*
* @package FireflyIII\Handlers\Events
*/
class ScanForBillsAfterStore
{
/**
* Scan a transaction journal for possible links to bills, right after storing.
*
* @param TransactionJournalStored $event
*
* @return bool
*/
public function handle(TransactionJournalStored $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,39 +0,0 @@
<?php
/**
* ScanForBillsAfterUpdate.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Support\Events\BillScanner;
/**
* Class RescanJournal
*
* @package FireflyIII\Handlers\Events
*/
class ScanForBillsAfterUpdate
{
/**
* Scan a transaction journal for possibly related bills after it has been updated.
*
* @param TransactionJournalUpdated $event
*
* @return bool
*/
public function handle(TransactionJournalUpdated $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,67 +0,0 @@
<?php
/**
* SendRegistrationMail.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UserRegistration;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Swift_TransportException;
/**
* Class SendRegistrationMail
*
* @package FireflyIII\Handlers\Events
*/
class SendRegistrationMail
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserRegistration $event
*
* @return bool
*/
public function handle(UserRegistration $event): bool
{
$sendMail = env('SEND_REGISTRATION_MAIL', true);
if (!$sendMail) {
return true;
}
// get the email address
$email = $event->user->email;
$address = route('index');
$ipAddress = $event->ipAddress;
// send email.
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* StoredJournalEventHandler.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\Handlers\Events;
use FireflyIII\Events\StoredTransactionJournal;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\Processor;
use FireflyIII\Support\Events\BillScanner;
/**
* Class StoredJournalEventHandler
*
* @package FireflyIII\Handlers\Events
*/
class StoredJournalEventHandler
{
/**
* This method connects a new transfer to a piggy bank.
*
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function connectToPiggyBank(StoredTransactionJournal $event): bool
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
/** @var PiggyBank $piggyBank */
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
return true;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return true;
}
$amount = TransactionJournal::amountPositive($journal);
// 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');
}
$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]);
return true;
}
/**
* This method grabs all the users rules and processes them.
*
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function processRules(StoredTransactionJournal $event): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($journal);
if ($rule->stop_processing) {
return true;
}
}
}
return true;
}
/**
* This method calls a special bill scanner that will check if the stored journal is part of a bill.
*
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function scanBills(StoredTransactionJournal $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,72 +0,0 @@
<?php
/**
* UpdateJournalConnection.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\TransactionJournal;
/**
* Class UpdateJournalConnection
*
* @package FireflyIII\Handlers\Events
*/
class UpdateJournalConnection
{
/**
* Handle the event.
*
* @param TransactionJournalUpdated $event
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
*
* @return bool
*/
public function handle(TransactionJournalUpdated $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;
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* UpdatedJournalEventHandler.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\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;
/**
* Class UpdatedJournalEventHandler
*
* @package FireflyIII\Handlers\Events
*/
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.
*
* @param UpdatedTransactionJournal $event
*
* @return bool
*/
public function processRules(UpdatedTransactionJournal $event):bool
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'update-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
$processor = Processor::make($rule);
$processor->handleTransactionJournal($journal);
if ($rule->stop_processing) {
break;
}
}
}
return true;
}
/**
* This method calls a special bill scanner that will check if the updated journal is part of a bill.
*
* @param UpdatedTransactionJournal $event
*
* @return bool
*/
public function scanBills(UpdatedTransactionJournal $event): bool
{
$journal = $event->journal;
BillScanner::scan($journal);
return true;
}
}

View File

@@ -1,107 +0,0 @@
<?php
/**
* UserConfirmation.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyIII\Events\ResendConfirmation;
use FireflyIII\Events\UserRegistration;
use FireflyIII\User;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Preferences;
use Swift_TransportException;
/**
* Class UserConfirmation
*
* @package FireflyIII\Handlers\Events
*/
class UserConfirmation
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* @param ResendConfirmation $event
*
* @return bool
*/
public function resendConfirmation(ResendConfirmation $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$this->doConfirm($user, $ipAddress);
return true;
}
/**
* Handle the event.
*
* @param UserRegistration $event
*
* @return bool
*/
public function sendConfirmation(UserRegistration $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$this->doConfirm($user, $ipAddress);
return true;
}
/**
* @param User $user
* @param string $ipAddress
*/
private function doConfirm(User $user, string $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;
}
$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;
}
}

View File

@@ -0,0 +1,225 @@
<?php
/**
* UserEventHandler.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\Handlers\Events;
use Exception;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Preferences;
use Session;
use Swift_TransportException;
/**
* Class UserEventHandler
*
* This class responds to any events that have anything to do with the User object.
*
* The method name reflects what is being done. This is in the present tense.
*
*
* @package FireflyIII\Handlers\Events
*/
class UserEventHandler
{
/**
* This method will bestow upon a user the "owner" role if he is the first user in the system.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function attachUserRole(RegisteredUser $event): bool
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
// first user ever?
if ($repository->count() === 1) {
$repository->attachRole($event->user, 'owner');
}
return true;
}
/**
* Handle user logout events.
*
* @return bool
*/
public function logoutUser(): bool
{
// dump stuff from the session:
Session::forget('twofactor-authenticated');
Session::forget('twofactor-authenticated-date');
return true;
}
/**
* This method will send a newly registered user a confirmation message, urging him or her to activate their account.
*
* @param RegisteredUser $event
*
* @return bool
*/
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;
}
/**
* If the user has somehow lost his or her confirmation message, this event will send it to the user again.
*
* At the moment, this method is exactly the same as the ::sendConfirmationMessage method, but that will change.
*
* @param ResentConfirmation $event
*
* @return bool
*/
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 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;
}
/**
* This method will send the user a registration mail, welcoming him or her to Firefly III.
* This message is only sent when the configuration of Firefly III says so.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function sendRegistrationMail(RegisteredUser $event)
{
$sendMail = env('SEND_REGISTRATION_MAIL', true);
if (!$sendMail) {
return true;
}
// get the email address
$email = $event->user->email;
$address = route('index');
$ipAddress = $event->ipAddress;
// send email.
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* When the user is confirmed, this method stores the IP address of the user
* as a preference. Since this preference cannot be edited, it is effectively hidden
* from the user yet stored conveniently.
*
* @param ConfirmedUser $event
*
* @return bool
*/
public function storeConfirmationIpAddress(ConfirmedUser $event): bool
{
Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress);
return true;
}
/**
* This message stores the users IP address on registration, in much the same
* fashion as the previous method.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function storeRegistrationIpAddress(RegisteredUser $event): bool
{
Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress);
return true;
}
}

View File

@@ -1,36 +0,0 @@
<?php
/**
* UserEventListener.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Session;
/**
* Class UserEventListener
*
* @package FireflyIII\Handlers\Events
*/
class UserEventListener
{
/**
* Handle user logout events.
*
* @return bool
*/
public function onUserLogout(): bool
{
// dump stuff from the session:
Session::forget('twofactor-authenticated');
Session::forget('twofactor-authenticated-date');
return true;
}
}

View File

@@ -1,61 +0,0 @@
<?php
/**
* UserSaveIpAddress.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UserIsConfirmed;
use FireflyIII\Events\UserRegistration;
use Preferences;
/**
* Class UserSaveIpAddress
*
* @package FireflyIII\Handlers\Events
*/
class UserSaveIpAddress
{
/**
* Create the event listener.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserIsConfirmed $event
*
* @return bool
*/
public function saveFromConfirmation(UserIsConfirmed $event): bool
{
Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress);
return true;
}
/**
* Handle the event.
*
* @param UserRegistration $event
*
* @return bool
*/
public function saveFromRegistration(UserRegistration $event): bool
{
Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress);
return true;
}
}

View File

@@ -3,8 +3,10 @@
* AttachmentHelper.php * AttachmentHelper.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* AttachmentHelperInterface.php * AttachmentHelperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Account.php * Account.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Balance.php * Balance.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BalanceEntry.php * BalanceEntry.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BalanceHeader.php * BalanceHeader.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BalanceLine.php * BalanceLine.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Bill.php * Bill.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BillLine.php * BillLine.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Budget.php * Budget.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BudgetLine.php * BudgetLine.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Category.php * Category.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,14 +3,15 @@
* Expense.php * Expense.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection; namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use stdClass; use stdClass;
@@ -36,36 +37,11 @@ class Expense
} }
/** /**
* @param TransactionJournal $entry * @param stdClass $entry
*/ */
public function addOrCreateExpense(TransactionJournal $entry) public function addOrCreateExpense(stdClass $entry)
{ {
// add each account individually: $this->expenses->put($entry->id, $entry);
$destinations = TransactionJournal::destinationTransactionList($entry);
foreach ($destinations as $transaction) {
$amount = strval($transaction->amount);
$account = $transaction->account;
if (bccomp('0', $amount) === -1) {
$amount = bcmul($amount, '-1');
}
$object = new stdClass;
$object->amount = $amount;
$object->name = $account->name;
$object->count = 1;
$object->id = $account->id;
// overrule some properties:
if ($this->expenses->has($account->id)) {
$object = $this->expenses->get($account->id);
$object->amount = bcadd($object->amount, $amount);
$object->count++;
}
$this->expenses->put($account->id, $object);
}
} }
/** /**

View File

@@ -3,14 +3,15 @@
* Income.php * Income.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection; namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use stdClass; use stdClass;
@@ -37,32 +38,11 @@ class Income
} }
/** /**
* @param TransactionJournal $entry * @param stdClass $entry
*/ */
public function addOrCreateIncome(TransactionJournal $entry) public function addOrCreateIncome(stdClass $entry)
{ {
// add each account individually: $this->incomes->put($entry->id, $entry);
$sources = TransactionJournal::sourceTransactionList($entry);
foreach ($sources as $transaction) {
$amount = strval($transaction->amount);
$account = $transaction->account;
$amount = bcmul($amount, '-1');
$object = new stdClass;
$object->amount = $amount;
$object->name = $account->name;
$object->count = 1;
$object->id = $account->id;
// overrule some properties:
if ($this->incomes->has($account->id)) {
$object = $this->incomes->get($account->id);
$object->amount = bcadd($object->amount, $amount);
$object->count++;
}
$this->incomes->put($account->id, $object);
}
} }

View File

@@ -3,8 +3,10 @@
* FiscalHelper.php * FiscalHelper.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* FiscalHelperInterface.php * FiscalHelperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* Help.php * Help.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -24,47 +26,39 @@ class Help implements HelpInterface
{ {
/** /**
* * @param string $route
* @param string $key * @param string $language
* *
* @return string * @return string
*/ */
public function getFromCache(string $key): string public function getFromCache(string $route, string $language): string
{ {
return Cache::get($key); return Cache::get('help.' . $route . '.' . $language);
} }
/** /**
* @param string $language * @param string $language
* @param string $route * @param string $route
* *
* @return array * @return string
*/ */
public function getFromGithub(string $language, string $route): array public function getFromGithub(string $language, string $route): string
{ {
$uri = sprintf('https://raw.githubusercontent.com/JC5/firefly-iii-help/master/%s/%s.md', $language, $route); $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
$routeIndex = str_replace('.', '-', $route); $content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
$title = trans('help.' . $routeIndex); $result = Requests::get($uri);
$content = [
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
'title' => $title,
];
$result = Requests::get($uri);
if ($result->status_code === 200) { if ($result->status_code === 200) {
$content['text'] = $result->body; $content = $result->body;
} }
if (strlen(trim($content['text'])) == 0) { if (strlen(trim($content)) == 0) {
$content['text'] = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>'; $content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
} }
$converter = new CommonMarkConverter(); $converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']); $content = $converter->convertToHtml($content);
return $content; return $content;
@@ -82,27 +76,26 @@ class Help implements HelpInterface
} }
/** /**
*
* @param string $route * @param string $route
* @param string $language
* *
* @return bool * @return bool
*/ */
public function inCache(string $route):bool public function inCache(string $route, string $language):bool
{ {
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text'); return Cache::has('help.' . $route . '.' . $language);
} }
/** /**
* *
* @param string $route * @param string $route
* @param string $language * @param string $language
* @param array $content * @param string $content
* *
* @internal param $title * @internal param $title
*/ */
public function putInCache(string $route, string $language, array $content) public function putInCache(string $route, string $language, string $content)
{ {
Cache::put('help.' . $route . '.text.' . $language, $content['text'], 10080); // a week. Cache::put('help.' . $route . '.' . $language, $content, 10080); // a week.
Cache::put('help.' . $route . '.title.' . $language, $content['title'], 10080);
} }
} }

View File

@@ -3,8 +3,10 @@
* HelpInterface.php * HelpInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -19,19 +21,20 @@ interface HelpInterface
{ {
/** /**
* @param string $key * @param string $route
* @param string $language
* *
* @return string * @return string
*/ */
public function getFromCache(string $key): string; public function getFromCache(string $route, string $language): string;
/** /**
* @param string $language * @param string $language
* @param string $route * @param string $route
* *
* @return array * @return string
*/ */
public function getFromGithub(string $language, string $route):array; public function getFromGithub(string $language, string $route):string;
/** /**
* @param string $route * @param string $route
@@ -42,15 +45,16 @@ interface HelpInterface
/** /**
* @param string $route * @param string $route
* @param string $language
* *
* @return bool * @return bool
*/ */
public function inCache(string $route): bool; public function inCache(string $route, string $language ): bool;
/** /**
* @param string $route * @param string $route
* @param string $language * @param string $language
* @param array $content * @param string $content
*/ */
public function putInCache(string $route, string $language, array $content); public function putInCache(string $route, string $language, string $content);
} }

View File

@@ -1,142 +0,0 @@
<?php
/**
* AccountReportHelper.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use DB;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Class AccountReportHelper
*
* @package FireflyIII\Helpers\Report
*/
class AccountReportHelper implements AccountReportHelperInterface
{
/**
* @param Account $account
* @param Collection $startSet
* @param Collection $endSet
* @param Collection $backupSet
*
* @return Account
*/
public static function reportFilter(Account $account, Collection $startSet, Collection $endSet, Collection $backupSet)
{
// The balance for today always incorporates transactions made on today. So to get todays "start" balance, we sub one day.
$account->startBalance = '0';
$account->endBalance = '0';
$currentStart = $startSet->filter(
function (Account $entry) use ($account) {
return $account->id == $entry->id;
}
);
$currentBackup = $backupSet->filter( // grab entry from current backup as well:
function (Account $entry) use ($account) {
return $account->id == $entry->id;
}
);
// first try to set from backup
if (!is_null($currentBackup->first())) {
$account->startBalance = $currentBackup->first()->balance;
}
// overrule with data from start
if (!is_null($currentStart->first())) {
$account->startBalance = $currentStart->first()->balance;
}
$currentEnd = $endSet->filter(
function (Account $entry) use ($account) {
return $account->id == $entry->id;
}
);
if (!is_null($currentEnd->first())) {
$account->endBalance = $currentEnd->first()->balance;
}
return $account;
}
/**
* This method generates a full report for the given period on all
* given accounts.
*
* a special consideration for accounts that did exist on this exact day.
* we also grab the balance from today just in case, to see if that changes things.
* it's a fall back for users who (rightly so) start keeping score at the first of
* the month and find the first report lacking / broken.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return AccountCollection
*/
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts): AccountCollection
{
$startAmount = '0';
$endAmount = '0';
$diff = '0';
$ids = $accounts->pluck('id')->toArray();
$yesterday = clone $start;
$yesterday->subDay();
$startSet = $this->getSet($ids, $yesterday); // get balances for start.
$backupSet = $this->getSet($ids, $start);
$endSet = $this->getSet($ids, $end);
$accounts->each(
function (Account $account) use ($startSet, $endSet, $backupSet) {
return self::reportFilter($account, $startSet, $endSet, $backupSet);
}
);
// summarize:
foreach ($accounts as $account) {
$startAmount = bcadd($startAmount, $account->startBalance);
$endAmount = bcadd($endAmount, $account->endBalance);
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
}
$object = new AccountCollection;
$object->setStart($startAmount);
$object->setEnd($endAmount);
$object->setDifference($diff);
$object->setAccounts($accounts);
return $object;
}
/**
* @param array $ids
* @param Carbon $date
*
* @return Collection
*/
private function getSet(array $ids, Carbon $date): Collection
{
return Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereIn('accounts.id', $ids)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
->groupBy('accounts.id')
->get(['accounts.id', DB::raw('SUM(`transactions`.`amount`) as `balance`')]);
}
}

View File

@@ -1,38 +0,0 @@
<?php
/**
* AccountReportHelperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use Illuminate\Support\Collection;
/**
* Interface AccountReportHelperInterface
*
* @package FireflyIII\Helpers\Report
*/
interface AccountReportHelperInterface
{
/**
* This method generates a full report for the given period on all
* given accounts
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return AccountCollection
*/
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts): AccountCollection;
}

View File

@@ -3,8 +3,10 @@
* BalanceReportHelper.php * BalanceReportHelper.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -126,7 +128,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface
->get( ->get(
[ [
't_destination.account_id', 't_destination.account_id',
DB::raw('SUM(`t_destination`.`amount`) as `sum`'), DB::raw('SUM(t_destination.amount) AS sum'),
] ]
); );

View File

@@ -3,8 +3,10 @@
* BalanceReportHelperInterface.php * BalanceReportHelperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BudgetReportHelper.php * BudgetReportHelper.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* BudgetReportHelperInterface.php * BudgetReportHelperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* ReportHelper.php * ReportHelper.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -22,7 +24,7 @@ use FireflyIII\Models\Bill;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
@@ -30,6 +32,7 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use stdClass;
/** /**
* Class ReportHelper * Class ReportHelper
@@ -99,7 +102,8 @@ class ReportHelper implements ReportHelperInterface
$billLine->setHit(true); $billLine->setHit(true);
} }
if ($billLine->isActive()) { // non active AND non hit? do not add:
if ($billLine->isActive() || $billLine->isHit()) {
$collection->addBill($billLine); $collection->addBill($billLine);
} }
} }
@@ -144,14 +148,14 @@ class ReportHelper implements ReportHelperInterface
public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense
{ {
$object = new Expense; $object = new Expense;
/** @var AccountRepositoryInterface $repos */
$repos = app(AccountRepositoryInterface::class);
$journals = $repos->expensesInPeriod($accounts, $start, $end);
/** @var TransactionJournal $entry */ /** @var AccountTaskerInterface $tasker */
foreach ($journals as $entry) { $tasker = app(AccountTaskerInterface::class);
$amount = TransactionJournal::amount($entry); $collection = $tasker->expenseReport($accounts, $accounts, $start, $end);
$object->addToTotal($amount);
/** @var stdClass $entry */
foreach ($collection as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateExpense($entry); $object->addOrCreateExpense($entry);
} }
@@ -170,13 +174,13 @@ class ReportHelper implements ReportHelperInterface
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income
{ {
$object = new Income; $object = new Income;
/** @var AccountRepositoryInterface $repos */ /** @var AccountTaskerInterface $tasker */
$repos = app(AccountRepositoryInterface::class); $tasker = app(AccountTaskerInterface::class);
$journals = $repos->incomesInPeriod($accounts, $start, $end); $collection = $tasker->incomeReport($accounts, $accounts, $start, $end);
foreach ($journals as $entry) { /** @var stdClass $entry */
$amount = TransactionJournal::amount($entry); foreach ($collection as $entry) {
$object->addToTotal($amount); $object->addToTotal($entry->amount);
$object->addOrCreateIncome($entry); $object->addOrCreateIncome($entry);
} }
@@ -255,9 +259,19 @@ class ReportHelper implements ReportHelperInterface
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where( ->where(
function (Builder $q) use ($ids) { // source.account_id in accountIds XOR destination.account_id in accountIds
$q->whereIn('source.account_id', $ids) function (Builder $query) use ($ids) {
->whereIn('destination.account_id', $ids, 'xor'); $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']); ->get(['tags.id', 'tags.tag', 'transaction_journals.id as journal_id', 'destination.amount']);

View File

@@ -3,8 +3,10 @@
* ReportHelperInterface.php * ReportHelperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);

View File

@@ -3,8 +3,10 @@
* AccountController.php * AccountController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -13,11 +15,13 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Crud\Account\AccountCrudInterface; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -42,8 +46,16 @@ class AccountController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts')); // translations:
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts'));
return $next($request);
}
);
} }
/** /**
@@ -70,16 +82,16 @@ class AccountController extends Controller
} }
/** /**
* @param AccountCrudInterface $crud * @param ARI $repository
* @param Account $account * @param Account $account
* *
* @return View * @return View
*/ */
public function delete(AccountCrudInterface $crud, Account $account) public function delete(ARI $repository, Account $account)
{ {
$typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type); $typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); $subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
$accountList = ExpandedForm::makeSelectListWithEmpty($crud->getAccountsByType([$account->accountType->type])); $accountList = ExpandedForm::makeSelectListWithEmpty($repository->getAccountsByType([$account->accountType->type]));
unset($accountList[$account->id]); unset($accountList[$account->id]);
// put previous url in session // put previous url in session
@@ -91,19 +103,19 @@ class AccountController extends Controller
} }
/** /**
* @param AccountCrudInterface $crud * @param ARI $repository
* @param Account $account * @param Account $account
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function destroy(AccountCrudInterface $crud, Account $account) public function destroy(ARI $repository, Account $account)
{ {
$type = $account->accountType->type; $type = $account->accountType->type;
$typeName = config('firefly.shortNamesByFullName.' . $type); $typeName = config('firefly.shortNamesByFullName.' . $type);
$name = $account->name; $name = $account->name;
$moveTo = $crud->find(intval(Input::get('move_account_before_delete'))); $moveTo = $repository->find(intval(Input::get('move_account_before_delete')));
$crud->destroy($account, $moveTo); $repository->destroy($account, $moveTo);
Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name]))); Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
Preferences::mark(); Preferences::mark();
@@ -112,18 +124,16 @@ class AccountController extends Controller
} }
/** /**
* @param ARI $repository
* @param Account $account * @param Account $account
* *
* @return View * @return View
*/ */
public function edit(ARI $repository, Account $account) public function edit(Account $account)
{ {
$what = config('firefly.shortNamesByFullName')[$account->accountType->type]; $what = config('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]); $subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$openingBalance = $repository->openingBalanceTransaction($account);
// put previous url in session if not redirect from store (not "return_to_edit"). // put previous url in session if not redirect from store (not "return_to_edit").
if (session('accounts.edit.fromUpdate') !== true) { if (session('accounts.edit.fromUpdate') !== true) {
@@ -134,21 +144,19 @@ class AccountController extends Controller
// pre fill some useful values. // pre fill some useful values.
// the opening balance is tricky: // the opening balance is tricky:
$openingBalanceAmount = null; $openingBalanceAmount = $account->getOpeningBalanceAmount();
$openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount;
if ($openingBalance->id) { $openingBalanceDate = $account->getOpeningBalanceDate();
$transaction = $repository->getFirstTransaction($openingBalance, $account); $openingBalanceDate = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d');
$openingBalanceAmount = $transaction->amount;
}
$preFilled = [ $preFilled = [
'accountNumber' => $account->getMeta('accountNumber'), 'accountNumber' => $account->getMeta('accountNumber'),
'accountRole' => $account->getMeta('accountRole'), 'accountRole' => $account->getMeta('accountRole'),
'ccType' => $account->getMeta('ccType'), 'ccType' => $account->getMeta('ccType'),
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'), 'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance->id ? $openingBalance->date->format('Y-m-d') : null, 'openingBalanceDate' => $openingBalanceDate,
'openingBalance' => $openingBalanceAmount, 'openingBalance' => $openingBalanceAmount,
'virtualBalance' => round($account->virtual_balance, 2), 'virtualBalance' => $account->virtual_balance,
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts'); Session::flash('gaEventCategory', 'accounts');
@@ -158,19 +166,19 @@ class AccountController extends Controller
} }
/** /**
* @param AccountCrudInterface $crud * @param ARI $repository
* @param string $what * @param string $what
* *
* @return View * @return View
*/ */
public function index(AccountCrudInterface $crud, string $what) public function index(ARI $repository, string $what)
{ {
$what = $what ?? 'asset'; $what = $what ?? 'asset';
$subTitle = trans('firefly.' . $what . '_accounts'); $subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$types = config('firefly.accountTypesByIdentifier.' . $what); $types = config('firefly.accountTypesByIdentifier.' . $what);
$accounts = $crud->getAccountsByType($types); $accounts = $repository->getAccountsByType($types);
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', Carbon::now()->startOfMonth()); $start = clone session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
@@ -194,13 +202,17 @@ class AccountController extends Controller
} }
/** /**
* @param ARI $repository * @param AccountTaskerInterface $tasker
* @param Account $account * @param ARI $repository
* @param Account $account
* *
* @return View * @return View
*/ */
public function show(ARI $repository, Account $account) public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account)
{ {
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
return $this->redirectToOriginalAccount($account);
}
// show journals from current period only: // show journals from current period only:
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitle = $account->name; $subTitle = $account->name;
@@ -212,7 +224,7 @@ class AccountController extends Controller
$page = intval(Input::get('page')); $page = intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data; $pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize; $offset = ($page - 1) * $pageSize;
$set = $repository->journalsInPeriod(new Collection([$account]), [], $start, $end); $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
$count = $set->count(); $count = $set->count();
$subSet = $set->splice($offset, $pageSize); $subSet = $set->splice($offset, $pageSize);
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
@@ -220,10 +232,7 @@ class AccountController extends Controller
// grouped other months thing: // grouped other months thing:
// oldest transaction in account: // oldest transaction in account:
$start = $repository->firstUseDate($account); $start = $repository->oldestJournalDate($account);
if ($start->year == 1900) {
$start = new Carbon;
}
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range); $start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range); $end = Navigation::endOfX(new Carbon, $range);
@@ -243,11 +252,17 @@ class AccountController extends Controller
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); 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) { while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range); $end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range);
$spent = $this->spentInPeriod($account, $end, $currentEnd); $spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$earned = $this->earnedInPeriod($account, $end, $currentEnd); $earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]); $entries->push([$dateStr, $dateName, $spent, $earned]);
@@ -260,13 +275,13 @@ class AccountController extends Controller
} }
/** /**
* @param ARI $repository * @param AccountTaskerInterface $tasker
* @param Account $account * @param Account $account
* @param string $date * @param string $date
* *
* @return View * @return View
*/ */
public function showWithDate(ARI $repository, Account $account, string $date) public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date)
{ {
$carbon = new Carbon($date); $carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
@@ -277,7 +292,7 @@ class AccountController extends Controller
$page = $page === 0 ? 1 : $page; $page = $page === 0 ? 1 : $page;
$pageSize = Preferences::get('transactionPageSize', 50)->data; $pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize; $offset = ($page - 1) * $pageSize;
$set = $repository->journalsInPeriod(new Collection([$account]), [], $start, $end); $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
$count = $set->count(); $count = $set->count();
$subSet = $set->splice($offset, $pageSize); $subSet = $set->splice($offset, $pageSize);
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
@@ -287,30 +302,16 @@ class AccountController extends Controller
} }
/** /**
* @param AccountFormRequest $request * @param AccountFormRequest $request
* @param AccountCrudInterface $crud * @param ARI $repository
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
*/ */
public function store(AccountFormRequest $request, AccountCrudInterface $crud) public function store(AccountFormRequest $request, ARI $repository)
{ {
$accountData = [ $data = $request->getAccountData();
'name' => $request->input('name'), $account = $repository->store($data);
'accountType' => $request->input('what'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')),
'active' => true,
'user' => auth()->user()->id,
'iban' => $request->input('iban'),
'accountNumber' => $request->input('accountNumber'),
'accountRole' => $request->input('accountRole'),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
];
$account = $crud->store($accountData);
Session::flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name]))); Session::flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
Preferences::mark(); Preferences::mark();
@@ -334,30 +335,16 @@ class AccountController extends Controller
} }
/** /**
* @param AccountFormRequest $request * @param AccountFormRequest $request
* @param AccountCrudInterface $crud * @param ARI $repository
* @param Account $account * @param Account $account
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function update(AccountFormRequest $request, AccountCrudInterface $crud, Account $account) public function update(AccountFormRequest $request, ARI $repository, Account $account)
{ {
$data = $request->getAccountData();
$accountData = [ $repository->update($account, $data);
'name' => $request->input('name'),
'active' => $request->input('active'),
'user' => auth()->user()->id,
'iban' => $request->input('iban'),
'accountNumber' => $request->input('accountNumber'),
'accountRole' => $request->input('accountRole'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
];
$crud->update($account, $accountData);
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name]))); Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
Preferences::mark(); Preferences::mark();
@@ -391,67 +378,27 @@ class AccountController extends Controller
} }
/** /**
* Asset accounts actually earn money by being the destination of a deposit or the destination
* of a transfer. The money moves to them.
*
* A revenue account doesn't really earn money itself. Money is earned "from" the revenue account.
* So, the call to find out how many money has been earned by/from a revenue account is slightly different.
*
*
*
* @param Account $account * @param Account $account
* @param Carbon $start
* @param Carbon $end
* *
* @return string * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/ */
private function earnedInPeriod(Account $account, Carbon $start, Carbon $end) private function redirectToOriginalAccount(Account $account)
{ {
/** @var ARI $repository */ /** @var Transaction $transaction */
$repository = app(ARI::class); $transaction = $account->transactions()->first();
$collection = new Collection([$account]); if (is_null($transaction)) {
$type = $account->accountType->type; throw new FireflyException('Expected a transaction. This account has none. BEEP, error.');
switch ($type) {
case AccountType::DEFAULT:
case AccountType::ASSET:
return $repository->earnedInPeriod($collection, $start, $end);
case AccountType::REVENUE:
return $repository->earnedFromInPeriod($collection, $start, $end);
default:
return '0';
} }
}
/** $journal = $transaction->transactionJournal;
* Asset accounts actually spend money by being the source of a withdrawal or the source /** @var Transaction $opposingTransaction */
* of a transfer. The money moves away from them. $opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
*
* An expense account doesn't really spend money itself. Money is spent "at" the expense account. if (is_null($opposingTransaction)) {
* So, the call to find out how many money has been spent on/at an expense account is slightly different. throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.');
*
*
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
private function spentInPeriod(Account $account, Carbon $start, Carbon $end): string
{
/** @var ARI $repository */
$repository = app(ARI::class);
$collection = new Collection([$account]);
$type = $account->accountType->type;
switch ($type) {
case AccountType::DEFAULT:
case AccountType::ASSET:
return $repository->spentInPeriod($collection, $start, $end);
case AccountType::EXPENSE:
return $repository->spentAtInPeriod($collection, $start, $end);
default:
return '0';
} }
}
return redirect(route('accounts.show', [$opposingTransaction->account_id]));
}
} }

View File

@@ -3,8 +3,10 @@
* ConfigurationController.php * ConfigurationController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com * Copyright (C) 2016 thegrumpydictator@gmail.com
* *
* This software may be modified and distributed under the terms * This software may be modified and distributed under the terms of the
* of the MIT license. See the LICENSE file for details. * Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/ */
declare(strict_types = 1); declare(strict_types = 1);
@@ -35,8 +37,15 @@ class ConfigurationController extends Controller
{ {
parent::__construct(); parent::__construct();
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o'); $this->middleware(
function ($request, $next) {
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request);
}
);
} }
@@ -58,14 +67,16 @@ class ConfigurationController extends Controller
/** /**
* @param ConfigurationRequest $request * @param ConfigurationRequest $request
*
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(ConfigurationRequest $request) public function store(ConfigurationRequest $request)
{ {
// get config values: // get config values:
$singleUserMode = intval($request->get('single_user_mode')) === 1 ? true : false; $data = $request->getConfigurationData();
// store config values // store config values
FireflyConfig::set('single_user_mode', $singleUserMode); FireflyConfig::set('single_user_mode', $data['single_user_mode']);
// flash message // flash message
Session::flash('success', strval(trans('firefly.configuration_updated'))); Session::flash('success', strval(trans('firefly.configuration_updated')));

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