Compare commits

...

581 Commits

Author SHA1 Message Date
James Cole
d550a626e9 Merge branch 'release/4.8.1' 2019-09-08 08:08:57 +02:00
James Cole
31dea785f6 Remove newline 2019-09-08 08:07:57 +02:00
James Cole
4eff0b73c7 Changelog [skip ci] 2019-09-08 08:03:28 +02:00
James Cole
dc55d712af Add missing variable. 2019-09-08 08:00:53 +02:00
James Cole
d735694e2a Update composer file. 2019-09-08 07:56:28 +02:00
James Cole
f4c2b8e348 Last minute changes. 2019-09-08 07:55:19 +02:00
James Cole
5ff0ca81b7 #2285 2019-09-07 21:02:34 +02:00
James Cole
5fcb5fdaa5 Fix for #2231 2019-09-07 20:44:02 +02:00
James Cole
03d8543cae New translations [skip ci] 2019-09-07 20:28:21 +02:00
James Cole
fb7a74fb5c Update changelog for upcoming release. [skip ci] 2019-09-07 20:22:10 +02:00
James Cole
b11f9611ff Another fix for #2487 2019-09-07 19:55:50 +02:00
James Cole
cb7a4fedf7 Fix #2553 2019-09-07 19:46:16 +02:00
James Cole
1c68b0902d Fix for #2555 2019-09-07 19:40:44 +02:00
James Cole
0c967cb011 Remove facebook [skip ci] 2019-09-07 19:32:30 +02:00
James Cole
c0fd371430 Fix #2557 2019-09-07 16:19:01 +02:00
James Cole
cb9c59e5e3 Enable clone transaction (after a fashion) 2019-09-07 07:26:31 +02:00
James Cole
8dccfc796a Add missing languages, fixes #2550 2019-09-07 06:30:54 +02:00
James Cole
b0b2bfb361 Remove link to clone function. 2019-09-07 06:23:36 +02:00
James Cole
62b65927ff Update some meta files. 2019-09-07 06:23:26 +02:00
James Cole
50fca4f19e New translations. 2019-09-06 20:26:57 +02:00
James Cole
c6c66b9dcb Introduce Greek. 2019-09-06 20:26:13 +02:00
James Cole
ff3e65baeb Upgrade API version 2019-09-06 17:19:30 +02:00
James Cole
30f454d38a Fix tests 2019-09-06 17:19:21 +02:00
James Cole
e9782a701f This is not a static function 2019-09-06 17:19:03 +02:00
James Cole
1a583c771b Fix issue in multi year reports 2019-09-06 17:18:55 +02:00
James Cole
69af0c3d65 Fix for #2548 2019-09-06 17:18:33 +02:00
James Cole
a6164456e6 Code for #2534 2019-09-06 16:29:52 +02:00
James Cole
377f31b910 Fix for #2547 2019-09-06 15:55:17 +02:00
James Cole
445ecdca17 Update for #2534 2019-09-06 12:01:14 +02:00
James Cole
aa7a35d93d New security policy. 2019-09-06 12:00:42 +02:00
James Cole
eb0d269ce7 Fix #2545 2019-09-06 09:37:00 +02:00
James Cole
77084ba72d More code for #2543 2019-09-06 06:30:01 +02:00
James Cole
07dd44af2d Fix #2543 2019-09-06 06:24:25 +02:00
James Cole
a29e78ed44 Fix #2536 2019-09-06 06:02:22 +02:00
James Cole
5d192ad4c2 Add bill / category and budget info 2019-09-05 19:04:58 +02:00
James Cole
3fb13b870f Fix #2513 2019-09-05 18:58:01 +02:00
James Cole
7cb53f4396 Some extra things for Docker image. 2019-09-05 17:47:25 +02:00
James Cole
2d47ee7e21 Fix null pointer in command 2019-09-05 17:46:18 +02:00
James Cole
95181a0a6a Rebuild JS. 2019-09-05 08:12:33 +02:00
James Cole
f36b369d73 Fix for #2483 2019-09-04 21:06:01 +02:00
James Cole
5623c3c43f Clean up some tests 2019-09-04 21:05:50 +02:00
James Cole
f9f1fa0fcb Clean up API code and fix test code. 2019-09-04 17:39:39 +02:00
James Cole
f52584d46b Remove unused code. 2019-09-04 16:07:44 +02:00
James Cole
bbcb492075 Fix #2539 [skip ci] 2019-09-04 16:02:27 +02:00
James Cole
0f2040f931 Fix some last-minute issues. 2019-09-04 10:27:13 +02:00
James Cole
2cda6eba94 Make sure amounts are always positive. 2019-09-04 10:12:33 +02:00
James Cole
d23656aac9 Code for #2466 2019-09-04 09:45:02 +02:00
James Cole
fff98186b2 Fix #2521 2019-09-04 09:21:31 +02:00
James Cole
5c468e1c49 Fix #2383 2019-09-04 09:20:26 +02:00
James Cole
2bff6b49e6 Fix #2537 2019-09-04 09:10:12 +02:00
James Cole
d107e78641 Code for #2389 2019-09-04 07:51:31 +02:00
James Cole
47de2fec28 Revamp multi-account report. 2019-09-03 22:35:41 +02:00
James Cole
4743230136 Refactor some views for double account report. 2019-09-03 17:30:45 +02:00
James Cole
7fb5c12a29 Updated language strings. 2019-09-03 17:21:45 +02:00
James Cole
ddabf6a246 Fixes #2464 2019-09-03 17:06:30 +02:00
James Cole
a8b521bdd5 Fix #2526 2019-09-03 16:57:03 +02:00
James Cole
e113fea8f0 Fixes #2522 2019-09-03 16:56:46 +02:00
James Cole
b3cbbeecf0 Update version. 2019-09-03 16:56:20 +02:00
James Cole
401086b75f Remove unused code. 2019-09-03 15:43:11 +02:00
James Cole
5a8146aa34 Revamped tag report as well. 2019-09-03 15:37:59 +02:00
James Cole
4872d3e4bc Clean up and add some todo's 2019-09-03 11:55:06 +02:00
James Cole
7cee183f1c Update docker files. 2019-09-03 11:43:54 +02:00
James Cole
8b0903c32e Finish multi-currency budget and account report. 2019-09-03 09:06:12 +02:00
James Cole
6443b7bde4 Make sure info includes earned data 2019-09-02 22:31:07 +02:00
James Cole
e75c15a61b First start making the category report also multi-currency 2019-09-02 21:47:20 +02:00
James Cole
206dae87ba Fix some issues in multi-currency budget reports. 2019-09-02 21:21:29 +02:00
James Cole
771cf73171 Budget report cleaned up and multi-currency. 2019-09-02 20:30:47 +02:00
James Cole
0fd7e4363d Make some parts of the budget repository multi-currency 2019-09-02 16:52:35 +02:00
James Cole
8246d901e7 Fix #2513 2019-09-01 19:17:46 +02:00
James Cole
30fc56a0d7 Fix some bugs in multi-currency reports. 2019-09-01 19:08:10 +02:00
James Cole
01876be152 Fix some bugs in multi-currency reports. 2019-09-01 18:50:10 +02:00
James Cole
a57a7d3bda Fix some bugs in multi-currency reports. 2019-09-01 18:41:57 +02:00
James Cole
5c099008e8 Make report budget charts multi-currency 2019-09-01 16:52:49 +02:00
James Cole
ce06fb73b1 Expand view of report and make multi currency 2019-09-01 15:23:33 +02:00
James Cole
1e9f354a81 Remove unused code. 2019-09-01 14:50:37 +02:00
James Cole
f5b01c7b21 Fix #2526 2019-09-01 14:49:26 +02:00
James Cole
48d3130b36 Fix #2527 2019-09-01 14:42:27 +02:00
James Cole
d247ed5dc5 Final things. 2019-09-01 11:13:03 +02:00
James Cole
4cd52963a6 Allow user to set multi-currency available budget. WIP 2019-09-01 10:48:18 +02:00
James Cole
509276e20b Allow user to set multi-currency available budget. WIP 2019-08-31 21:47:55 +02:00
James Cole
ca777857c2 Allow user to set multi-currency available budget. WIP 2019-08-31 09:35:35 +02:00
James Cole
61b6e266da Catch "no category" entries. 2019-08-30 09:30:06 +02:00
James Cole
717b9d21fd Move method to correct repository. 2019-08-30 09:19:29 +02:00
James Cole
134d1b2746 Move method to correct repository. 2019-08-30 09:13:10 +02:00
James Cole
5973b94677 Move method to correct repository. 2019-08-30 08:19:55 +02:00
James Cole
40441de545 Move method to correct repository. 2019-08-30 08:15:09 +02:00
James Cole
a90eacf686 Move method to correct repository. 2019-08-30 08:12:15 +02:00
James Cole
09bc50dd4d Move method to correct repository. 2019-08-30 08:09:39 +02:00
James Cole
7685e2007f Move method to correct repository. 2019-08-30 08:09:13 +02:00
James Cole
4a3e1a9604 Move method to correct repository. 2019-08-30 08:03:13 +02:00
James Cole
9fad13c788 Move method to correct repository. 2019-08-30 08:02:11 +02:00
James Cole
97d87b0657 Move method to correct repository. 2019-08-30 08:00:52 +02:00
James Cole
1da4597f94 Move method to correct repository. 2019-08-30 07:54:09 +02:00
James Cole
e525960320 Move method to correct repository. 2019-08-30 07:49:08 +02:00
James Cole
4cdbea2737 Move method to correct repository. 2019-08-30 07:45:48 +02:00
James Cole
6a06ab35c9 Fix #2516 2019-08-30 07:28:48 +02:00
James Cole
6d3b8e7def Fix #2518 2019-08-30 07:17:02 +02:00
James Cole
a6b1fcb609 Refactor references to budget repository. 2019-08-29 21:42:55 +02:00
James Cole
48f0aa842e Start refactoring budget repositories. 2019-08-29 21:33:12 +02:00
James Cole
9d8625df3b Fix some pointers in test code [skip ci] 2019-08-29 21:32:50 +02:00
James Cole
19feefda2d Improve test coverage. 2019-08-29 17:53:25 +02:00
James Cole
91b6b86202 Fix #2512 2019-08-29 08:55:23 +02:00
James Cole
f4eca2d4ae Test new category code. 2019-08-28 17:01:48 +02:00
James Cole
5fb7635100 Remove double lines from code. 2019-08-28 16:28:14 +02:00
James Cole
fa706d27d8 Refactor category repositories. 2019-08-28 12:28:23 +02:00
James Cole
e11af70f99 Retire some old code. 2019-08-27 14:45:14 +02:00
James Cole
0daed6529d Refactor methods in category repositories. 2019-08-27 12:11:46 +02:00
James Cole
e5269bb312 Refactor and split category repository. 2019-08-27 10:52:07 +02:00
James Cole
7eb9086a28 Make budget tables multi-currency 2019-08-27 10:25:23 +02:00
James Cole
33d0b8ca22 Fix #2501 2019-08-27 07:51:34 +02:00
James Cole
7720519076 Fix for #2500 2019-08-27 07:28:39 +02:00
James Cole
57961de214 Fix for #2499 2019-08-27 07:26:32 +02:00
James Cole
d1eab98281 Fix #2498 2019-08-27 07:20:46 +02:00
James Cole
e37abeb36d Fix #2493 2019-08-27 07:17:22 +02:00
James Cole
49fc6fbd38 Fix #2492 2019-08-27 07:10:51 +02:00
James Cole
467a04ad78 Fix #2490 2019-08-27 07:01:09 +02:00
James Cole
7ef1098ab4 Fix #2488 2019-08-27 06:57:30 +02:00
James Cole
d050c0269a Fix #2487 2019-08-27 06:55:12 +02:00
James Cole
9e4f6abf5d Fix #2485 2019-08-27 06:52:54 +02:00
James Cole
c41ae3a9bf Fix #2484 2019-08-27 06:45:35 +02:00
James Cole
4ff8b3b556 Make sure user interface works for new recurring transactions. 2019-08-27 06:36:16 +02:00
James Cole
ce5fcbbda2 Remove bill references. 2019-08-27 06:12:08 +02:00
James Cole
e308b0f617 Make sure tags work, make sure update is refreshed. 2019-08-27 06:08:30 +02:00
James Cole
e209766ad0 Can also show and update notes. 2019-08-27 05:57:58 +02:00
James Cole
f2c2d4e126 Special string for skip field. 2019-08-27 05:57:28 +02:00
James Cole
96caf3491e Copy method for validation. 2019-08-27 05:57:09 +02:00
James Cole
a67bbaa1eb No date requirements for new recurrence. 2019-08-27 05:55:46 +02:00
James Cole
2b40b60d01 Push empty object {} and recurrence doesn't change. #2483 2019-08-26 19:09:55 +02:00
James Cole
f9dc58c3a8 Post new recurrence meta (piggy, tags) works. #2483 2019-08-26 18:44:04 +02:00
James Cole
c2a57a457b New upgrade command for recurring transactions #2483 2019-08-26 18:38:23 +02:00
James Cole
ac64bac558 Error about not submitting a repetition is given under "repetitions". #2483 2019-08-26 18:15:44 +02:00
James Cole
f884880ab8 Error about not submitting a transaction is given under "transactions". #2483 2019-08-26 18:15:21 +02:00
James Cole
5e04237a2c Experiment for #2489 2019-08-26 17:38:26 +02:00
James Cole
339ccbc5f8 Put meta data in correct array, make sure edit screen works. #2483 2019-08-26 08:59:43 +02:00
James Cole
1f3c621bea Make sure version is OK. Will trigger the commands again but no matter. 2019-08-26 07:13:48 +02:00
James Cole
a49e20c2aa New command to migrate recurrence meta data. 2019-08-26 07:12:46 +02:00
James Cole
8978904fdd Edit index to match transformer. 2019-08-26 07:12:32 +02:00
James Cole
5e0d9bddba Forgot to remove a line. 2019-08-26 07:12:20 +02:00
James Cole
96222fdcea Make the chunks larger. 2019-08-26 07:12:01 +02:00
James Cole
05bee57932 Second fix for #2480 2019-08-25 16:36:26 +02:00
James Cole
1e00d7cb16 Merge category and budget info with the transaction array #2483 2019-08-25 16:27:17 +02:00
James Cole
6bc5794dfd Rename recurrence_repetitions and nr_of_repetitions. 2019-08-25 16:15:02 +02:00
James Cole
1f9fddaa32 Drop transaction_type_id, rename transaction_type #2483 2019-08-25 16:13:47 +02:00
James Cole
5d68fab374 Fix #2480 2019-08-25 16:04:49 +02:00
James Cole
8680254503 Can now leave out actions and triggers.
If added though, all data must be present. #2477
2019-08-25 07:55:21 +02:00
James Cole
c0909aebba Rule controller can handle empty submission. #2477
The rule repository can't handle this (yet).
2019-08-25 07:48:46 +02:00
James Cole
e16adca336 Consistency for #2477 2019-08-25 07:40:14 +02:00
James Cole
aa1b9fa5a5 Rules will no longer list the "user-action" trigger
Rules will have a "moment" field that says either "update-journal" or "store-journal".
2019-08-25 07:35:26 +02:00
James Cole
af2f085aa7 Disable the encryption of uploads, in line with other efforts not to encrypt local data. 2019-08-25 07:25:01 +02:00
James Cole
6b86a35ffb Fix for broken bills. 2019-08-24 13:23:36 +02:00
James Cole
7c5e10de33 And now also make sure that old meta data isn't changed. 2019-08-24 08:22:46 +02:00
James Cole
d836c8217d No longer have to submit mandatory fields to account end point. Just submit the field you wish to update, the rest will be untouched. 2019-08-24 07:56:08 +02:00
James Cole
e5e3797d9c Better sum for bill view. 2019-08-23 21:54:47 +02:00
James Cole
4b45c0d0a9 Possible fix for #2439 in develop 2019-08-23 19:37:53 +02:00
James Cole
afcf5f76ef Fix #2470 2019-08-23 19:27:55 +02:00
James Cole
5ecb7b0c07 Fix #2470 2019-08-23 19:25:26 +02:00
James Cole
a86e956dbb Force PHP 7.3, break anything before PHP 7.3 2019-08-23 18:55:14 +02:00
James Cole
731ef7b13c Merge pull request #2471 from hamuz/patch-1
Add date attribute to the list row
2019-08-23 15:34:19 +02:00
HamuZ HamuZ
2c0ad7047d Add date attribute to the list row
Used to be there previously (at least at 4.7.8). Used in my automation.
Similar to other views: https://github.com/firefly-iii/firefly-iii/search?q=data-date&unscoped_q=data-date
2019-08-23 16:32:42 +03:00
James Cole
682ad4c5aa Merge tag '4.8.0.3' into develop
4.8.0.3
2019-08-23 14:25:19 +02:00
James Cole
f090a9534f Merge branch 'release/4.8.0.3' 2019-08-23 14:25:17 +02:00
James Cole
ad289bc8cd Updated composer file. 2019-08-23 14:11:26 +02:00
James Cole
6f5b6e536d Updated version. 2019-08-23 14:11:18 +02:00
James Cole
c810bad97c New translations. 2019-08-23 14:11:10 +02:00
James Cole
a172c51495 Update changelog. 2019-08-23 13:54:51 +02:00
James Cole
633c776ebf Fix for #2438 2019-08-23 13:53:05 +02:00
James Cole
19bd295c71 Version update. 2019-08-23 13:52:36 +02:00
James Cole
3b39fb93e7 Fix tag overview. 2019-08-23 13:09:38 +02:00
James Cole
e3a338db0e Fix some view issues reported in #2436 2019-08-23 13:06:00 +02:00
James Cole
02df24bbc9 Possible fix for #2437 2019-08-23 09:41:31 +02:00
James Cole
4f6ba1e706 Re-order two columns #2437 2019-08-23 06:42:49 +02:00
James Cole
a1f57a0949 Fix #2467 2019-08-23 06:40:48 +02:00
James Cole
3d444eb833 Fix #2465 and #2463 2019-08-23 06:10:03 +02:00
James Cole
57b2a8d459 Add a note 2019-08-23 06:03:40 +02:00
James Cole
8a06507003 Fix #2459 2019-08-22 19:14:26 +02:00
James Cole
04d7137dff More code for #2457 2019-08-22 19:10:57 +02:00
James Cole
a2be71499f Add autocomplete #2457 2019-08-22 19:07:01 +02:00
James Cole
effba42ac2 Typo fix in budget helper and add debug info. #2451 2019-08-22 18:07:42 +02:00
James Cole
aea8603ca1 Typo fix in budget helper and add debug info. #2451 2019-08-22 18:07:33 +02:00
James Cole
fdb2abdf92 Some notes for future releases. #2443 2019-08-22 18:04:26 +02:00
James Cole
4b1d66bcf6 Merge pull request #2444 from GeoffreyFrogeye/availablebudgets
Fix wrong indexes in BudgetRepository
2019-08-22 18:03:34 +02:00
James Cole
248e393405 Merge pull request #2442 from GeoffreyFrogeye/currencycharlimit
Fix consistency in symbol character limit
2019-08-22 17:58:12 +02:00
James Cole
967ea6a181 Another fix for #2440 2019-08-22 17:56:48 +02:00
James Cole
a11f876c49 Fix return type #2440 2019-08-22 17:09:08 +02:00
James Cole
2a4051fe92 Fix for #2439 2019-08-22 17:06:43 +02:00
James Cole
10737d10a5 Merge pull request #2458 from jlauwers/patch-1
typo in readme
2019-08-22 04:43:42 +02:00
James Cole
6f75e0df3c Fix for #2451 2019-08-21 18:07:15 +02:00
James Cole
24a2238134 Fix #2456 2019-08-21 18:01:57 +02:00
James Cole
c7931f2b72 Fix for #2460 2019-08-21 17:27:59 +02:00
Jonathan
852584b6a8 typo in readme 2019-08-21 08:39:21 +02:00
James Cole
21a9e981a2 Fix for #2446 2019-08-21 05:15:47 +02:00
James Cole
ecc37b2ed3 Some undocumented changes for #2445 and #2447 2019-08-21 04:59:35 +02:00
James Cole
c0935e192d Fix #2448 2019-08-21 04:46:17 +02:00
James Cole
92a2404b61 Fix for invoice date #2449 2019-08-21 04:45:00 +02:00
James Cole
d9a2bd3e5f Update composer file with new command. 2019-08-21 04:37:27 +02:00
James Cole
22f110df8d Round the search time and make sure the count is shown #2453 2019-08-21 04:37:17 +02:00
James Cole
c76337926b Fix general docker file. 2019-08-21 04:18:37 +02:00
Geoffrey “Frogeye” Preud'homme
bf57dec07c Fix wrong indexes in BudgetRepository
Closes #2443
2019-08-18 17:58:42 +02:00
Geoffrey “Frogeye” Preud'homme
10d760a614 Fix consistency in symbol character limit
Closes #2441
2019-08-18 17:16:10 +02:00
James Cole
7b813065da Remove log debug things. 2019-08-18 13:01:47 +02:00
James Cole
c218a12af7 New command to make sure opening balance currency information is correct. 2019-08-18 13:01:38 +02:00
James Cole
98ae0efb16 SQL issue in piggy banks 2019-08-18 11:19:25 +02:00
James Cole
98cff18efa Fix email message for new transactions 2019-08-18 11:16:33 +02:00
James Cole
a7b2fbbf10 Budget box is back. 2019-08-18 09:07:08 +02:00
James Cole
ccc12171d6 Make sure all account meta data is deleted properly. 2019-08-18 09:00:15 +02:00
James Cole
525f69cf63 Inform user on currency disabling and where a currency may still be used. #2432 2019-08-18 08:46:36 +02:00
James Cole
4d2c5c1b58 Build both images using base image. 2019-08-18 08:10:30 +02:00
James Cole
450d07580e Push build from other base image. 2019-08-18 07:16:30 +02:00
James Cole
246100f2b7 Debug info for #2437 2019-08-17 21:02:02 +02:00
James Cole
9974e0229d Merge tag '4.8.0.2' into develop
4.8.0.2
2019-08-17 12:38:06 +02:00
James Cole
ad44f99dbf Merge branch 'release/4.8.0.2' 2019-08-17 12:38:04 +02:00
James Cole
12a64fefc8 Fix test. 2019-08-17 12:31:55 +02:00
James Cole
2ee8cbbae4 Update change logs and files for new version. 2019-08-17 12:27:15 +02:00
James Cole
34c19b145c Updated strings. 2019-08-17 12:24:16 +02:00
James Cole
c5e047ea02 Update copyright 2019-08-17 12:13:02 +02:00
James Cole
fc78c32fca Add newline to files 2019-08-17 12:09:03 +02:00
James Cole
b53cbbe469 Fix test coverage. 2019-08-17 12:08:09 +02:00
James Cole
1d4434698e Fix some tests. 2019-08-17 11:32:05 +02:00
James Cole
6d1bfd3956 Code cleanup 2019-08-17 10:54:16 +02:00
James Cole
1974d5f1e3 Code cleanup 2019-08-17 10:48:28 +02:00
James Cole
c235e42d6c Bill report URL must be set. 2019-08-17 10:48:09 +02:00
James Cole
342985d52a Code cleanup 2019-08-17 10:47:51 +02:00
James Cole
c2296c3ad5 Code cleanup 2019-08-17 10:47:29 +02:00
James Cole
23479790fe Code cleanup 2019-08-17 10:47:10 +02:00
James Cole
44823c6fec Code cleanup 2019-08-17 10:46:55 +02:00
James Cole
acfdf7dc90 Code cleanup 2019-08-17 10:46:40 +02:00
James Cole
6038a68ba9 Code cleanup 2019-08-17 10:46:32 +02:00
James Cole
79cf61b653 Fix #2434 2019-08-17 08:29:35 +02:00
James Cole
b97d3d3627 And fix sorting. 2019-08-17 08:04:41 +02:00
James Cole
7f887e294a Fix some charts. 2019-08-17 08:00:27 +02:00
James Cole
6f78735bc5 Fix some report issues. 2019-08-17 07:47:39 +02:00
James Cole
3e242aaca6 Clean up login controller. 2019-08-17 06:35:45 +02:00
James Cole
0796e422d4 Cleanup showLoginForm() 2019-08-17 06:24:49 +02:00
James Cole
fbd7c9ae07 Remove logout method (is the same) 2019-08-17 06:23:12 +02:00
James Cole
5cfb1b63dc Remove inspection warnings. 2019-08-17 06:22:42 +02:00
James Cole
f09d0e87e4 Remove inspection, add TODO's, make code a bit simpler. 2019-08-16 21:38:35 +02:00
James Cole
820358af73 Remove unused code. 2019-08-16 21:23:20 +02:00
James Cole
5a2998c80e Simplify bill overview. 2019-08-16 21:21:38 +02:00
James Cole
a32df0066e Fix form inconsistencies. 2019-08-16 19:08:20 +02:00
James Cole
02db333d46 Update for balance box in report. 2019-08-16 17:54:38 +02:00
James Cole
070f46c755 Budget box is multi-currency. 2019-08-16 08:27:08 +02:00
James Cole
b4a732bf77 Namespace errors, spotted by @davids3 #2392 2019-08-16 07:24:04 +02:00
James Cole
d8eb59736e Improve report boxes one by one #2428 2019-08-16 06:21:10 +02:00
James Cole
41c15b0cf8 Forgot auth in API 2019-08-16 06:20:16 +02:00
James Cole
fdf99400bc Some TODO's for the future. 2019-08-16 06:20:07 +02:00
James Cole
1ba45afeab Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2019-08-16 05:22:24 +02:00
James Cole
1819ece55d Merge pull request #2429 from GeoffreyFrogeye/emd_date
Fix typo in BudgetRepository (emd_date)
2019-08-16 05:15:50 +02:00
James Cole
78335210db Cleanup API middleware. 2019-08-16 05:08:56 +02:00
Geoffrey “Frogeye” Preud'homme
ca23bb6272 Fix typo in BudgetRepository (emd_date) 2019-08-15 23:23:06 +02:00
James Cole
eed84e18bb Fix #2423 2019-08-14 20:25:38 +02:00
James Cole
12641f96b1 Fix #2422 2019-08-14 20:23:11 +02:00
James Cole
1b97c4d58f Fix date for recurring transactions #2407 2019-08-14 20:06:16 +02:00
James Cole
ee3a2ef41c Fix report issues. 2019-08-14 19:51:46 +02:00
James Cole
084ceb0c4e Report fixes for #2418 2019-08-14 19:06:05 +02:00
James Cole
2497c4ee5c Fix #2426 2019-08-14 17:54:15 +02:00
James Cole
757a118f87 Change comment [skip ci] 2019-08-14 06:16:44 +02:00
James Cole
97908932e6 Fix #2393 2019-08-13 19:01:30 +02:00
James Cole
4c73ad8306 Fix #2402 2019-08-13 18:50:18 +02:00
James Cole
10a2078661 Fix #2404 2019-08-13 18:45:44 +02:00
James Cole
3fb09136cf Fix #2410 2019-08-13 18:38:15 +02:00
James Cole
843cced454 Fix #2405 2019-08-13 16:45:04 +02:00
James Cole
71a501868f Fix docker build back to original script. 2019-08-13 16:26:27 +02:00
James Cole
0a0ad8200a Fix #2414 2019-08-13 16:24:36 +02:00
James Cole
a9baf54b12 Fix #2416 2019-08-13 16:02:28 +02:00
James Cole
ed565136fc Fix #2415 2019-08-13 16:00:35 +02:00
James Cole
4719b87314 Try the ARM thing with my own version of the script. 2019-08-12 19:53:54 +02:00
James Cole
24b30af018 Install extension. 2019-08-12 19:31:42 +02:00
James Cole
e9f4695355 Fix ARM script. 2019-08-12 18:44:57 +02:00
James Cole
5b969fa014 Fix LDAP in ARM 2019-08-12 18:34:32 +02:00
James Cole
499e713683 Remove unused methods. 2019-08-12 18:24:33 +02:00
James Cole
5eadb51b78 Some refactoring. 2019-08-12 18:19:13 +02:00
James Cole
713a962005 Log errors instead of giving exceptions 2019-08-12 18:19:06 +02:00
James Cole
d96e77a3ec Use new methods from category repository 2019-08-12 18:18:51 +02:00
James Cole
bc68c367c8 Refactor earnedInperiod and spentInPeriod 2019-08-12 18:18:26 +02:00
James Cole
33e241d39a Remove initial balance accounts from auto-complete. 2019-08-12 18:17:43 +02:00
James Cole
e67812bf64 Method is no longer used. 2019-08-12 18:17:15 +02:00
James Cole
1090ce6597 Remove sum, make multi-currency 2019-08-12 18:16:28 +02:00
James Cole
e358f19548 Make method static 2019-08-12 18:16:12 +02:00
James Cole
eac406d62d Merge tag '4.8.0.1' into develop
4.8.0.1
2019-08-12 18:12:03 +02:00
James Cole
142a488ab3 Merge branch 'release/4.8.0.1' 2019-08-12 18:12:02 +02:00
James Cole
893c83e086 Fix tests. 2019-08-12 17:36:37 +02:00
James Cole
76dad84ba6 Updated translations. 2019-08-12 17:12:11 +02:00
James Cole
6e78d4efa7 Update version. 2019-08-12 17:11:05 +02:00
James Cole
66ffbf9e47 Rename version so Ctrl-F doesn't pick them up 2019-08-12 17:10:58 +02:00
James Cole
c499899988 Update version and changelog. 2019-08-12 17:10:33 +02:00
James Cole
77ea246fc3 New files for future release. 2019-08-12 16:59:23 +02:00
James Cole
80f9896f2a Remove unused lines. 2019-08-12 16:58:45 +02:00
James Cole
90c60e55f7 Fix #2401 2019-08-12 16:54:48 +02:00
James Cole
b085ee3437 Fix #2395 2019-08-11 19:14:13 +02:00
James Cole
3b3f24fe56 Fix #2399 2019-08-11 18:58:30 +02:00
James Cole
1d2c834b2c Code for #2397 2019-08-11 17:32:00 +02:00
James Cole
cc243f9fb7 Remove unused methods. 2019-08-11 07:29:13 +02:00
James Cole
788bde5562 Fix some tag related issues. 2019-08-11 07:29:05 +02:00
James Cole
f67a9547cc Box is now positive. 2019-08-11 07:26:06 +02:00
James Cole
8545d73119 Fix tests 2019-08-11 07:25:59 +02:00
James Cole
c4964cf603 Merge pull request #2382 from mfix22/patch-1
Fix ranger.yml config
2019-08-11 07:25:30 +02:00
James Cole
903b4e520c New Docker files. 2019-08-10 17:21:58 +02:00
James Cole
c0033ae56b Code cleanup in AccountForm. 2019-08-10 17:11:57 +02:00
James Cole
3daddd690f Fix all views. 2019-08-10 16:50:37 +02:00
James Cole
0d9bae6ec2 Fix the references for AccountForm. 2019-08-10 16:36:15 +02:00
James Cole
6e2978231b Refactor the expandedform methods. First commit to see how Scrutinizer likes this. This commit will break most views. 2019-08-10 15:09:44 +02:00
James Cole
0097c66522 Refactor journal repositories. 2019-08-10 14:41:08 +02:00
James Cole
93f1854be0 Refactor journal repository and fix tests. 2019-08-10 13:42:33 +02:00
James Cole
b7f3c53688 Fix #2388 2019-08-10 11:17:23 +02:00
James Cole
94b8bb8f66 Cleanup journal repository 2019-08-10 08:42:46 +02:00
James Cole
edb038c822 Update composer file. 2019-08-10 08:42:14 +02:00
James Cole
b6a9204c4f See what happens in the build now. 2019-08-10 07:19:37 +02:00
James Cole
1d1b335cac Fix #2384 2019-08-10 07:18:18 +02:00
James Cole
0516065f5a Cleanup todo's [skip ci] 2019-08-10 07:05:19 +02:00
James Cole
606f33ceeb Add disclaimer for Firefly III analytics code. 2019-08-10 07:05:07 +02:00
James Cole
cb1db06a7c Remove unnecessary loops. 2019-08-10 07:04:54 +02:00
James Cole
7b0ccdbdb4 Split request 2019-08-10 07:04:31 +02:00
James Cole
55cf9fa9d0 Update link in readme. 2019-08-10 06:16:58 +02:00
James Cole
b3156e9798 New command for php config in ARM image. 2019-08-10 06:16:50 +02:00
Michael Fix
8e51b3edef Fix ranger.yml config 2019-08-09 13:40:11 -07:00
James Cole
6f8b1f3b9d Fix link in readme [skip ci] 2019-08-09 21:06:59 +02:00
James Cole
3b81b7a904 Merge tag '4.8.0' into develop
4.8.0
2019-08-09 21:03:15 +02:00
James Cole
81cc138700 Merge branch 'release/4.8.0' 2019-08-09 21:03:12 +02:00
James Cole
5b908b77f3 Final changelog things. Also triggers a build. 2019-08-09 21:02:46 +02:00
James Cole
a248544641 Some more last-minute fixes. 2019-08-09 20:33:57 +02:00
James Cole
b09504d0f7 Fix broken duplication test routine. 2019-08-09 18:35:34 +02:00
James Cole
1e3d85439e Last minute fixes in test code and UI 2019-08-09 18:06:43 +02:00
James Cole
392317b01f Meta data fixes. 2019-08-09 18:06:16 +02:00
James Cole
9d73fb8193 Update donation link [skip ci] 2019-08-09 06:14:51 +02:00
James Cole
64427ef004 New icons [skip ci] 2019-08-09 06:13:40 +02:00
James Cole
fd2f4e1459 Fix test coverage. 2019-08-09 05:58:52 +02:00
James Cole
dcbc2ca0c3 Update JS dependencies. 2019-08-09 05:46:50 +02:00
James Cole
55a5838b3c Small update to readme [skip ci] 2019-08-08 18:09:07 +02:00
James Cole
d7a3fcf26d Update changelog and readme. 2019-08-08 18:00:44 +02:00
James Cole
c8c8fce55f Update language stuff 2019-08-08 17:52:46 +02:00
James Cole
c32f3254c5 Make sure 2FA works as expected. 2019-08-08 17:52:37 +02:00
James Cole
3dff8dfaf8 Warning if you change your language. 2019-08-08 17:08:36 +02:00
James Cole
53c7c6685b New language! 2019-08-08 17:05:13 +02:00
James Cole
564f00561e New language! 2019-08-08 17:04:17 +02:00
James Cole
c27db00550 Updated strings. 2019-08-08 17:04:06 +02:00
James Cole
717d1a3612 Getting close enough for inclusion. 2019-08-08 17:03:19 +02:00
James Cole
9bd238b45f Switch some fields. 2019-08-07 18:52:42 +02:00
James Cole
d81128d8c6 Fix help for transaction thing. 2019-08-07 18:51:35 +02:00
James Cole
bdf76cf9b2 Reference .env file. 2019-08-06 19:24:25 +02:00
James Cole
aabf8ce8cc Fix redirect in link controller. 2019-08-06 19:23:18 +02:00
James Cole
8bcf8095a9 Switch around text [skip ci] 2019-08-06 05:42:15 +02:00
James Cole
efc6b0e45a Add a todo [skip ci] 2019-08-06 05:39:05 +02:00
James Cole
00d785d891 New middleware that should make sure the new forms redirect as well. 2019-08-05 19:45:20 +02:00
James Cole
e37100ae97 Sync between command line and FF 2019-08-05 17:07:19 +02:00
James Cole
a077d58e9c Update libraries, add some views. 2019-08-04 19:54:31 +02:00
James Cole
cc56a981cd Fix problem with type of transaction. 2019-08-04 17:26:00 +02:00
James Cole
2147caf3ef Fix tests 2019-08-04 11:12:24 +02:00
James Cole
62b5cf04ad Refactor tests and code to handle new 2FA methods. 2019-08-04 10:27:37 +02:00
James Cole
d3be043aa7 Some experimental docker changes. 2019-08-04 08:01:13 +02:00
James Cole
933f02d1d9 Extra upgrade text. 2019-08-04 07:43:01 +02:00
James Cole
02c92318fb One command to upgrade, not 27 2019-08-04 07:41:32 +02:00
James Cole
a328d393d3 Clear cache 2019-08-04 07:29:31 +02:00
James Cole
a06868b0c3 Add element for cash account. 2019-08-04 07:29:25 +02:00
James Cole
d38766e5db Firefly III can generate new backup codes. 2019-08-04 07:21:11 +02:00
James Cole
e41211bed7 Can use backup codes to login. 2019-08-04 07:10:18 +02:00
James Cole
0b6c3efe8d Can now disable MFA 2019-08-04 07:10:05 +02:00
James Cole
616d921bef Can disable MFA 2019-08-04 07:09:51 +02:00
James Cole
ea52a52022 New MFA submit routine with history. 2019-08-04 07:00:47 +02:00
James Cole
cc76be1aad Enable the creation of a MFA token in users auth app, and store the MFA secret in profile. 2019-08-03 20:09:09 +02:00
James Cole
2554840714 Index can now handle new MFA method. 2019-08-03 19:57:24 +02:00
James Cole
e1e351aad7 Add new MFA middleware 2019-08-03 19:54:30 +02:00
James Cole
e9de7bbf15 Disable the MFA middleware. 2019-08-03 19:53:30 +02:00
James Cole
2bf3bdb3de Basic MFA view 2019-08-03 19:49:38 +02:00
James Cole
0a2bc99630 Republish configuration file, and edit it. 2019-08-03 19:49:32 +02:00
James Cole
7f1bd19e45 remove "twoFactorAuthEnabled" from preferences. Kill switches for all code that references them (easier for refactor) 2019-08-03 19:46:12 +02:00
James Cole
f8d2292fa8 Upgrade libraries. 2019-08-03 19:35:20 +02:00
James Cole
b0bd35503f Add MFA code in prep of new MFA routine 2019-08-03 19:19:55 +02:00
James Cole
0b8427f881 Add some TODO's, refactor some code. 2019-08-03 19:17:59 +02:00
James Cole
cf121fea50 Improve test coverage. 2019-08-03 14:45:37 +02:00
James Cole
75c2529d3e Improve test coverage. 2019-08-03 10:50:43 +02:00
James Cole
b8b59b13a7 Allow bread crumb for bills. 2019-08-03 08:33:05 +02:00
James Cole
12c1fa2367 Add new search keywords 2019-08-03 08:32:55 +02:00
James Cole
febaab62f7 Various fixes. Sorry, lazy day. 2019-08-03 06:27:56 +02:00
James Cole
43dbad4e4c Merge tag '4.7.17.6' into develop
4.7.17.6

# Conflicts:
#	.travis.yml
#	app/Http/Controllers/Import/JobConfigurationController.php
#	app/Http/Controllers/Transaction/SingleController.php
#	config/firefly.php
2019-08-03 05:11:58 +02:00
James Cole
2089ee14e9 Merge branch 'hotfix/4.7.17.6' 2019-08-03 05:10:21 +02:00
James Cole
ec8e1d5a7a Version update 2019-08-03 05:10:06 +02:00
James Cole
2d7494f8cd Fix similar XSS issues. 2019-08-03 05:08:35 +02:00
James Cole
8717f469b1 Fix #2370 2019-08-03 05:08:20 +02:00
James Cole
40dd2e0f7f Improve test coverage. 2019-08-03 04:46:47 +02:00
James Cole
00dee03709 Update version again. 2019-08-02 21:21:38 +02:00
James Cole
ca9f8b56f7 Merge tag '4.7.17.5' into develop
4.7.17.5

# Conflicts:
#	resources/views/v1/transactions/convert.twig
2019-08-02 20:59:57 +02:00
James Cole
3435cb937c Merge tag '4.7.17.4' into develop
4.7.17.4

# Conflicts:
#	config/firefly.php
#	resources/views/v1/transactions/convert.twig
2019-08-02 17:10:55 +02:00
James Cole
6ac08de71d First steps for reconciliation fix. 2019-08-02 16:31:36 +02:00
James Cole
494f783a2b Fix #2333 2019-08-02 08:28:51 +02:00
James Cole
59801b8bc1 Fix #2332 2019-08-02 05:49:20 +02:00
James Cole
3dbde59787 Fix #2331 2019-08-02 05:44:51 +02:00
James Cole
ac903b88ba Fix search 2019-08-02 05:32:30 +02:00
James Cole
fc70afa3ea Improve test coverage. 2019-08-02 05:25:24 +02:00
James Cole
4bd8e1b11e Fix #2328 2019-08-02 05:24:51 +02:00
James Cole
04bf92d946 Fix #2188 2019-08-01 21:07:40 +02:00
James Cole
eb2ee0c683 Extend cases for #2179 2019-08-01 17:19:32 +02:00
James Cole
324e0e7e30 Fix #2361 2019-08-01 17:17:26 +02:00
James Cole
81dce5d7c7 Fix #2179 2019-08-01 06:22:07 +02:00
James Cole
b049ca27f1 Improve test coverage. 2019-08-01 06:21:44 +02:00
James Cole
9b574ce7ad Improve test coverage. 2019-07-31 16:53:09 +02:00
James Cole
5524941c90 Update rule handling and views. 2019-07-27 15:01:13 +02:00
James Cole
67c0ef6ec6 Improve test coverage. 2019-07-27 13:54:06 +02:00
James Cole
d94d34ca63 Expand test coverage. 2019-07-26 17:48:24 +02:00
James Cole
6ff4a0b45c Improve test coverage. 2019-07-25 14:19:49 +02:00
James Cole
ee95606ec0 Improve test coverage. 2019-07-24 19:02:41 +02:00
James Cole
226e2f7185 Improve test coverage. 2019-07-23 17:33:23 +02:00
James Cole
1e7e0facf1 Improve test coverage. 2019-07-22 19:23:14 +02:00
James Cole
b7a4b0fdfd Refactored a lot of tests. 2019-07-21 17:15:06 +02:00
James Cole
5242c0368b Fixes #2153 2019-07-21 06:02:02 +02:00
James Cole
8f9ba21f4d Fix test coverage. 2019-07-20 22:32:40 +02:00
James Cole
58d370a893 Fix #1860 2019-07-20 22:32:32 +02:00
James Cole
3b8e95fcca Fix #1652 2019-07-20 16:48:35 +02:00
James Cole
889b7e9a18 Improve mass controller and test controllers. 2019-07-20 16:02:50 +02:00
James Cole
6d34cfb940 Implemented new link thing. 2019-07-20 06:47:34 +02:00
James Cole
63832b31f8 Improve test coverage 2019-07-20 06:22:37 +02:00
James Cole
4de537ce76 New code for edit transaction, and some tests. 2019-07-19 16:08:42 +02:00
James Cole
a42992efb0 Merge tag '4.7.17.3' into develop
4.7.17.3

# Conflicts:
#	changelog.md
#	config/firefly.php
2019-07-16 19:24:07 +02:00
James Cole
2772a1bb3b Now supports file uploads 2019-07-15 21:00:35 +02:00
James Cole
0263e2b720 Remove unused extension. 2019-07-15 19:27:03 +02:00
James Cole
5d81de6117 Merge tag '4.7.17.2' into develop
4.7.17.2

# Conflicts:
#	app/Support/Twig/Extension/Transaction.php
#	changelog.md
#	config/firefly.php
2019-07-15 19:14:06 +02:00
James Cole
806ea2edbd Merge tag '4.7.17.1' into develop
4.7.17.1

# Conflicts:
#	changelog.md
#	config/firefly.php
2019-07-15 16:51:50 +02:00
James Cole
24efe9a096 Attempt to fix uploads. 2019-07-13 20:58:00 +02:00
James Cole
2210b8054d Fix Google Ana;ytics. 2019-07-13 20:57:29 +02:00
James Cole
3eb695f2ad Fix test for convert controller. 2019-07-13 20:57:06 +02:00
James Cole
7fd3f77c3e Make sure the convert controller works again. 2019-07-05 19:43:16 +02:00
James Cole
3c5c14ff5a Refactored the bulk edit controller. 2019-07-04 17:31:47 +02:00
James Cole
54623061d8 Make sure the date is passed on when running the cron job. 2019-07-02 06:11:06 +02:00
James Cole
5bbe1eab7c Expand test coverage and improve transaction management code. 2019-07-01 20:22:35 +02:00
James Cole
94acb50a6f Update meta files. 2019-07-01 20:21:54 +02:00
James Cole
6197c77303 Improve recurrences 2019-06-29 19:47:40 +02:00
James Cole
947b83cbd1 Improve test coverage. 2019-06-29 19:47:31 +02:00
James Cole
003d07504f Improve test coverage. 2019-06-29 08:14:28 +02:00
James Cole
cf904eb677 Big bunch of code to improve test coverage. 2019-06-25 19:24:01 +02:00
James Cole
6d68020cf4 Merge pull request #2318 from SanderKleykens/feature/ingbelgium
CSV file fix for ING Belgium
2019-06-24 08:07:28 +02:00
Sander Kleykens
9e9e5bd6dd CSV file fix for ING Belgium
Parse the description and opposing account information (IBAN and name) from ING Belgium CSV files.
2019-06-23 22:24:52 +02:00
James Cole
43d753e5bd Improve test coverage and efficiency for accounts and budgets. 2019-06-23 11:13:36 +02:00
James Cole
8f25562923 Refactor fiscal helper in tests. 2019-06-23 10:40:46 +02:00
James Cole
29158acfff New language string. 2019-06-23 10:39:59 +02:00
James Cole
c98d2187a0 Removed unused test file. 2019-06-23 10:38:54 +02:00
James Cole
b3349f1f9d New test suite 2019-06-23 05:53:10 +02:00
James Cole
9f50c5db3d Finalise account tests 2019-06-23 05:53:01 +02:00
James Cole
311659ba0d Move command around. 2019-06-23 05:52:33 +02:00
James Cole
956ec23d3c Remove unnecessary slash from in_array() 2019-06-22 13:09:25 +02:00
James Cole
940a730827 Fix reconciliation. 2019-06-22 13:09:11 +02:00
James Cole
2710a30a7c Warn about expensive code in test environment. 2019-06-22 10:25:57 +02:00
James Cole
0f70cc5780 Improve account CRUD and tests. 2019-06-22 10:25:34 +02:00
James Cole
150818e673 Do composer update for new commands. 2019-06-22 10:24:26 +02:00
James Cole
317fea6cb9 Remove unused collector. 2019-06-22 10:24:16 +02:00
James Cole
abf70a235a Debug the account-create controller. 2019-06-22 05:51:32 +02:00
James Cole
74a3d155b0 Rename some fields in account overviews. 2019-06-21 19:10:24 +02:00
James Cole
c72cc2482a New command in all lists. 2019-06-21 19:10:14 +02:00
James Cole
2d3d7f7720 Some generic code refactoring. 2019-06-21 19:10:02 +02:00
James Cole
fb1af395f9 New command and test 2019-06-21 19:07:11 +02:00
James Cole
f3123802a9 Composer update. 2019-06-21 19:06:56 +02:00
James Cole
b2628a290b make sure rules fire for events. 2019-06-21 19:06:50 +02:00
James Cole
47c2d0eaf1 Expand unit tests 2019-06-21 19:05:36 +02:00
James Cole
e80cde86d6 Remove dead code. 2019-06-21 19:05:28 +02:00
James Cole
72c0d7a874 Improve test coverage and quality 2019-06-16 13:16:46 +02:00
James Cole
1ce1a84c9e Sync some translations. 2019-06-16 13:16:26 +02:00
James Cole
3a34037f30 Sync some translations. 2019-06-16 13:16:18 +02:00
James Cole
bc33d1b67d Renamed various fields from their old camel casing to new ones. 2019-06-16 13:16:04 +02:00
James Cole
23d7abd55f Cleanup expected and unexpected bugs in the factories. 2019-06-16 13:15:32 +02:00
James Cole
4a1e56671b New PHPUnit config 2019-06-16 13:15:06 +02:00
James Cole
5d1cfc661f Optimise test code. 2019-06-13 18:07:49 +02:00
James Cole
162c8af6ca Update some meta files. 2019-06-13 18:07:38 +02:00
James Cole
b46f641b62 New funding doc for GitHub 2019-06-13 18:03:33 +02:00
James Cole
6964424bdc Finish command tests 2019-06-13 15:48:35 +02:00
James Cole
6bcb2ec144 Expand test coverage 2019-06-13 07:17:31 +02:00
James Cole
aacd218056 Improve test coverage. 2019-06-13 06:39:05 +02:00
James Cole
6fdfa722dd Remove having clause for #2306 2019-06-12 05:39:02 +02:00
James Cole
5f768a0525 Fix budget limit test, and a bug in the other currencies corrector. 2019-06-11 21:21:50 +02:00
James Cole
b632405a11 Fix for #2306 2019-06-11 21:04:17 +02:00
James Cole
9e2a2bca0a Remove references to old command + remove old command. 2019-06-11 20:10:59 +02:00
James Cole
6675749349 Split and simplify command. 2019-06-11 20:09:13 +02:00
James Cole
2ab9d2e6ee Improve test coverage. 2019-06-10 20:14:00 +02:00
James Cole
8efb73694d Full API coverage. 2019-06-09 15:28:54 +02:00
James Cole
d95544d588 Improve code coverage for rules. 2019-06-09 10:27:23 +02:00
James Cole
c02ab6f6d8 Fix route calls [skip ci] 2019-06-09 10:27:11 +02:00
James Cole
2b76b4a2b2 Re-implement transaction and transaction link tests. 2019-06-09 09:58:55 +02:00
James Cole
851c4d2907 Clean up code [skip ci] 2019-06-09 09:41:23 +02:00
James Cole
42bd5d05b5 Renaming methods must also be included in api.php 2019-06-09 09:40:21 +02:00
James Cole
9d0a5c97c2 Implement rule group and rule tests 2019-06-09 09:40:08 +02:00
James Cole
a1929b0521 Suppress PHPMD messages and make sure to use route() [skip ci] 2019-06-09 09:39:56 +02:00
James Cole
57cfd7e3bc Add separate request classes for rule groups and triggers. 2019-06-09 09:39:23 +02:00
James Cole
55345fa931 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2019-06-09 08:26:29 +02:00
James Cole
3c2dfc52bc API updates. 2019-06-09 08:26:23 +02:00
James Cole
73e32efd79 Merge pull request #2303 from JurajMlich/master
Correct ratesapi URL
2019-06-09 08:25:50 +02:00
Juraj Mlich
ced12ca83f Correct ratesapi URL 2019-06-08 16:04:37 +02:00
James Cole
85f9c256a1 Refactor some code for recurrences. 2019-06-08 06:19:21 +02:00
James Cole
7c2c24d330 Rename env variables. 2019-06-08 06:18:55 +02:00
James Cole
3681bf258b Composer update 2019-06-08 06:18:43 +02:00
James Cole
8b5551fc26 Replace \get_class with get_class 2019-06-07 18:20:15 +02:00
James Cole
779650f63d Deprecate the export function. 2019-06-07 18:19:24 +02:00
James Cole
9c5df6ab6e Clean up some code. 2019-06-07 18:13:54 +02:00
James Cole
fba3cb6d90 Remove unnecessary backslash 2019-06-07 17:58:11 +02:00
James Cole
e4a9abc315 Clean up repositories and cron code. 2019-06-07 17:57:46 +02:00
James Cole
a845cb9af9 Removed some todo's. 2019-06-05 19:38:28 +02:00
James Cole
9b7835c9ed Various API updates. 2019-06-04 20:42:11 +02:00
James Cole
6a6d67f2b4 Split group update now works. 2019-06-02 16:33:25 +02:00
James Cole
f46834e203 Remove some serious duplicates. 2019-06-02 06:39:54 +02:00
James Cole
2a2f29533d Some improvements, also edit screen. 2019-06-01 20:38:18 +02:00
James Cole
0a6e20eae4 Remove last references 2019-05-31 13:36:13 +02:00
James Cole
e15c35de64 Migrated all code to group collector. 2019-05-31 13:35:33 +02:00
James Cole
eb6329e556 Various code cleanup as suggested by PHPStorm. 2019-05-30 12:39:06 +02:00
James Cole
8b7e87ae57 Big refactor to remove the deprecated transaction collector. 2019-05-30 12:31:19 +02:00
James Cole
10a6ff9bf8 make sure reports work as expected. 2019-05-30 06:23:25 +02:00
James Cole
bdf48227bb Merge branch 'v480' into develop
# Conflicts:
#	public/v1/js/app.js
2019-05-29 22:03:43 +02:00
James Cole
ad32c30f82 Changes for the merge with 4.8.0. 2019-05-29 22:03:07 +02:00
James Cole
9e235ac5c8 Final fix before we merge back into develop and wreck havoc on everybody. 2019-05-29 22:02:15 +02:00
James Cole
bff156aad4 Add copyright things. 2019-05-29 21:56:39 +02:00
James Cole
7e2159d12c Removed a lot of references to the old collector. 2019-05-29 21:52:08 +02:00
James Cole
280b2efee6 New translations 2019-05-29 18:30:52 +02:00
James Cole
d13317095f Replace transaction collector. 2019-05-29 18:28:28 +02:00
James Cole
627ef09f11 Can store and submit transactions. 2019-05-25 10:04:56 +02:00
James Cole
3ec7336d5c Add some stuff for edge cases. 2019-05-25 09:17:46 +02:00
James Cole
47e0f6ed89 Bug catching in form. 2019-05-24 05:47:26 +02:00
James Cole
8f1928c933 Error reporting in new form. 2019-05-24 05:29:04 +02:00
James Cole
695244c928 Bug fix in budget search 2019-05-24 05:28:41 +02:00
James Cole
6e9128d894 Catch errors, prep to render them. 2019-05-17 06:52:16 +02:00
James Cole
9a53f17fff Fix for #2270 2019-05-16 19:37:32 +02:00
James Cole
91183a91b6 Will make submit form possible. 2019-05-12 19:21:14 +02:00
James Cole
7aef52870f Make sure all custom fields are included in form. 2019-05-12 13:46:20 +02:00
James Cole
5d09d7e923 Add custom component for date. 2019-05-12 07:40:24 +02:00
James Cole
fd28589395 Fix model and add debug info. 2019-05-11 05:32:09 +02:00
James Cole
c0029af929 Update composer lock file. 2019-05-09 20:05:52 +02:00
James Cole
43a17d8676 Lots of new code and first submission thing. 2019-05-08 19:57:07 +02:00
James Cole
4c8f26f25e Remove encryption parameter 2019-05-05 16:16:03 +02:00
James Cole
9015a5e0c1 Updated lock files. 2019-05-05 08:09:43 +02:00
James Cole
8676764513 Remove various sort routines. 2019-05-04 20:58:43 +02:00
James Cole
d5c5fa4fad Lots of new code for new transaction screen. 2019-05-04 20:58:11 +02:00
James Cole
912fe99981 New code for overview. Temp submit. 2019-04-19 07:00:19 +02:00
James Cole
4d3af1dcde Basic list, no functionalities. 2019-04-18 20:05:40 +02:00
James Cole
66c55b7bbe Improve tests, models and views. 2019-04-16 16:20:46 +02:00
James Cole
5ac39dbdef Clean up tests, test only the important things. 2019-04-12 04:53:18 +02:00
James Cole
6f063a134f Reset some code. 2019-04-11 06:06:25 +02:00
James Cole
966186cccd Disable all tests that may need some work in 4.8.0 2019-04-10 19:03:33 +02:00
James Cole
784d990e20 Remove slash from method call. 2019-04-09 20:05:20 +02:00
James Cole
80896b7181 Some code optimisations. 2019-04-09 15:42:25 +02:00
James Cole
97726c3822 Disable all kinds of tests until upgrades are complete. 2019-04-09 15:32:48 +02:00
James Cole
63070bffc3 Merge branch 'develop' into v480
* develop:
  Fix #2204
  CSV file fix for Belfius
2019-04-09 07:51:11 +02:00
James Cole
b3e48ede70 Fix #2204 2019-04-08 20:40:12 +02:00
James Cole
13afd4582f First attempt at displaying a group. 2019-04-08 20:31:31 +02:00
James Cole
4eb8b1a7da Merge pull request #2209 from SanderKleykens/feature/belfius
CSV file fix for Belfius
2019-04-08 11:39:39 +02:00
Sander Kleykens
251e9f9ba1 CSV file fix for Belfius
Correct the description for outgoing recurring transactions in Belfius CSV files
2019-04-06 20:47:35 +02:00
James Cole
c7bf167f81 New stale config. 2019-04-06 11:50:16 +02:00
James Cole
c07ca12574 Merge branch 'develop' into v480
* develop:
  docs: Fix minor typo
  docs: Fix minor typo
  Experimental Ranger config.
2019-04-06 11:09:58 +02:00
James Cole
bc735e3a59 Update scripts. 2019-04-06 11:09:14 +02:00
James Cole
47fdf4b1a2 Validate account info 2019-04-06 11:08:46 +02:00
James Cole
c519b4d0df Is now capable of updating transactions over the API. 2019-04-06 08:10:50 +02:00
James Cole
846fb57520 Merge pull request #2206 from eddybrando/patch-1
docs: Fix minor typo
2019-03-31 15:16:17 +02:00
Eddybrando Vásquez
88001f4bc4 docs: Fix minor typo
Use proper verb tense
2019-03-31 14:46:18 +02:00
James Cole
b692cccdfb User can submit new journal through API. 2019-03-31 13:36:49 +02:00
James Cole
50a071119b Merge pull request #2205 from eddybrando/patch-1
docs: Fix minor typo
2019-03-30 21:54:24 +01:00
Eddybrando Vásquez
66d275a90d docs: Fix minor typo
Replace "you're" with "your".
2019-03-30 21:46:07 +01:00
James Cole
c07ef3658b Refactor installer. 2019-03-30 11:03:39 +01:00
James Cole
636eeffaed Experimental Ranger config. 2019-03-30 07:39:16 +01:00
James Cole
5b1fb5354e Update API and transaction components. 2019-03-30 07:09:52 +01:00
James Cole
484ed6a585 Introduce group collector to API 2019-03-25 15:14:09 +01:00
James Cole
11e537810a Merge branch 'develop' into v480
* develop:
  Update Docker files slightly (yes I am a nitpicker).
  - Fixed copying Dockerfile.amd64 to Dockerfile.arm in the 2nd last commit
  entrypoint.sh: Wait for DB to start up
  Misc Optimizations: Changes on Dockerfile*

# Conflicts:
#	.deploy/docker/entrypoint.sh
2019-03-24 14:56:08 +01:00
James Cole
cc67445f35 Update Docker files slightly (yes I am a nitpicker). 2019-03-24 14:54:46 +01:00
James Cole
c4ceb9d2cd Merge pull request #2192 from hulloanson/docker-wait-db
Docker's entrypoint.sh: wait for DB before running DB-dependent artisan commands
2019-03-24 14:53:10 +01:00
James Cole
bc43ea2c25 Update version etc. 2019-03-24 14:52:45 +01:00
James Cole
c946a4040f First working version of the group collector. 2019-03-24 14:48:12 +01:00
hulloanson
f5c415f079 - Fixed copying Dockerfile.amd64 to Dockerfile.arm in the 2nd last commit 2019-03-24 18:21:04 +08:00
hulloanson
638f361479 entrypoint.sh: Wait for DB to start up
- Added wait-for-it.sh

- use wait-for-it.sh to wait for pgsql / mysql DB before running
`artisan` commands
2019-03-24 18:00:25 +08:00
hulloanson
ff23898c83 Misc Optimizations: Changes on Dockerfile*
- Moved lenghty docker-php-ext-install before adding contents of
FIREFLY_PATH to reduce build time after minor changes in FIREFLY_PATH
2019-03-24 17:59:17 +08:00
James Cole
d94b23b15d Build a new collector and first view online. 2019-03-24 09:23:36 +01:00
James Cole
fb304de75e Final commands. 2019-03-24 09:23:10 +01:00
James Cole
5f76b563dc Update composer stuff. 2019-03-24 09:23:00 +01:00
James Cole
ce30375341 Refactor upgrade and verify commands. 2019-03-23 18:58:06 +01:00
James Cole
1b0be2a47e Refactor upgrade and verify commands. 2019-03-23 08:10:59 +01:00
James Cole
a89be86ca4 Merge branch 'develop' into v480
* develop:
  Update config for Romanian.
  German string
  Romanian strings.
  Fix #2166
2019-03-22 18:29:52 +01:00
James Cole
f84655e339 Update config for Romanian. 2019-03-22 18:29:34 +01:00
James Cole
a0cead6548 German string 2019-03-22 18:27:05 +01:00
James Cole
4dbc5f1413 Romanian strings. 2019-03-22 18:26:46 +01:00
James Cole
943620c035 Fix #2166 2019-03-22 06:56:01 +01:00
James Cole
a1268ffd39 Command updates. 2019-03-20 18:47:51 +01:00
James Cole
fcd98b4d33 Revamp upgrade commands. 2019-03-20 18:31:00 +01:00
James Cole
e411d7a825 Improve upgrade command structure. 2019-03-18 16:53:05 +01:00
James Cole
3545d894fd Improve factories and tests. 2019-03-18 16:52:49 +01:00
James Cole
200a4b18a8 First full implementation of new storage routine. 2019-03-17 17:05:16 +01:00
James Cole
6bd2b4f288 Merge branch 'develop' into v480
* develop: (21 commits)
  Update lock file
  Update change logs and config files.
  Enable norsk, update version of DB
  Various language string updates.
  Norwegian strings.
  Improve installer middleware for Sandstorm.
  Fix some issues with importer #2166
  Other delete thing.
  More debug things.
  Extra debug info for #2159 and some kernel changes.
  Extra debug info for #2159
  Fix #2173
  Rename class and add copyright statement @wrouesnel #2167
  Fix LDAP auth configuration paths.
  Fix some cache issues and a version bump.
  Updated file list.
  Updated list.
  New file list.
  Update composer file.
  Small fix in changelog.
  ...
2019-03-17 12:34:36 +01:00
James Cole
c4d57af936 Merge tag '4.7.17' into develop
4.7.17
2019-03-17 09:40:14 +01:00
James Cole
431cf08401 Various improvements. 2019-03-08 05:47:51 +01:00
James Cole
e6d7c0ddbd Merge branch 'develop' into v480
* develop:
  Fix test for PHPUnit8
  Update version and changelog.
  Fix issue with bill display
  Update language strings.
2019-03-06 13:43:11 +01:00
James Cole
e4fb223f77 Code for 4.8.0 2019-03-05 17:26:49 +01:00
1475 changed files with 115271 additions and 73448 deletions

View File

@@ -1,185 +0,0 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=${FF_APP_ENV}
# Set to true if you want to see debug information in error screens.
APP_DEBUG=${APP_DEBUG}
# This should be your email address
SITE_OWNER=${SITE_OWNER}
# The encryption key for your database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=${FF_APP_KEY}
# Change this value to your preferred time zone.
# Example: Europe/Amsterdam
TZ=${TZ}
# This variable must match your installation's external address but keep in mind that
# it's only used on the command line as a fallback value.
APP_URL=${APP_URL}
# TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy.
TRUSTED_PROXIES=${TRUSTED_PROXIES}
# The log channel defines where your log entries go to.
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
LOG_CHANNEL=stdout
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=${APP_LOG_LEVEL}
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# For other database types, please see the FAQ: http://firefly-iii.readthedocs.io/en/latest/support/faq.html
DB_CONNECTION=${FF_DB_CONNECTION}
DB_HOST=${FF_DB_HOST}
DB_PORT=${FF_DB_PORT}
DB_DATABASE=${FF_DB_NAME}
DB_USERNAME=${FF_DB_USER}
DB_PASSWORD="${FF_DB_PASSWORD}"
# PostgreSQL supports SSL. You can configure it here.
PGSQL_SSL=${PGSQL_SSL}
PGSQL_SSL_MODE=${PGSQL_SSL_MODE}
PGSQL_SSL_ROOT_CERT=${PGSQL_SSL_ROOT_CERT}
PGSQL_SSL_CERT=${PGSQL_SSL_CERT}
PGSQL_SSL_KEY=${PGSQL_SSL_KEY}
PGSQL_SSL_CRL_FILE=${PGSQL_SSL_CRL_FILE}
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
# You can configure another file storage backend if you cannot use the local storage option.
# To set this up, fill in the following variables. The upload path is used to store uploaded
# files and the export path is to store exported data (before download).
SFTP_HOST=${SFTP_HOST}
SFTP_PORT=${SFTP_PORT}
SFTP_UPLOAD_PATH=${SFTP_UPLOAD_PATH}
SFTP_EXPORT_PATH=${SFTP_EXPORT_PATH}
# SFTP uses either the username/password combination or the private key to authenticate.
SFTP_USERNAME=${SFTP_USERNAME}
SFTP_PASSWORD="${SFTP_PASSWORD}"
SFTP_PRIV_KEY=${SFTP_PRIV_KEY}
# Cookie settings. Should not be necessary to change these.
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
# If you want Firefly III to mail you, update these settings
# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html
MAIL_DRIVER=${MAIL_DRIVER}
MAIL_HOST=${MAIL_HOST}
MAIL_PORT=${MAIL_PORT}
MAIL_FROM=${MAIL_FROM}
MAIL_USERNAME=${MAIL_USERNAME}
MAIL_PASSWORD="${MAIL_PASSWORD}"
MAIL_ENCRYPTION=${MAIL_ENCRYPTION}
# Other mail drivers:
MAILGUN_DOMAIN=${MAILGUN_DOMAIN}
MAILGUN_SECRET=${MAILGUN_SECRET}
MANDRILL_SECRET=${MANDRILL_SECRET}
SPARKPOST_SECRET=${SPARKPOST_SECRET}
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=false
# These messages contain (sensitive) transaction information:
SEND_REPORT_JOURNALS=${SEND_REPORT_JOURNALS}
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=${MAPBOX_API_KEY}
# Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
CER_PROVIDER=${CER_PROVIDER}
# If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
# the free API up to the point where you might as well offer nothing.
FIXER_API_KEY=${FIXER_API_KEY}
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID=${ANALYTICS_ID}
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=true
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "ldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://firefly-iii.readthedocs.io/en/latest/installation/authentication.html
LOGIN_PROVIDER=${LOGIN_PROVIDER}
# LDAP connection configuration
ADLDAP_CONNECTION_SCHEME=${ADLDAP_CONNECTION_SCHEME}
ADLDAP_AUTO_CONNECT=${ADLDAP_AUTO_CONNECT}
# LDAP connection settings
ADLDAP_CONTROLLERS=${ADLDAP_CONTROLLERS}
ADLDAP_PORT=${ADLDAP_PORT}
ADLDAP_TIMEOUT=${ADLDAP_TIMEOUT}
ADLDAP_BASEDN="${ADLDAP_BASEDN}"
ADLDAP_FOLLOW_REFFERALS=${ADLDAP_FOLLOW_REFFERALS}
ADLDAP_USE_SSL=${ADLDAP_USE_SSL}
ADLDAP_USE_TLS=${ADLDAP_USE_TLS}
ADLDAP_ADMIN_USERNAME=${ADLDAP_ADMIN_USERNAME}
ADLDAP_ADMIN_PASSWORD="${ADLDAP_ADMIN_PASSWORD}"
ADLDAP_ACCOUNT_PREFIX="${ADLDAP_ACCOUNT_PREFIX}"
ADLDAP_ACCOUNT_SUFFIX="${ADLDAP_ACCOUNT_SUFFIX}"
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=${ADLDAP_PASSWORD_SYNC}
ADLDAP_LOGIN_FALLBACK=${ADLDAP_LOGIN_FALLBACK}
ADLDAP_DISCOVER_FIELD=${ADLDAP_DISCOVER_FIELD}
ADLDAP_AUTH_FIELD=${ADLDAP_AUTH_FIELD}
# Will allow SSO if your server provides an AUTH_USER field.
WINDOWS_SSO_DISCOVER=${WINDOWS_SSO_DISCOVER}
WINDOWS_SSO_KEY=${WINDOWS_SSO_KEY}
# field to sync as local username.
ADLDAP_SYNC_FIELD=${ADLDAP_SYNC_FIELD}
# You can disable the X-Frame-Options header if it interfears with tools like
# Organizr. This is at your own risk.
DISABLE_FRAME_HEADER=${DISABLE_FRAME_HEADER}
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
ADLDAP_CONNECTION=default
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=true
IS_SANDSTORM=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
FFIII_LAYOUT=v1

View File

@@ -3,8 +3,6 @@
# build image # build image
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
if [ "$TRAVIS_BRANCH" == "develop" ]; then if [ "$TRAVIS_BRANCH" == "develop" ]; then
echo "Build develop amd64" echo "Build develop amd64"
docker build -t jc5x/firefly-iii:develop-amd64 -f Dockerfile.amd64 . docker build -t jc5x/firefly-iii:develop-amd64 -f Dockerfile.amd64 .

View File

@@ -2,7 +2,6 @@
docker run --rm --privileged multiarch/qemu-user-static:register --reset docker run --rm --privileged multiarch/qemu-user-static:register --reset
# get qemu-arm-static binary # get qemu-arm-static binary
mkdir tmp mkdir tmp
pushd tmp && \ pushd tmp && \

View File

@@ -2,8 +2,6 @@
echo "Now in entrypoint.sh for Firefly III" echo "Now in entrypoint.sh for Firefly III"
lscpu
# make sure the correct directories exists (suggested by @chrif): # make sure the correct directories exists (suggested by @chrif):
echo "Making directories..." echo "Making directories..."
mkdir -p $FIREFLY_PATH/storage/app/public mkdir -p $FIREFLY_PATH/storage/app/public
@@ -14,6 +12,7 @@ mkdir -p $FIREFLY_PATH/storage/export
mkdir -p $FIREFLY_PATH/storage/framework/cache/data mkdir -p $FIREFLY_PATH/storage/framework/cache/data
mkdir -p $FIREFLY_PATH/storage/framework/sessions mkdir -p $FIREFLY_PATH/storage/framework/sessions
mkdir -p $FIREFLY_PATH/storage/framework/testing mkdir -p $FIREFLY_PATH/storage/framework/testing
mkdir -p $FIREFLY_PATH/storage/framework/views/twig
mkdir -p $FIREFLY_PATH/storage/framework/views/v1 mkdir -p $FIREFLY_PATH/storage/framework/views/v1
mkdir -p $FIREFLY_PATH/storage/framework/views/v2 mkdir -p $FIREFLY_PATH/storage/framework/views/v2
mkdir -p $FIREFLY_PATH/storage/logs mkdir -p $FIREFLY_PATH/storage/logs
@@ -27,12 +26,6 @@ then
echo "Touched!" echo "Touched!"
fi fi
if [[ $FF_DB_CONNECTION == "sqlite" ]]
then
touch $FIREFLY_PATH/storage/database/database.sqlite
echo "Touched!"
fi
# make sure we own the volumes: # make sure we own the volumes:
echo "Run chown on ${FIREFLY_PATH}/storage..." echo "Run chown on ${FIREFLY_PATH}/storage..."
chown -R www-data:www-data -R $FIREFLY_PATH/storage chown -R www-data:www-data -R $FIREFLY_PATH/storage
@@ -43,18 +36,62 @@ chmod -R 775 $FIREFLY_PATH/storage
echo "Remove log file..." echo "Remove log file..."
rm -f $FIREFLY_PATH/storage/logs/laravel.log rm -f $FIREFLY_PATH/storage/logs/laravel.log
echo "Map environment variables on .env file..."
cat $FIREFLY_PATH/.deploy/docker/.env.docker | envsubst > $FIREFLY_PATH/.env
echo "Dump auto load..." echo "Dump auto load..."
composer dump-autoload composer dump-autoload
echo "Discover packages..." echo "Discover packages..."
php artisan package:discover php artisan package:discover
echo "Run various artisan commands..." echo "Run various artisan commands..."
if [[ -z "$DB_PORT" ]]; then
if [[ $DB_CONNECTION == "pgsql" ]]; then
DB_PORT=5432
elif [[ $DB_CONNECTION == "mysql" ]]; then
DB_PORT=3306
fi
fi
if [[ ! -z "$DB_PORT" ]]; then
$FIREFLY_PATH/.deploy/docker/wait-for-it.sh "${DB_HOST}:${DB_PORT}" -- echo "db is up. Time to execute artisan commands"
fi
#env $(grep -v "^\#" .env | xargs)
php artisan cache:clear
php artisan migrate --seed php artisan migrate --seed
php artisan firefly:decrypt-all php artisan firefly-iii:decrypt-all
php artisan firefly:upgrade-database
php artisan firefly:verify # there are 13 upgrade commands
php artisan firefly-iii:transaction-identifiers
php artisan firefly-iii:migrate-to-groups
php artisan firefly-iii:account-currencies
php artisan firefly-iii:transfer-currencies
php artisan firefly-iii:other-currencies
php artisan firefly-iii:migrate-notes
php artisan firefly-iii:migrate-attachments
php artisan firefly-iii:bills-to-rules
php artisan firefly-iii:bl-currency
php artisan firefly-iii:cc-liabilities
php artisan firefly-iii:back-to-journals
php artisan firefly-iii:rename-account-meta
php artisan firefly-iii:migrate-recurrence-meta
# there are 14 verify commands
php artisan firefly-iii:fix-piggies
php artisan firefly-iii:create-link-types
php artisan firefly-iii:create-access-tokens
php artisan firefly-iii:remove-bills
php artisan firefly-iii:enable-currencies
php artisan firefly-iii:fix-transfer-budgets
php artisan firefly-iii:fix-uneven-amount
php artisan firefly-iii:delete-zero-amount
php artisan firefly-iii:delete-orphaned-transactions
php artisan firefly-iii:delete-empty-journals
php artisan firefly-iii:delete-empty-groups
php artisan firefly-iii:fix-account-types
php artisan firefly-iii:rename-meta-fields
php artisan firefly-iii:fix-ob-currencies
# report commands
php artisan firefly-iii:report-empty-objects
php artisan firefly-iii:report-sum
php artisan passport:install php artisan passport:install
php artisan cache:clear php artisan cache:clear

178
.deploy/docker/wait-for-it.sh Executable file
View File

@@ -0,0 +1,178 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
WAITFORIT_cmdname=${0##*/}
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
else
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
fi
WAITFORIT_start_ts=$(date +%s)
while :
do
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
WAITFORIT_result=$?
else
(echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
WAITFORIT_result=$?
fi
if [[ $WAITFORIT_result -eq 0 ]]; then
WAITFORIT_end_ts=$(date +%s)
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
break
fi
sleep 1
done
return $WAITFORIT_result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
else
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
fi
WAITFORIT_PID=$!
trap "kill -INT -$WAITFORIT_PID" INT
wait $WAITFORIT_PID
WAITFORIT_RESULT=$?
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
fi
return $WAITFORIT_RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
WAITFORIT_hostport=(${1//:/ })
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
shift 1
;;
--child)
WAITFORIT_CHILD=1
shift 1
;;
-q | --quiet)
WAITFORIT_QUIET=1
shift 1
;;
-s | --strict)
WAITFORIT_STRICT=1
shift 1
;;
-h)
WAITFORIT_HOST="$2"
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
WAITFORIT_HOST="${1#*=}"
shift 1
;;
-p)
WAITFORIT_PORT="$2"
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
WAITFORIT_PORT="${1#*=}"
shift 1
;;
-t)
WAITFORIT_TIMEOUT="$2"
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
WAITFORIT_TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
WAITFORIT_CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
# check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
WAITFORIT_ISBUSY=1
WAITFORIT_BUSYTIMEFLAG="-t"
else
WAITFORIT_ISBUSY=0
WAITFORIT_BUSYTIMEFLAG=""
fi
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
wait_for
WAITFORIT_RESULT=$?
exit $WAITFORIT_RESULT
else
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
wait_for_wrapper
WAITFORIT_RESULT=$?
else
wait_for
WAITFORIT_RESULT=$?
fi
fi
if [[ $WAITFORIT_CLI != "" ]]; then
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
exit $WAITFORIT_RESULT
fi
exec "${WAITFORIT_CLI[@]}"
else
exit $WAITFORIT_RESULT
fi

View File

@@ -1,10 +1,14 @@
en_US en_US
cs_CZ
es_ES es_ES
de_DE de_DE
fr_FR fr_FR
it_IT it_IT
nb_NO
nl_NL nl_NL
pl_PL pl_PL
pt_BR pt_BR
ro_RO
ru_RU ru_RU
nb_NO hu_HU
el_GR

View File

@@ -44,17 +44,17 @@ spec:
- image: firefly-local - image: firefly-local
name: firefly-local name: firefly-local
env: env:
- name: FF_APP_ENV - name: APP_ENV
value: "local" value: "local"
- name: FF_APP_KEY - name: APP_KEY
value: "S0m3R@nd0mString0f32Ch@rsEx@ct1y" value: "S0m3R@nd0mString0f32Ch@rsEx@ct1y"
- name: FF_DB_HOST - name: DB_HOST
value: "172.17.0.9" value: "172.17.0.9"
- name: FF_DB_NAME - name: DB_NAME
value: "firefly_db" value: "firefly_db"
- name: FF_DB_USER - name: DB_USER
value: "firefly_db" value: "firefly_db"
- name: FF_DB_PASSWORD - name: DB_PASSWORD
value: "password" value: "password"
volumeMounts: volumeMounts:
- mountPath: "/var/www/firefly-iii/storage/export" - mountPath: "/var/www/firefly-iii/storage/export"

View File

@@ -8,8 +8,8 @@ APP_DEBUG=false
# This should be your email address # This should be your email address
SITE_OWNER=mail@example.com SITE_OWNER=mail@example.com
# The encryption key for your database and sessions. Keep this very secure. # The encryption key for your sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST. # If you generate a new one existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it # Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=SomeRandomStringOf32CharsExactly APP_KEY=SomeRandomStringOf32CharsExactly
@@ -22,6 +22,7 @@ TZ=Europe/Amsterdam
APP_URL=http://localhost APP_URL=http://localhost
# TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy. # TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy.
# Set it to ** and reverse proxies work just fine.
TRUSTED_PROXIES= TRUSTED_PROXIES=
# The log channel defines where your log entries go to. # The log channel defines where your log entries go to.
@@ -38,12 +39,12 @@ APP_LOG_LEVEL=notice
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III # Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# For other database types, please see the FAQ: http://firefly-iii.readthedocs.io/en/latest/support/faq.html # For other database types, please see the FAQ: http://firefly-iii.readthedocs.io/en/latest/support/faq.html
DB_CONNECTION=mysql DB_CONNECTION=pgsql
DB_HOST=127.0.0.1 DB_HOST=firefly_iii_db
DB_PORT=3306 DB_PORT=5432
DB_DATABASE=homestead DB_DATABASE=firefly
DB_USERNAME=homestead DB_USERNAME=firefly
DB_PASSWORD=secret DB_PASSWORD=secret_firefly_password
# PostgreSQL supports SSL. You can configure it here. # PostgreSQL supports SSL. You can configure it here.
PGSQL_SSL_MODE=prefer PGSQL_SSL_MODE=prefer
@@ -101,10 +102,11 @@ SEND_REPORT_JOURNALS=true
MAPBOX_API_KEY= MAPBOX_API_KEY=
# Firefly III currently supports two provider for live Currency Exchange Rates: # Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one. # "fixer", and "ratesapi".
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates, # RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key. # built compatible with Fixer.IO, based on data published by European Central Bank, and doesn't require API key.
CER_PROVIDER=fixer CER_PROVIDER=ratesapi
# If you have select "fixer" as default currency exchange rates, # If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates. # set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited # Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
@@ -114,10 +116,6 @@ FIXER_API_KEY=
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here. # If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID= ANALYTICS_ID=
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=true
# Firefly III has two options for user authentication. "eloquent" is the default, # Firefly III has two options for user authentication. "eloquent" is the default,
# and "ldap" for LDAP servers. # and "ldap" for LDAP servers.
# For full instructions on these settings please visit: # For full instructions on these settings please visit:
@@ -179,6 +177,7 @@ PUSHER_ID=
DEMO_USERNAME= DEMO_USERNAME=
DEMO_PASSWORD= DEMO_PASSWORD=
IS_DOCKER=false IS_DOCKER=false
USE_ENCRYPTION=false
IS_SANDSTORM=false IS_SANDSTORM=false
IS_HEROKU=false IS_HEROKU=false
BUNQ_USE_SANDBOX=false BUNQ_USE_SANDBOX=false

5
.github/funding.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
# These are supported funding model platforms
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: JC5
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA

8
.github/ranger.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
# in .github/ranger.yml
comments:
-
action: delete_comment
pattern: 1
-
action: delete_comment
pattern: ":+1:"

12
.github/security.md vendored Normal file
View File

@@ -0,0 +1,12 @@
# Security Policy
## Supported Versions
Only the latest version of Firefly III is supported. If you're not running the latest version of Firefly III, please upgrade at your earliest convenience.
## Reporting a Vulnerability
If you find something that compromises the security of Firefly III, you should [send me a message](mailto:thegrumpydictator@gmail.com) as soon as possible. These issues will be fixed immediately. You can also open an issue, but if you feel the issue is sensitive, please drop me a message instead.
You can use my [GPG key](https://keybase.io/jc5) for extra security. My [GitHub commits](https://github.com/firefly-iii/firefly-iii/commits/master) are almost always signed with this key.

1
.github/stale.yml vendored
View File

@@ -14,6 +14,7 @@ exemptLabels:
- feature - feature
- bug - bug
- possible-bug - possible-bug
- "possible bug"
- announcement - announcement
# Set to true to ignore issues in a project (defaults to false) # Set to true to ignore issues in a project (defaults to false)

View File

@@ -1,3 +1,162 @@
# 4.8.1 (API 0.10.2)
- Firefly III 4.8.1 requires PHP 7.3.
- Support for Greek
- [Issue 2383](https://github.com/firefly-iii/firefly-iii/issues/2383) Some tables in reports now also report percentages.
- [Issue 2389](https://github.com/firefly-iii/firefly-iii/issues/2389) Add category / budget information to transaction lists.
- [Issue 2464](https://github.com/firefly-iii/firefly-iii/issues/2464) Can now search for tag.
- [Issue 2466](https://github.com/firefly-iii/firefly-iii/issues/2466) Can order recurring transactions in a more useful manner.
- [Issue 2497](https://github.com/firefly-iii/firefly-iii/issues/2497) Transaction creation moment in hover of tag title.
- [Issue 2471](https://github.com/firefly-iii/firefly-iii/issues/2471) Added date tag to table cells.
- [Issue 2291](https://github.com/firefly-iii/firefly-iii/issues/2291) All reports are now properly multi-currency.
- [Issue 2481](https://github.com/firefly-iii/firefly-iii/issues/2481) As part of the removal of local encryption, uploads and imports are no longer encrypted.
- [Issue 2495](https://github.com/firefly-iii/firefly-iii/issues/2495) A better message of transaction submission.
- [Issue 2506](https://github.com/firefly-iii/firefly-iii/issues/2506) Some bugs in tag report fixed.
- [Issue 2510](https://github.com/firefly-iii/firefly-iii/issues/2510) All transaction descriptions cut off at 255 chars.
- Better sum in bill view.
- Clean up docker files for flawless operation.
- The bunq API has changed, and support for bunq has been disabled.
- [Issue 2470](https://github.com/firefly-iii/firefly-iii/issues/2470) Bad links for transactions.
- [Issue 2480](https://github.com/firefly-iii/firefly-iii/issues/2480) Large queries would break in SQLite.
- [Issue 2484](https://github.com/firefly-iii/firefly-iii/issues/2484) Transaction description auto-complete.
- [Issue 2487](https://github.com/firefly-iii/firefly-iii/issues/2487) Fix issues with FinTS
- [Issue 2488](https://github.com/firefly-iii/firefly-iii/issues/2488) 404 after deleting a tag.
- [Issue 2490](https://github.com/firefly-iii/firefly-iii/issues/2490) "Reset form after submission" doesn't work.
- [Issue 2492](https://github.com/firefly-iii/firefly-iii/issues/2492) After submitting and fixing an error, the error is persistent.
- [Issue 2493](https://github.com/firefly-iii/firefly-iii/issues/2493) Auto detect transaction type is a bit better now.
- [Issue 2498](https://github.com/firefly-iii/firefly-iii/issues/2498) Pressing enter in some fields breaks the form.
- [Issue 2499](https://github.com/firefly-iii/firefly-iii/issues/2499) Auto-complete issues in transaction link form.
- [Issue 2500](https://github.com/firefly-iii/firefly-iii/issues/2500) Issue when submitting edited transactions.
- [Issue 2501](https://github.com/firefly-iii/firefly-iii/issues/2501) Better error messages for empty submissions.
- [Issue 2508](https://github.com/firefly-iii/firefly-iii/issues/2508) Can remove category from transaction.
- [Issue 2516](https://github.com/firefly-iii/firefly-iii/issues/2516) Can no longer import transactions with no amount.
- [Issue 2518](https://github.com/firefly-iii/firefly-iii/issues/2518) Link in balance box goes to current period.
- [Issue 2521](https://github.com/firefly-iii/firefly-iii/issues/2521) Foreign transaction currency is hidden when the user hasn't enabled foreign currencies.
- [Issue 2522](https://github.com/firefly-iii/firefly-iii/issues/2522) Some reports were missing the "overspent" field.
- [Issue 2526](https://github.com/firefly-iii/firefly-iii/issues/2526) It was impossible to remove the budget of a transaction.
- [Issue 2527](https://github.com/firefly-iii/firefly-iii/issues/2527) Some bulk edits were buggy.
- [Issue 2539](https://github.com/firefly-iii/firefly-iii/issues/2539) Fixed a typo.
- [Issue 2545](https://github.com/firefly-iii/firefly-iii/issues/2545) Deleted tags would still show up.
- [Issue 2547](https://github.com/firefly-iii/firefly-iii/issues/2547) Changing the opening balance to 0 will now remove it.
- [Issue 2549](https://github.com/firefly-iii/firefly-iii/issues/2549) Can now clone transactions again.
- [Issue 2550](https://github.com/firefly-iii/firefly-iii/issues/2550) Added missing locales for moment.js
- [Issue 2553](https://github.com/firefly-iii/firefly-iii/issues/2553) Fixed an issue with split transactions.
- [Issue 2555](https://github.com/firefly-iii/firefly-iii/issues/2555) Better error for when you submit the same account twice.
- [Issue 2439](https://github.com/firefly-iii/firefly-iii/issues/2439) SQL error in API post new user
- ... and many other bugs.
- [Issue 2475](https://github.com/firefly-iii/firefly-iii/issues/2475) Tags are now the same for all views.
- [Issue 2476](https://github.com/firefly-iii/firefly-iii/issues/2476) Amount is now represented equally in all views.
- [Issue 2477](https://github.com/firefly-iii/firefly-iii/issues/2477) Rules are easier to update.
- [Issue 2483](https://github.com/firefly-iii/firefly-iii/issues/2483) Several consistencies fixed.
- [Issue 2484](https://github.com/firefly-iii/firefly-iii/issues/2484) Transaction link view fixed.
- [Issue 2557](https://github.com/firefly-iii/firefly-iii/issues/2557) Fix for issue in summary API
- No longer have to submit mandatory fields to account end point. Just submit the field you wish to update, the rest will be untouched.
- Rules will no longer list the "user-action" trigger Rules will have a "moment" field that says either "update-journal" or "store-journal".
# 4.8.0.3 (API 0.10.1)
- Autocomplete for transaction description.
- [Issue 2438](https://github.com/firefly-iii/firefly-iii/issues/2438) Some balance issues when working with multiple currencies (a known issue)
- [Issue 2425](https://github.com/firefly-iii/firefly-iii/issues/2425) Transaction edit/create form is weird with the enter button
- [Issue 2424](https://github.com/firefly-iii/firefly-iii/issues/2424) auto complete tab doesn't work.
- [Issue 2441](https://github.com/firefly-iii/firefly-iii/issues/2441) Inconsistent character limit for currencies.
- [Issue 2443](https://github.com/firefly-iii/firefly-iii/issues/2443) 500 error when submitting budgets
- [Issue 2446](https://github.com/firefly-iii/firefly-iii/issues/2446) Can't update current amount for piggy bank
- [Issue 2440](https://github.com/firefly-iii/firefly-iii/issues/2440) Errors when interacting with recurring transactions
- [Issue 2439](https://github.com/firefly-iii/firefly-iii/issues/2439) SQL error in API post new user
- Transaction report (after import, over email) is mostly empty
- Mass edit checkboxes doesn't work in a tag overview
- [Issue 2437](https://github.com/firefly-iii/firefly-iii/issues/2437) CPU issues when viewing accounts, probably run-away queries.
- [Issue 2432](https://github.com/firefly-iii/firefly-iii/issues/2432) Can't disable all currencies except one / can't disable EUR and switch to something else.
- Option to edit the budget is gone from edit transaction form.
- [Issue 2453](https://github.com/firefly-iii/firefly-iii/issues/2453) Search view things
- [Issue 2449](https://github.com/firefly-iii/firefly-iii/issues/2449) Can't add invoice date.
- [Issue 2448](https://github.com/firefly-iii/firefly-iii/issues/2448) Bad link in transaction overview
- [Issue 2447](https://github.com/firefly-iii/firefly-iii/issues/2447) Bad link in bill overview
- Improvements to various API end-points. Docs are updated.
# 4.8.0.2 (API 0.10.0)
- [Issue 2203](https://github.com/firefly-iii/firefly-iii/issues/2203) Reconciliation inconsistencies.
- [Issue 2392](https://github.com/firefly-iii/firefly-iii/issues/2392) Bad namespace leads to installation errors.
- [Issue 2393](https://github.com/firefly-iii/firefly-iii/issues/2393) Missing budget selector.
- [Issue 2402](https://github.com/firefly-iii/firefly-iii/issues/2402) bad amounts in default report
- [Issue 2405](https://github.com/firefly-iii/firefly-iii/issues/2405) Due date can't be edited.
- [Issue 2404](https://github.com/firefly-iii/firefly-iii/issues/2404) bad page indicator in the "no category" transaction overview.
- [Issue 2407](https://github.com/firefly-iii/firefly-iii/issues/2407) Fix recurring transaction dates
- [Issue 2410](https://github.com/firefly-iii/firefly-iii/issues/2410) Transaction links inconsistent
- [Issue 2414](https://github.com/firefly-iii/firefly-iii/issues/2414) Can't edit recurring transactions
- [Issue 2415](https://github.com/firefly-iii/firefly-iii/issues/2415) Return here + reset form results in empty transaction form
- [Issue 2416](https://github.com/firefly-iii/firefly-iii/issues/2416) Some form inconsistencies.
- [Issue 2418](https://github.com/firefly-iii/firefly-iii/issues/2418) Reports are inaccurate or broken.
- [Issue 2422](https://github.com/firefly-iii/firefly-iii/issues/2422) PHP error when matching transactions.
- [Issue 2423](https://github.com/firefly-iii/firefly-iii/issues/2423) Reports are inaccurate or broken.
- [Issue 2426](https://github.com/firefly-iii/firefly-iii/issues/2426) Inconsistent documentation and instructions.
- [Issue 2427](https://github.com/firefly-iii/firefly-iii/issues/2427) Deleted account and "initial balance" accounts may appear in dropdowns.
- [Issue 2428](https://github.com/firefly-iii/firefly-iii/issues/2428) Reports are inaccurate or broken.
- [Issue 2429](https://github.com/firefly-iii/firefly-iii/issues/2429) Typo leads to SQL errors in available budgets API
- [Issue 2431](https://github.com/firefly-iii/firefly-iii/issues/2431) Issues creating new recurring transactions.
- [Issue 2434](https://github.com/firefly-iii/firefly-iii/issues/2434) You can edit the initial balance transaction but it fails to save.
- ARM build should work now.
# 4.8.0.1 (API 0.10.0)
- The balance box on the dashboard shows only negative numbers, skewing the results.
- Selecting or using tags in new transactions results in an error.
- Editing a transaction with tags will drop the tags from the transaction.
- [Issue 2382](https://github.com/firefly-iii/firefly-iii/issues/2382) Ranger config
- [Issue 2384](https://github.com/firefly-iii/firefly-iii/issues/2384) When upgrading manually, you may see: `The command "generate-keys" does not exist.`
- [Issue 2385](https://github.com/firefly-iii/firefly-iii/issues/2385) When upgrading manually, the firefly:verify command may fail to run.
- [Issue 2388](https://github.com/firefly-iii/firefly-iii/issues/2388) When registering as a new user, leaving the opening balance at 0 will give you an error.
- [Issue 2395](https://github.com/firefly-iii/firefly-iii/issues/2395) Editing split transactions is broken.
- [Issue 2397](https://github.com/firefly-iii/firefly-iii/issues/2397) Transfers are stored the wrong way around.
- [Issue 2399](https://github.com/firefly-iii/firefly-iii/issues/2399) Not all account balances are updated after you create a new transaction.
- [Issue 2401](https://github.com/firefly-iii/firefly-iii/issues/2401) Could not delete a split from a split transaction.
# 4.8.0 (API 0.10.0)
- Hungarian translation!
- New database model that changes the concept of "split transactions";
- New installation routine with rewritten database integrity tests and upgrade code;
- Rewritten screen to create transactions which will now completely rely on the API;
- Most terminal commands now have the prefix `firefly-iii`.
- New MFA code that will generate backup codes for you and is more robust. MFA will have to be re-enabled for ALL users.
- This will probably be the last Firefly III version to have import routines for files, Bunq and others. These will be moved to separate applications that use the Firefly III API.
- The export function has been removed.
- [Issue 1652](https://github.com/firefly-iii/firefly-iii/issues/1652), new strings to use during the import.
- [Issue 1860](https://github.com/firefly-iii/firefly-iii/issues/1860), fixing the default currency not being on top in a JSON box.
- [Issue 2031](https://github.com/firefly-iii/firefly-iii/issues/2031), a fix for Triodos imports.
- [Issue 2153](https://github.com/firefly-iii/firefly-iii/issues/2153), problems with editing credit cards.
- [Issue 2179](https://github.com/firefly-iii/firefly-iii/issues/2179), consistent and correct redirect behavior.
- [Issue 2180](https://github.com/firefly-iii/firefly-iii/issues/2180), API issues with foreign amounts.
- [Issue 2187](https://github.com/firefly-iii/firefly-iii/issues/2187), bulk editing reconciled transactions was broken.
- [Issue 2188](https://github.com/firefly-iii/firefly-iii/issues/2188), redirect loop in bills
- [Issue 2189](https://github.com/firefly-iii/firefly-iii/issues/2189), bulk edit could not handle tags.
- [Issue 2203](https://github.com/firefly-iii/firefly-iii/issues/2203), [issue 2208](https://github.com/firefly-iii/firefly-iii/issues/2208), [issue 2352](https://github.com/firefly-iii/firefly-iii/issues/2352), reconciliation fixes
- [Issue 2204](https://github.com/firefly-iii/firefly-iii/issues/2204), transaction type fix
- [Issue 2211](https://github.com/firefly-iii/firefly-iii/issues/2211), mass edit fixes.
- [Issue 2212](https://github.com/firefly-iii/firefly-iii/issues/2212), bug in the API when deleting objects.
- [Issue 2214](https://github.com/firefly-iii/firefly-iii/issues/2214), could not view attachment.
- [Issue 2219](https://github.com/firefly-iii/firefly-iii/issues/2219), max amount was a little low.
- [Issue 2239](https://github.com/firefly-iii/firefly-iii/issues/2239), fixed ordering issue.
- [Issue 2246](https://github.com/firefly-iii/firefly-iii/issues/2246), could not disable EUR.
- [Issue 2268](https://github.com/firefly-iii/firefly-iii/issues/2268), could not import into liability accounts.
- [Issue 2293](https://github.com/firefly-iii/firefly-iii/issues/2293), could not trigger rule on deposits in some circumstances
- [Issue 2314](https://github.com/firefly-iii/firefly-iii/issues/2314), could not trigger rule on transfers in some circumstances
- [Issue 2325](https://github.com/firefly-iii/firefly-iii/issues/2325), some balance issues on the frontpage.
- [Issue 2328](https://github.com/firefly-iii/firefly-iii/issues/2328), some date range issues in reports
- [Issue 2331](https://github.com/firefly-iii/firefly-iii/issues/2331), some broken fields in reports.
- [Issue 2333](https://github.com/firefly-iii/firefly-iii/issues/2333), API issues with piggy banks.
- [Issue 2355](https://github.com/firefly-iii/firefly-iii/issues/2355), configuration issues with LDAP
- [Issue 2361](https://github.com/firefly-iii/firefly-iii/issues/2361), some ordering issues.
- Updated API to reflect the changes in the database.
- New API end-point for a summary of your data.
- Some new API charts.
# 4.7.17.6 (API 0.9.2)
- XSS issue in liability account redirect, found by [@0x2500](https://github.com/0x2500).
# 4.7.17.5 (API 0.9.2) # 4.7.17.5 (API 0.9.2)
- Several XSS issues, found by [@0x2500](https://github.com/0x2500). - Several XSS issues, found by [@0x2500](https://github.com/0x2500).
@@ -100,7 +259,7 @@
- [Issue 2009](https://github.com/firefly-iii/firefly-iii/issues/2009) Could not change recurrence back to "forever". - [Issue 2009](https://github.com/firefly-iii/firefly-iii/issues/2009) Could not change recurrence back to "forever".
- [Issue 2033](https://github.com/firefly-iii/firefly-iii/issues/2033) Longitude can go from -180 to 180. - [Issue 2033](https://github.com/firefly-iii/firefly-iii/issues/2033) Longitude can go from -180 to 180.
- [Issue 2034](https://github.com/firefly-iii/firefly-iii/issues/2034) Rules were not being triggered in mass-edit. - [Issue 2034](https://github.com/firefly-iii/firefly-iii/issues/2034) Rules were not being triggered in mass-edit.
- #2043 In rare instances the repetition of a recurring transaction was displayed incorrectly. - [Issue 2043](https://github.com/firefly-iii/firefly-iii/issues/2043) In rare instances the repetition of a recurring transaction was displayed incorrectly.
- Fixed broken translations in the recurring transactions overview. - Fixed broken translations in the recurring transactions overview.
- When you create a recurring transfer you make make it fill (or empty) a piggy bank. This was not working, despite a fix in 4.7.8. - When you create a recurring transfer you make make it fill (or empty) a piggy bank. This was not working, despite a fix in 4.7.8.
- Fixed a bug where the importer would not be capable of creating new currencies. - Fixed a bug where the importer would not be capable of creating new currencies.

View File

@@ -11,7 +11,7 @@ mkdir -p /var/log
mkdir -p /var/log/mysql mkdir -p /var/log/mysql
mkdir -p /var/log/nginx mkdir -p /var/log/nginx
# Wipe /var/run, since pidfiles and socket files from previous launches should go away # Wipe /var/run, since pidfiles and socket files from previous launches should go away
# TODO someday: I'd prefer a tmpfs for these. # Someday: I'd prefer a tmpfs for these.
rm -rf /var/run rm -rf /var/run
mkdir -p /var/run mkdir -p /var/run
rm -rf /var/tmp rm -rf /var/tmp

View File

@@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
manifest = ( manifest = (
appTitle = (defaultText = "Firefly III"), appTitle = (defaultText = "Firefly III"),
appVersion = 31, appVersion = 36,
appMarketingVersion = (defaultText = "4.7.17.5"), appMarketingVersion = (defaultText = "4.8.1"),
actions = [ actions = [
# Define your "new document" handlers here. # Define your "new document" handlers here.

View File

@@ -25,13 +25,14 @@ sed -i 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# zh_TW.UTF-8 UTF-8/zh_TW.UTF-8 UTF-8/g' /etc/locale.gen sed -i 's/# zh_TW.UTF-8 UTF-8/zh_TW.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/g' /etc/locale.gen sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# nb_NO.UTF-8 UTF-8/nb_NO.UTF-8 UTF-8/g' /etc/locale.gen sed -i 's/# nb_NO.UTF-8 UTF-8/nb_NO.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# ro_RO.UTF-8 UTF-8/ro_RO.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# cs_CZ.UTF-8 UTF-8/cs_CZ.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# id_ID.UTF-8 UTF-8/id_ID.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# hu_HU.UTF-8 UTF-8/hu_HU.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# el_GR.UTF-8 UTF-8/el_GR.UTF-8 UTF-8/g' /etc/locale.gen
dpkg-reconfigure --frontend=noninteractive locales dpkg-reconfigure --frontend=noninteractive locales
# actually add repository # actually add repository
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E9C74FEEA2098A6E apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E9C74FEEA2098A6E
add-apt-repository "deb http://packages.dotdeb.org jessie all" add-apt-repository "deb http://packages.dotdeb.org jessie all"

View File

@@ -1,7 +1,7 @@
sudo: required sudo: required
language: bash language: bash
env: env:
- VERSION=4.7.17.5 - VERSION=4.8.1
dist: xenial dist: xenial

View File

@@ -1,54 +1,19 @@
FROM php:7.2-apache FROM jc5x/firefly-iii-base-image:latest
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1 ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com" LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes # Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Copy in Firefly III source # Copy in Firefly III source
WORKDIR $FIREFLY_PATH WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH ADD . $FIREFLY_PATH
# copy ca certs to correct location # Ensure correct app directory permission, then `composer install`
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \ RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \ chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest composer install --prefer-dist --no-dev --no-scripts --no-suggest
# configure PHP
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
sed -i 's/max_execution_time = 30/max_execution_time = 600/' /usr/local/etc/php/php.ini && \
sed -i 's/memory_limit = 128M/memory_limit = 512M/' /usr/local/etc/php/php.ini
# Expose port 80 # Expose port 80
EXPOSE 80 EXPOSE 80

View File

@@ -1,55 +1,19 @@
FROM php:7.2-apache FROM jc5x/firefly-iii-base-image:latest
ARG ARCH
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1 ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com" LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes # Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Copy in Firefly III source # Copy in Firefly III source
WORKDIR $FIREFLY_PATH WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH ADD . $FIREFLY_PATH
# copy ca certs to correct location # Ensure correct app directory permission, then `composer install`
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \ RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \ chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest composer install --prefer-dist --no-dev --no-scripts --no-suggest
# configure PHP
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
sed -i 's/max_execution_time = 30/max_execution_time = 600/' /usr/local/etc/php/php.ini && \
sed -i 's/memory_limit = 128M/memory_limit = 512M/' /usr/local/etc/php/php.ini
# Expose port 80 # Expose port 80
EXPOSE 80 EXPOSE 80

View File

@@ -1,46 +1,17 @@
FROM arm32v7/php:7.2.8-apache-stretch FROM jc5x/firefly-iii-base-image:latest-arm
ARG ARCH
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1 ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com" LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes # Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev
# Copy in Firefly III source # Copy in Firefly III source
WORKDIR $FIREFLY_PATH WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH ADD . $FIREFLY_PATH
# copy ca certs to correct location # Ensure correct app directory permission, then `composer install`
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \ RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \ chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest composer install --prefer-dist --no-dev --no-scripts --no-suggest
# Expose port 80 # Expose port 80

View File

@@ -1,26 +1,34 @@
FROM arm32v7/php:7.2.8-apache-stretch FROM arm32v7/php:7.2-apache-stretch
ARG ARCH ARG ARCH
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1 ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com" LABEL version="1.5" maintainer="thegrumpydictator@gmail.com"
# Create volumes # Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff # Install stuff Firefly III runs with & depends on: php extensions, locales, dev headers and composer
RUN apt-get update && apt-get install -y libpng-dev \ RUN apt-get update && apt-get install -y locales unzip && apt-get clean && rm -rf /var/lib/apt/lists/*
libicu-dev \
unzip \ ADD https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/master/install-php-extensions /usr/local/bin/
gettext-base \
libldap2-dev \ RUN chmod uga+x /usr/local/bin/install-php-extensions && sync && \
libpq-dev \ install-php-extensions --cleanup bcmath ldap gd pdo_pgsql pdo_sqlite pdo_mysql intl opcache memcached
locales \
libmemcached-dev RUN a2enmod rewrite && a2enmod ssl
RUN echo "hu_HU.UTF-8 UTF-8\nro_RO.UTF-8 UTF-8\nnb_NO.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\ncs_CZ.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen
RUN locale-gen
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy in Firefly III source # Copy in Firefly III source
WORKDIR $FIREFLY_PATH WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH ADD . $FIREFLY_PATH
# Ensure correct app directory permission, then `composer install`
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# copy ca certs to correct location # copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
@@ -30,19 +38,6 @@ COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III) # Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# Expose port 80 # Expose port 80
EXPOSE 80 EXPOSE 80

View File

@@ -27,14 +27,12 @@ namespace FireflyIII\Api\V1\Controllers;
use DB; use DB;
use FireflyIII\Transformers\UserTransformer; use FireflyIII\Transformers\UserTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use League\Fractal\Manager;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Returns basic information about this installation. * Returns basic information about this installation.
* *
* @codeCoverageIgnore
* Class AboutController. * Class AboutController.
*/ */
class AboutController extends Controller class AboutController extends Controller
@@ -51,6 +49,8 @@ class AboutController extends Controller
$phpVersion = str_replace($search, $replace, PHP_VERSION); $phpVersion = str_replace($search, $replace, PHP_VERSION);
$phpOs = str_replace($search, $replace, PHP_OS); $phpOs = str_replace($search, $replace, PHP_OS);
$currentDriver = DB::getDriverName(); $currentDriver = DB::getDriverName();
$data $data
= [ = [
'version' => config('firefly.version'), 'version' => config('firefly.version'),
@@ -66,15 +66,11 @@ class AboutController extends Controller
/** /**
* Returns information about the user. * Returns information about the user.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
*/ */
public function user(Request $request): JsonResponse public function user(): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var UserTransformer $transformer */ /** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class); $transformer = app(UserTransformer::class);

View File

@@ -23,32 +23,28 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\AccountRequest; use FireflyIII\Api\V1\Requests\AccountStoreRequest;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Api\V1\Requests\AccountUpdateRequest;
use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\Transformers\PiggyBankTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class AccountController. * Class AccountController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class AccountController extends Controller class AccountController extends Controller
{ {
@@ -58,6 +54,8 @@ class AccountController extends Controller
/** /**
* AccountController constructor. * AccountController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -78,8 +76,9 @@ class AccountController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @param \FireflyIII\Models\Account $account * @param Account $account
* *
* @codeCoverageIgnore
* @return JsonResponse * @return JsonResponse
*/ */
public function delete(Account $account): JsonResponse public function delete(Account $account): JsonResponse
@@ -94,15 +93,12 @@ class AccountController extends Controller
* *
* @param Request $request * @param Request $request
* *
* @codeCoverageIgnore
* @return JsonResponse * @return JsonResponse
*/ */
public function index(Request $request): JsonResponse public function index(Request $request): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// read type from URI
$type = $request->get('type') ?? 'all'; $type = $request->get('type') ?? 'all';
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
@@ -119,8 +115,6 @@ class AccountController extends Controller
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.accounts.index') . $this->buildParams()); $paginator->setPath(route('api.v1.accounts.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
@@ -134,18 +128,18 @@ class AccountController extends Controller
/** /**
* List all of them. * List all piggies.
* *
* @param Request $request
* @param Account $account * @param Account $account
* *
* @return JsonResponse] * @return JsonResponse
* @codeCoverageIgnore
*
*/ */
public function piggyBanks(Request $request, Account $account): JsonResponse public function piggyBanks(Account $account): JsonResponse
{ {
// create some objects: // create some objects:
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -159,9 +153,6 @@ class AccountController extends Controller
$paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.accounts.piggy_banks', [$account->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.accounts.piggy_banks', [$account->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PiggyBankTransformer $transformer */ /** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class); $transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -176,16 +167,13 @@ class AccountController extends Controller
/** /**
* Show single instance. * Show single instance.
* *
* @param Request $request
* @param Account $account * @param Account $account
* *
* @return \Illuminate\Http\JsonResponse * @return JsonResponse
*/ */
public function show(Request $request, Account $account): JsonResponse public function show(Account $account): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
@@ -198,17 +186,15 @@ class AccountController extends Controller
/** /**
* Store a new instance. * Store a new instance.
* *
* @param AccountRequest $request * @param AccountStoreRequest $request
* *
* @return \Illuminate\Http\JsonResponse * @return JsonResponse
*/ */
public function store(AccountRequest $request): JsonResponse public function store(AccountStoreRequest $request): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAllAccountData();
$account = $this->repository->store($data); $account = $this->repository->store($data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
@@ -220,12 +206,15 @@ class AccountController extends Controller
} }
/** /**
* Show all transactions. * Show all transaction groups related to the account.
*
* @codeCoverageIgnore
* *
* @param Request $request * @param Request $request
* @param Account $account * @param Account $account
* *
* @return JsonResponse * @return JsonResponse
*
*/ */
public function transactions(Request $request, Account $account): JsonResponse public function transactions(Request $request, Account $account): JsonResponse
{ {
@@ -240,43 +229,31 @@ class AccountController extends Controller
} }
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
if ($this->repository->isAsset($account)) {
$collector->setAccounts(new Collection([$account]));
}
if (!$this->repository->isAsset($account)) {
$collector->setOpposingAccounts(new Collection([$account]));
}
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector->setUser($admin)->setAccounts(new Collection([$account]))
->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types);
// set range if necessary:
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.accounts.transactions', [$account->id]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ $paginator = $collector->getPaginatedGroups();
$transformer = app(TransactionTransformer::class); $paginator->setPath(route('api.v1.accounts.transactions', [$account->id]) . $this->buildParams());
$groups = $paginator->getCollection();
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($groups, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -285,19 +262,17 @@ class AccountController extends Controller
/** /**
* Update account. * Update account.
* *
* @param AccountRequest $request * @param AccountUpdateRequest $request
* @param Account $account * @param Account $account
* *
* @return \Illuminate\Http\JsonResponse * @return JsonResponse
*/ */
public function update(AccountRequest $request, Account $account): JsonResponse public function update(AccountUpdateRequest $request, Account $account): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getUpdateData();
$data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type); $data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$this->repository->update($account, $data); $this->repository->update($account, $data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);

View File

@@ -23,7 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\AttachmentRequest; use FireflyIII\Api\V1\Requests\AttachmentStoreRequest;
use FireflyIII\Api\V1\Requests\AttachmentUpdateRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
@@ -34,16 +35,14 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response as LaravelResponse; use Illuminate\Http\Response as LaravelResponse;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer; use function strlen;
/** /**
* Class AttachmentController. * Class AttachmentController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class AttachmentController extends Controller class AttachmentController extends Controller
{ {
@@ -52,6 +51,8 @@ class AttachmentController extends Controller
/** /**
* AccountController constructor. * AccountController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -71,6 +72,8 @@ class AttachmentController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @codeCoverageIgnore
*
* @param Attachment $attachment * @param Attachment $attachment
* *
* @return JsonResponse * @return JsonResponse
@@ -87,6 +90,7 @@ class AttachmentController extends Controller
* *
* @param Attachment $attachment * @param Attachment $attachment
* *
* @codeCoverageIgnore
* @return LaravelResponse * @return LaravelResponse
* @throws FireflyException * @throws FireflyException
*/ */
@@ -110,7 +114,7 @@ class AttachmentController extends Controller
->header('Expires', '0') ->header('Expires', '0')
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public') ->header('Pragma', 'public')
->header('Content-Length', \strlen($content)); ->header('Content-Length', strlen($content));
return $response; return $response;
} }
@@ -120,15 +124,12 @@ class AttachmentController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -142,9 +143,6 @@ class AttachmentController extends Controller
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.attachments.index') . $this->buildParams()); $paginator->setPath(route('api.v1.attachments.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AttachmentTransformer $transformer */ /** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class); $transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -158,17 +156,13 @@ class AttachmentController extends Controller
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param Request $request
* @param Attachment $attachment * @param Attachment $attachment
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function show(Request $request, Attachment $attachment): JsonResponse public function show(Attachment $attachment): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AttachmentTransformer $transformer */ /** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class); $transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -181,18 +175,16 @@ class AttachmentController extends Controller
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
* @param AttachmentRequest $request * @param AttachmentStoreRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
*/ */
public function store(AttachmentRequest $request): JsonResponse public function store(AttachmentStoreRequest $request): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$attachment = $this->repository->store($data); $attachment = $this->repository->store($data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AttachmentTransformer $transformer */ /** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class); $transformer = app(AttachmentTransformer::class);
@@ -206,18 +198,16 @@ class AttachmentController extends Controller
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
* @param AttachmentRequest $request * @param AttachmentUpdateRequest $request
* @param Attachment $attachment * @param Attachment $attachment
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function update(AttachmentRequest $request, Attachment $attachment): JsonResponse public function update(AttachmentUpdateRequest $request, Attachment $attachment): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$this->repository->update($attachment, $data); $this->repository->update($attachment, $data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AttachmentTransformer $transformer */ /** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class); $transformer = app(AttachmentTransformer::class);
@@ -231,6 +221,8 @@ class AttachmentController extends Controller
/** /**
* Upload an attachment. * Upload an attachment.
* *
* @codeCoverageIgnore
*
* @param Request $request * @param Request $request
* @param Attachment $attachment * @param Attachment $attachment
* *

View File

@@ -27,30 +27,28 @@ use FireflyIII\Api\V1\Requests\AvailableBudgetRequest;
use FireflyIII\Factory\TransactionCurrencyFactory; use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Transformers\AvailableBudgetTransformer; use FireflyIII\Transformers\AvailableBudgetTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class AvailableBudgetController. * Class AvailableBudgetController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class AvailableBudgetController extends Controller class AvailableBudgetController extends Controller
{ {
/** @var BudgetRepositoryInterface The budget repository */ /** @var AvailableBudgetRepositoryInterface */
private $repository; private $abRepository;
/** /**
* AccountController constructor. * AvailableBudgetController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -59,8 +57,8 @@ class AvailableBudgetController extends Controller
function ($request, $next) { function ($request, $next) {
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class); $this->abRepository = app(AvailableBudgetRepositoryInterface::class);
$this->repository->setUser($user); $this->abRepository->setUser($user);
return $next($request); return $next($request);
} }
@@ -72,11 +70,13 @@ class AvailableBudgetController extends Controller
* *
* @param AvailableBudget $availableBudget * @param AvailableBudget $availableBudget
* *
* @codeCoverageIgnore
*
* @return JsonResponse * @return JsonResponse
*/ */
public function delete(AvailableBudget $availableBudget): JsonResponse public function delete(AvailableBudget $availableBudget): JsonResponse
{ {
$this->repository->destroyAvailableBudget($availableBudget); $this->abRepository->destroyAvailableBudget($availableBudget);
return response()->json([], 204); return response()->json([], 204);
} }
@@ -84,34 +84,21 @@ class AvailableBudgetController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of available budgets. Count it and split it.
$collection = $this->repository->getAvailableBudgets();
// filter list on start and end date, if present.
// TODO: put this in the query.
$start = $this->parameters->get('start'); $start = $this->parameters->get('start');
$end = $this->parameters->get('end'); $end = $this->parameters->get('end');
if (null !== $start && null !== $end) {
$collection = $collection->filter(
function (AvailableBudget $availableBudget) use ($start, $end) {
return $availableBudget->start_date->gte($start) && $availableBudget->end_date->lte($end);
}
);
}
// get list of available budgets. Count it and split it.
$collection = $this->abRepository->getAvailableBudgetsByDate($start, $end);
$count = $collection->count(); $count = $collection->count();
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
@@ -119,9 +106,6 @@ class AvailableBudgetController extends Controller
$paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.available_budgets.index') . $this->buildParams()); $paginator->setPath(route('api.v1.available_budgets.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -135,16 +119,14 @@ class AvailableBudgetController extends Controller
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param Request $request
* @param AvailableBudget $availableBudget * @param AvailableBudget $availableBudget
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, AvailableBudget $availableBudget): JsonResponse public function show(AvailableBudget $availableBudget): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
@@ -172,10 +154,9 @@ class AvailableBudgetController extends Controller
if (null === $currency) { if (null === $currency) {
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
} }
$availableBudget = $this->repository->setAvailableBudget($currency, $data['start'], $data['end'], $data['amount']); $data['currency'] = $currency;
$manager = new Manager; $availableBudget = $this->abRepository->store($data);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager = $this->getManager();
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
@@ -214,10 +195,8 @@ class AvailableBudgetController extends Controller
$data['currency_id'] = $currency->id; $data['currency_id'] = $currency->id;
$this->repository->updateAvailableBudget($availableBudget, $data); $this->abRepository->updateAvailableBudget($availableBudget, $data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);

View File

@@ -26,27 +26,25 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BillRequest; use FireflyIII\Api\V1\Requests\BillRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\BillTransformer; use FireflyIII\Transformers\BillTransformer;
use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class BillController. * Class BillController.
*
*/ */
class BillController extends Controller class BillController extends Controller
{ {
@@ -56,6 +54,8 @@ class BillController extends Controller
/** /**
* BillController constructor. * BillController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -77,17 +77,14 @@ class BillController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
* @param Bill $bill * @param Bill $bill
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function attachments(Request $request, Bill $bill): JsonResponse public function attachments(Bill $bill): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$collection = $this->repository->getAttachments($bill); $collection = $this->repository->getAttachments($bill);
@@ -98,9 +95,6 @@ class BillController extends Controller
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.bills.attachments', [$bill->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.bills.attachments', [$bill->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AttachmentTransformer $transformer */ /** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class); $transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -117,6 +111,7 @@ class BillController extends Controller
* @param Bill $bill * @param Bill $bill
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Bill $bill): JsonResponse public function delete(Bill $bill): JsonResponse
{ {
@@ -128,20 +123,17 @@ class BillController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
$bills = $this->repository->getBills();
$manager = $this->getManager();
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$paginator = $this->repository->getPaginator($pageSize); $count = $bills->count();
/** @var Collection $bills */ $bills = $bills->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$bills = $paginator->getCollection(); $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BillTransformer $transformer */ /** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class); $transformer = app(BillTransformer::class);
@@ -156,16 +148,14 @@ class BillController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request
* @param Bill $bill * @param Bill $bill
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function rules(Request $request, Bill $bill): JsonResponse public function rules(Bill $bill): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -179,9 +169,6 @@ class BillController extends Controller
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.bills.rules', [$bill->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.bills.rules', [$bill->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -197,17 +184,14 @@ class BillController extends Controller
/** /**
* Show the specified bill. * Show the specified bill.
* *
* @param Request $request
* @param Bill $bill * @param Bill $bill
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, Bill $bill): JsonResponse public function show(Bill $bill): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BillTransformer $transformer */ /** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class); $transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -229,9 +213,7 @@ class BillController extends Controller
{ {
$bill = $this->repository->store($request->getAll()); $bill = $this->repository->store($request->getAll());
if (null !== $bill) { if (null !== $bill) {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BillTransformer $transformer */ /** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class); $transformer = app(BillTransformer::class);
@@ -253,6 +235,7 @@ class BillController extends Controller
* @param Bill $bill * @param Bill $bill
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, Bill $bill): JsonResponse public function transactions(Request $request, Bill $bill): JsonResponse
{ {
@@ -261,30 +244,39 @@ class BillController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setBills(new Collection([$bill]));
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// include source + destination account name and type.
->setBill($bill)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
// do parameter stuff on new group collector.
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types); // get paginator.
$paginator = $collector->getPaginatedTransactions(); $paginator = $collector->getPaginatedGroups();
$paginator->setPath(route('api.v1.bills.transactions', [$bill->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.bills.transactions', [$bill->id]) . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -305,9 +297,7 @@ class BillController extends Controller
{ {
$data = $request->getAll(); $data = $request->getAll();
$bill = $this->repository->update($bill, $data); $bill = $this->repository->update($bill, $data);
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BillTransformer $transformer */ /** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class); $transformer = app(BillTransformer::class);

View File

@@ -23,39 +23,42 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use Exception;
use FireflyIII\Api\V1\Requests\BudgetLimitRequest; use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
use FireflyIII\Api\V1\Requests\BudgetRequest; use FireflyIII\Api\V1\Requests\BudgetRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\Transformers\BudgetTransformer; use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class BudgetController. * Class BudgetController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class BudgetController extends Controller class BudgetController extends Controller
{ {
use TransactionFilter; use TransactionFilter;
/** @var BudgetLimitRepositoryInterface */
private $blRepository;
/** @var BudgetRepositoryInterface The budget repository */ /** @var BudgetRepositoryInterface The budget repository */
private $repository; private $repository;
/** /**
* BudgetController constructor. * BudgetController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -65,9 +68,10 @@ class BudgetController extends Controller
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var BudgetRepositoryInterface repository */
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
$this->repository->setUser($admin); $this->repository->setUser($admin);
$this->blRepository->setUser($admin);
return $next($request); return $next($request);
} }
@@ -77,25 +81,22 @@ class BudgetController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
* @param Budget $budget * @param Budget $budget
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function budgetLimits(Request $request, Budget $budget): JsonResponse public function budgetLimits(Budget $budget): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$this->parameters->set('budget_id', $budget->id); $this->parameters->set('budget_id', $budget->id);
$collection = $this->repository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); $collection = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end'));
$count = $collection->count(); $count = $collection->count();
$budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.budget_limits', [$budget->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.budgets.budget_limits', [$budget->id]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -113,6 +114,7 @@ class BudgetController extends Controller
* @param Budget $budget * @param Budget $budget
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Budget $budget): JsonResponse public function delete(Budget $budget): JsonResponse
{ {
@@ -124,15 +126,12 @@ class BudgetController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -146,9 +145,6 @@ class BudgetController extends Controller
$paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.index') . $this->buildParams()); $paginator->setPath(route('api.v1.budgets.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetTransformer $transformer */ /** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class); $transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -162,16 +158,14 @@ class BudgetController extends Controller
/** /**
* Show a budget. * Show a budget.
* *
* @param Request $request
* @param Budget $budget * @param Budget $budget
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, Budget $budget): JsonResponse public function show(Budget $budget): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetTransformer $transformer */ /** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class); $transformer = app(BudgetTransformer::class);
@@ -189,14 +183,13 @@ class BudgetController extends Controller
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
*
*/ */
public function store(BudgetRequest $request): JsonResponse public function store(BudgetRequest $request): JsonResponse
{ {
$budget = $this->repository->store($request->getAll()); $budget = $this->repository->store($request->getAll());
if (null !== $budget) { if (null !== $budget) {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetTransformer $transformer */ /** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class); $transformer = app(BudgetTransformer::class);
@@ -216,15 +209,14 @@ class BudgetController extends Controller
* @param Budget $budget * @param Budget $budget
* *
* @return JsonResponse * @return JsonResponse
* @throws Exception
*/ */
public function storeBudgetLimit(BudgetLimitRequest $request, Budget $budget): JsonResponse public function storeBudgetLimit(BudgetLimitRequest $request, Budget $budget): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$data['budget'] = $budget; $data['budget'] = $budget;
$budgetLimit = $this->repository->storeBudgetLimit($data); $budgetLimit = $this->blRepository->storeBudgetLimit($data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);
@@ -243,6 +235,7 @@ class BudgetController extends Controller
* @param Budget $budget * @param Budget $budget
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, Budget $budget): JsonResponse public function transactions(Request $request, Budget $budget): JsonResponse
{ {
@@ -258,31 +251,37 @@ class BudgetController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class); // use new group collector:
$collector->setUser($admin); /** @var GroupCollectorInterface $collector */
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); $collector = app(GroupCollectorInterface::class);
$collector->setAllAssetAccounts(); $collector
$collector->setBudget($budget); ->setUser($admin)
// filter on budget.
->setBudget($budget)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.budgets.transactions', [$budget->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.budgets.transactions', [$budget->id]) . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -304,9 +303,7 @@ class BudgetController extends Controller
{ {
$data = $request->getAll(); $data = $request->getAll();
$budget = $this->repository->update($budget, $data); $budget = $this->repository->update($budget, $data);
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetTransformer $transformer */ /** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class); $transformer = app(BudgetTransformer::class);

View File

@@ -26,37 +26,38 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BudgetLimitRequest; use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class BudgetLimitController. * Class BudgetLimitController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class BudgetLimitController extends Controller class BudgetLimitController extends Controller
{ {
use TransactionFilter; use TransactionFilter;
/** @var BudgetLimitRepositoryInterface */
private $blRepository;
/** @var BudgetRepositoryInterface The budget repository */ /** @var BudgetRepositoryInterface The budget repository */
private $repository; private $repository;
/** /**
* AccountController constructor. * BudgetLimitController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -66,7 +67,9 @@ class BudgetLimitController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
$this->repository->setUser($user); $this->repository->setUser($user);
$this->blRepository->setUser($user);
return $next($request); return $next($request);
} }
@@ -79,10 +82,11 @@ class BudgetLimitController extends Controller
* @param BudgetLimit $budgetLimit * @param BudgetLimit $budgetLimit
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(BudgetLimit $budgetLimit): JsonResponse public function delete(BudgetLimit $budgetLimit): JsonResponse
{ {
$this->repository->destroyBudgetLimit($budgetLimit); $this->blRepository->destroyBudgetLimit($budgetLimit);
return response()->json([], 204); return response()->json([], 204);
} }
@@ -93,11 +97,11 @@ class BudgetLimitController extends Controller
* @param Request $request * @param Request $request
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(Request $request): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$budgetId = (int)($request->get('budget_id') ?? 0); $budgetId = (int)($request->get('budget_id') ?? 0);
$budget = $this->repository->findNull($budgetId); $budget = $this->repository->findNull($budgetId);
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -105,10 +109,10 @@ class BudgetLimitController extends Controller
$collection = new Collection; $collection = new Collection;
if (null === $budget) { if (null === $budget) {
$collection = $this->repository->getAllBudgetLimits($this->parameters->get('start'), $this->parameters->get('end')); $collection = $this->blRepository->getAllBudgetLimits($this->parameters->get('start'), $this->parameters->get('end'));
} }
if (null !== $budget) { if (null !== $budget) {
$collection = $this->repository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); $collection = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end'));
} }
$count = $collection->count(); $count = $collection->count();
@@ -116,8 +120,6 @@ class BudgetLimitController extends Controller
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams()); $paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -131,16 +133,14 @@ class BudgetLimitController extends Controller
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param Request $request
* @param BudgetLimit $budgetLimit * @param BudgetLimit $budgetLimit
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, BudgetLimit $budgetLimit): JsonResponse public function show(BudgetLimit $budgetLimit): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);
@@ -158,6 +158,7 @@ class BudgetLimitController extends Controller
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
*
*/ */
public function store(BudgetLimitRequest $request): JsonResponse public function store(BudgetLimitRequest $request): JsonResponse
{ {
@@ -167,10 +168,8 @@ class BudgetLimitController extends Controller
throw new FireflyException('Unknown budget.'); throw new FireflyException('Unknown budget.');
} }
$data['budget'] = $budget; $data['budget'] = $budget;
$budgetLimit = $this->repository->storeBudgetLimit($data); $budgetLimit = $this->blRepository->storeBudgetLimit($data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);
@@ -188,6 +187,7 @@ class BudgetLimitController extends Controller
* @param BudgetLimit $budgetLimit * @param BudgetLimit $budgetLimit
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, BudgetLimit $budgetLimit): JsonResponse public function transactions(Request $request, BudgetLimit $budgetLimit): JsonResponse
{ {
@@ -196,27 +196,35 @@ class BudgetLimitController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class); // use new group collector:
$collector->setUser($admin); /** @var GroupCollectorInterface $collector */
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); $collector = app(GroupCollectorInterface::class);
$collector->setAllAssetAccounts(); $collector
$collector->setBudget($budgetLimit->budget); ->setUser($admin)
// filter on budget.
->setBudget($budgetLimit->budget)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date); $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types); $collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions(); $paginator = $collector->getPaginatedGroups();
$paginator->setPath(route('api.v1.budget_limits.transactions', [$budgetLimit->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.budget_limits.transactions', [$budgetLimit->id]) . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -237,10 +245,8 @@ class BudgetLimitController extends Controller
{ {
$data = $request->getAll(); $data = $request->getAll();
$data['budget'] = $budgetLimit->budget; $data['budget'] = $budgetLimit->budget;
$budgetLimit = $this->repository->updateBudgetLimit($budgetLimit, $data); $budgetLimit = $this->blRepository->updateBudgetLimit($budgetLimit, $data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);

View File

@@ -25,28 +25,23 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\CategoryRequest; use FireflyIII\Api\V1\Requests\CategoryRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\CategoryTransformer; use FireflyIII\Transformers\CategoryTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class CategoryController. * Class CategoryController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class CategoryController extends Controller class CategoryController extends Controller
{ {
@@ -56,6 +51,8 @@ class CategoryController extends Controller
/** /**
* CategoryController constructor. * CategoryController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -80,6 +77,7 @@ class CategoryController extends Controller
* @param Category $category * @param Category $category
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Category $category): JsonResponse public function delete(Category $category): JsonResponse
{ {
@@ -91,15 +89,12 @@ class CategoryController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -113,9 +108,6 @@ class CategoryController extends Controller
$paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.categories.index') . $this->buildParams()); $paginator->setPath(route('api.v1.categories.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var CategoryTransformer $transformer */ /** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class); $transformer = app(CategoryTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -131,16 +123,14 @@ class CategoryController extends Controller
/** /**
* Show the category. * Show the category.
* *
* @param Request $request
* @param Category $category * @param Category $category
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, Category $category): JsonResponse public function show(Category $category): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var CategoryTransformer $transformer */ /** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class); $transformer = app(CategoryTransformer::class);
@@ -163,9 +153,7 @@ class CategoryController extends Controller
{ {
$category = $this->repository->store($request->getAll()); $category = $this->repository->store($request->getAll());
if (null !== $category) { if (null !== $category) {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var CategoryTransformer $transformer */ /** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class); $transformer = app(CategoryTransformer::class);
@@ -186,6 +174,7 @@ class CategoryController extends Controller
* @param Category $category * @param Category $category
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, Category $category): JsonResponse public function transactions(Request $request, Category $category): JsonResponse
{ {
@@ -194,34 +183,37 @@ class CategoryController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setCategory($category);
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on category.
->setCategory($category)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types); $paginator = $collector->getPaginatedGroups();
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.categories.transactions', [$category->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.categories.transactions', [$category->id]) . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -242,9 +234,7 @@ class CategoryController extends Controller
{ {
$data = $request->getAll(); $data = $request->getAll();
$category = $this->repository->update($category, $data); $category = $this->repository->update($category, $data);
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var CategoryTransformer $transformer */ /** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class); $transformer = app(CategoryTransformer::class);

View File

@@ -26,22 +26,22 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Api\V1\Requests\DateRequest;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\ApiSupport;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/** /**
* Class AccountController * Class AccountController
*/ */
class AccountController extends Controller class AccountController extends Controller
{ {
use ApiSupport;
/** @var CurrencyRepositoryInterface */ /** @var CurrencyRepositoryInterface */
private $currencyRepository; private $currencyRepository;
/** @var AccountRepositoryInterface */ /** @var AccountRepositoryInterface */
@@ -49,6 +49,8 @@ class AccountController extends Controller
/** /**
* AccountController constructor. * AccountController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -69,22 +71,19 @@ class AccountController extends Controller
} }
/** /**
* @param Request $request * @param DateRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function expenseOverview(Request $request): JsonResponse public function expenseOverview(DateRequest $request): JsonResponse
{ {
// parameters for chart: // parameters for chart:
$start = (string)$request->get('start'); $dates = $request->getAll();
$end = (string)$request->get('end'); /** @var Carbon $start */
if ('' === $start || '' === $end) { $start = $dates['start'];
throw new FireflyException('Start and end are mandatory parameters.'); /** @var Carbon $end */
} $end = $dates['end'];
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
$start->subDay(); $start->subDay();
// prep some vars: // prep some vars:
@@ -156,32 +155,31 @@ class AccountController extends Controller
return response()->json($chartData); return response()->json($chartData);
} }
/** /**
* @param Request $request * @param DateRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function overview(Request $request): JsonResponse public function overview(DateRequest $request): JsonResponse
{ {
// parameters for chart: // parameters for chart:
$start = (string)$request->get('start'); $dates = $request->getAll();
$end = (string)$request->get('end'); /** @var Carbon $start */
if ('' === $start || '' === $end) { $start = $dates['start'];
throw new FireflyException('Start and end are mandatory parameters.'); /** @var Carbon $end */
} $end = $dates['end'];
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
// user's preferences // user's preferences
$defaultSet = $this->repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray(); $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray();
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getDefaultCurrency();
if (0 === \count($frontPage->data)) { // @codeCoverageIgnoreStart
if (0 === count($frontPage->data)) {
$frontPage->data = $defaultSet; $frontPage->data = $defaultSet;
$frontPage->save(); $frontPage->save();
} }
// @codeCoverageIgnoreEnd
// get accounts: // get accounts:
$accounts = $this->repository->getAccountsById($frontPage->data); $accounts = $this->repository->getAccountsById($frontPage->data);
@@ -190,7 +188,7 @@ class AccountController extends Controller
foreach ($accounts as $account) { foreach ($accounts as $account) {
$currency = $this->repository->getAccountCurrency($account); $currency = $this->repository->getAccountCurrency($account);
if (null === $currency) { if (null === $currency) {
$currency = $default; $currency = $default; // @codeCoverageIgnore
} }
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
@@ -202,7 +200,7 @@ class AccountController extends Controller
'yAxisID' => 0, // 0, 1, 2 'yAxisID' => 0, // 0, 1, 2
'entries' => [], 'entries' => [],
]; ];
/** @var Carbon $currentStart */
$currentStart = clone $start; $currentStart = clone $start;
$range = app('steam')->balanceInRange($account, $start, clone $end); $range = app('steam')->balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 12); $previous = round(array_values($range)[0], 12);
@@ -221,22 +219,19 @@ class AccountController extends Controller
} }
/** /**
* @param Request $request * @param DateRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function revenueOverview(Request $request): JsonResponse public function revenueOverview(DateRequest $request): JsonResponse
{ {
// parameters for chart: // parameters for chart:
$start = (string)$request->get('start'); $dates = $request->getAll();
$end = (string)$request->get('end'); /** @var Carbon $start */
if ('' === $start || '' === $end) { $start = $dates['start'];
throw new FireflyException('Start and end are mandatory parameters.'); /** @var Carbon $end */
} $end = $dates['end'];
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
$start->subDay(); $start->subDay();
// prep some vars: // prep some vars:
@@ -267,7 +262,8 @@ class AccountController extends Controller
$tempData[] = [ $tempData[] = [
'name' => $accountNames[$accountId], 'name' => $accountNames[$accountId],
'difference' => bcmul($diff, '-1'), 'difference' => bcmul($diff, '-1'),
'diff_float' => (float)$diff * -1, // For some reason this line is never covered in code coverage:
'diff_float' => ((float)$diff) * -1, // @codeCoverageIgnore
'currency_id' => $currencyId, 'currency_id' => $currencyId,
]; ];
} }
@@ -308,41 +304,4 @@ class AccountController extends Controller
return response()->json($chartData); return response()->json($chartData);
} }
/**
* Small helper function for the revenue and expense account charts.
* TODO should include Trait instead of doing this.
*
* @param array $names
*
* @return array
*/
protected function expandNames(array $names): array
{
$result = [];
foreach ($names as $entry) {
$result[$entry['name']] = 0;
}
return $result;
}
/**
* Small helper function for the revenue and expense account charts.
* TODO should include Trait instead of doing this.
*
* @param Collection $accounts
*
* @return array
*/
protected function extractNames(Collection $accounts): array
{
$return = [];
/** @var Account $account */
foreach ($accounts as $account) {
$return[$account->id] = $account->name;
}
return $return;
}
} }

View File

@@ -28,6 +28,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -37,11 +38,15 @@ use Illuminate\Support\Collection;
*/ */
class AvailableBudgetController extends Controller class AvailableBudgetController extends Controller
{ {
/** @var OperationsRepositoryInterface */
private $opsRepository;
/** @var BudgetRepositoryInterface */ /** @var BudgetRepositoryInterface */
private $repository; private $repository;
/** /**
* AvailableBudgetController constructor. * AvailableBudgetController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -51,7 +56,9 @@ class AvailableBudgetController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->repository->setUser($user); $this->repository->setUser($user);
$this->opsRepository->setUser($user);
return $next($request); return $next($request);
} }
@@ -67,7 +74,7 @@ class AvailableBudgetController extends Controller
{ {
$currency = $availableBudget->transactionCurrency; $currency = $availableBudget->transactionCurrency;
$budgets = $this->repository->getActiveBudgets(); $budgets = $this->repository->getActiveBudgets();
$budgetInformation = $this->repository->spentInPeriodMc($budgets, new Collection, $availableBudget->start_date, $availableBudget->end_date); $budgetInformation = $this->opsRepository->spentInPeriodMc($budgets, new Collection, $availableBudget->start_date, $availableBudget->end_date);
$spent = 0.0; $spent = 0.0;
// get for current currency // get for current currency

View File

@@ -26,12 +26,12 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Api\V1\Requests\DateRequest;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface;
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/** /**
* Class CategoryController * Class CategoryController
@@ -40,9 +40,15 @@ class CategoryController extends Controller
{ {
/** @var CategoryRepositoryInterface */ /** @var CategoryRepositoryInterface */
private $categoryRepository; private $categoryRepository;
/** @var NoCategoryRepositoryInterface */
private $noCatRepository;
/** @var OperationsRepositoryInterface */
private $opsRepository;
/** /**
* AccountController constructor. * AccountController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -52,7 +58,11 @@ class CategoryController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$this->categoryRepository = app(CategoryRepositoryInterface::class); $this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->noCatRepository = app(NoCategoryRepositoryInterface::class);
$this->categoryRepository->setUser($user); $this->categoryRepository->setUser($user);
$this->opsRepository->setUser($user);
$this->noCatRepository->setUser($user);
return $next($request); return $next($request);
} }
@@ -61,144 +71,127 @@ class CategoryController extends Controller
/** /**
* @param Request $request * @param DateRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException *
* TODO after 4.8,0, simplify
*/ */
public function overview(Request $request): JsonResponse public function overview(DateRequest $request): JsonResponse
{ {
// parameters for chart: // parameters for chart:
$start = (string)$request->get('start'); $dates = $request->getAll();
$end = (string)$request->get('end'); /** @var Carbon $start */
if ('' === $start || '' === $end) { $start = $dates['start'];
throw new FireflyException('Start and end are mandatory parameters.'); /** @var Carbon $end */
} $end = $dates['end'];
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
$tempData = []; $tempData = [];
$spent = $this->categoryRepository->spentInPeriodPerCurrency(new Collection, new Collection, $start, $end); $spentWith = $this->opsRepository->listExpenses($start, $end);
$earned = $this->categoryRepository->earnedInPeriodPerCurrency(new Collection, new Collection, $start, $end); $earnedWith = $this->opsRepository->listIncome($start, $end);
$spentWithout = $this->noCatRepository->listExpenses($start, $end);
$earnedWithout = $this->noCatRepository->listIncome($start, $end);
$categories = []; $categories = [];
// earned:
foreach ($earned as $categoryId => $row) { foreach ([$spentWith, $earnedWith] as $set) {
$categoryName = $row['name']; foreach ($set as $currency) {
foreach ($row['earned'] as $currencyId => $income) { foreach ($currency['categories'] as $category) {
// find or make set for currency: $categories[] = $category['name'];
$key = sprintf('%s-e', $currencyId); $inKey = sprintf('%d-i', $currency['currency_id']);
$decimalPlaces = $income['currency_decimal_places']; $outKey = sprintf('%d-e', $currency['currency_id']);
if (!isset($tempData[$key])) { // make data arrays if not yet present.
$tempData[$key] = [ $tempData[$inKey] = $tempData[$inKey] ?? [
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $income['currency_symbol']]), 'currency_id' => $currency['currency_id'],
'currency_id' => $income['currency_id'], 'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
'currency_code' => $income['currency_code'], 'currency_code' => $currency['currency_code'],
'currency_symbol' => $income['currency_symbol'], 'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $decimalPlaces, 'currency_decimal_places' => $currency['currency_decimal_places'],
'type' => 'bar', // line, area or bar 'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2 'yAxisID' => 0, // 0, 1, 2
'entries' => [], 'entries' => [
// per category:
// "category" => 5,
],
]; ];
} $tempData[$outKey] = $tempData[$outKey] ?? [
$amount = round($income['earned'], $decimalPlaces); 'currency_id' => $currency['currency_id'],
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount; 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
$tempData[$key]['entries'][$categoryName] 'currency_code' => $currency['currency_code'],
= $amount; 'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $currency['currency_decimal_places'],
}
}
// earned with no category:
$noCategory = $this->categoryRepository->earnedInPeriodPcWoCategory(new Collection, $start, $end);
foreach ($noCategory as $currencyId => $income) {
$categoryName = (string)trans('firefly.no_category');
// find or make set for currency:
$key = sprintf('%s-e', $currencyId);
$decimalPlaces = $income['currency_decimal_places'];
if (!isset($tempData[$key])) {
$tempData[$key] = [
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $income['currency_symbol']]),
'currency_id' => $income['currency_id'],
'currency_code' => $income['currency_code'],
'currency_symbol' => $income['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar 'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2 'yAxisID' => 0, // 0, 1, 2
'entries' => [], 'entries' => [
// per category:
// "category" => 5,
],
]; ];
foreach ($category['transaction_journals'] as $journal) {
// is it expense or income?
$letter = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i';
$currentKey = sprintf('%d-%s', $currency['currency_id'], $letter);
$name = $category['name'];
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
}
}
} }
$amount = round($income['spent'], $decimalPlaces);
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
$tempData[$key]['entries'][$categoryName]
= $amount;
} }
foreach ([$spentWithout, $earnedWithout] as $set) {
// spent foreach ($set as $currency) {
foreach ($spent as $categoryId => $row) { $inKey = sprintf('%d-i', $currency['currency_id']);
$categoryName = $row['name']; $outKey = sprintf('%d-e', $currency['currency_id']);
// create a new set if necessary, "spent (EUR)": $categories[] = (string)trans('firefly.no_category');
foreach ($row['spent'] as $currencyId => $expense) { // make data arrays if not yet present.
// find or make set for currency: $tempData[$inKey] = $tempData[$inKey] ?? [
$key = sprintf('%s-s', $currencyId); 'currency_id' => $currency['currency_id'],
$decimalPlaces = $expense['currency_decimal_places']; 'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
if (!isset($tempData[$key])) { 'currency_code' => $currency['currency_code'],
$tempData[$key] = [ 'currency_symbol' => $currency['currency_symbol'],
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $expense['currency_symbol']]), 'currency_decimal_places' => $currency['currency_decimal_places'],
'currency_id' => $expense['currency_id'],
'currency_code' => $expense['currency_code'],
'currency_symbol' => $expense['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar 'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2 'yAxisID' => 0, // 0, 1, 2
'entries' => [], 'entries' => [
// per category:
// "category" => 5,
],
]; ];
} $tempData[$outKey] = $tempData[$outKey] ?? [
$amount = round($expense['spent'], $decimalPlaces); 'currency_id' => $currency['currency_id'],
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount; 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
$tempData[$key]['entries'][$categoryName] 'currency_code' => $currency['currency_code'],
= $amount; 'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $currency['currency_decimal_places'],
}
}
// spent with no category
$noCategory = $this->categoryRepository->spentInPeriodPcWoCategory(new Collection, $start, $end);
foreach ($noCategory as $currencyId => $expense) {
$categoryName = (string)trans('firefly.no_category');
// find or make set for currency:
$key = sprintf('%s-s', $currencyId);
$decimalPlaces = $expense['currency_decimal_places'];
if (!isset($tempData[$key])) {
$tempData[$key] = [
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $expense['currency_symbol']]),
'currency_id' => $expense['currency_id'],
'currency_code' => $expense['currency_code'],
'currency_symbol' => $expense['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar 'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2 'yAxisID' => 0, // 0, 1, 2
'entries' => [], 'entries' => [
// per category:
// "category" => 5,
],
]; ];
foreach ($currency['transaction_journals'] as $journal) {
// is it expense or income?
$letter = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i';
$currentKey = sprintf('%d-%s', $currency['currency_id'], $letter);
$name = (string)trans('firefly.no_category');
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
}
} }
$amount = round($expense['spent'], $decimalPlaces);
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
$tempData[$key]['entries'][$categoryName]
= $amount;
} }
asort($categories);
$keys = array_keys($categories);
// re-sort every spent array and add 0 for missing entries. // re-sort every spent array and add 0 for missing entries.
foreach ($tempData as $index => $set) { foreach ($tempData as $index => $set) {
$oldSet = $set['entries']; $oldSet = $set['entries'];
$newSet = []; $newSet = [];
foreach ($keys as $key) { foreach ($categories as $category) {
$value = $oldSet[$key] ?? 0; $value = $oldSet[$category] ?? '0';
$value = $value < 0 ? $value * -1 : $value; $value = -1 === bccomp($value, '0') ? bcmul($value, '-1') : $value;
$newSet[$key] = $value; $newSet[$category] = $value;
} }
$tempData[$index]['entries'] = $newSet; $tempData[$index]['entries'] = $newSet;
} }

View File

@@ -32,6 +32,8 @@ use Illuminate\Http\JsonResponse;
/** /**
* Class ConfigurationController. * Class ConfigurationController.
*
* @codeCoverageIgnore
*/ */
class ConfigurationController extends Controller class ConfigurationController extends Controller
{ {
@@ -41,7 +43,8 @@ class ConfigurationController extends Controller
private $repository; private $repository;
/** /**
* BudgetController constructor. * ConfigurationController constructor.
*
*/ */
public function __construct() public function __construct()
{ {
@@ -81,7 +84,6 @@ class ConfigurationController extends Controller
* @param string $name * @param string $name
* *
* @return JsonResponse * @return JsonResponse
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function update(ConfigurationRequest $request, string $name): JsonResponse public function update(ConfigurationRequest $request, string $name): JsonResponse
{ {
@@ -96,7 +98,6 @@ class ConfigurationController extends Controller
* Get all config values. * Get all config values.
* *
* @return array * @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
private function getConfigData(): array private function getConfigData(): array
{ {

View File

@@ -30,6 +30,8 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
use League\Fractal\Manager;
use League\Fractal\Serializer\JsonApiSerializer;
use Log; use Log;
use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\ParameterBag;
@@ -37,7 +39,7 @@ use Symfony\Component\HttpFoundation\ParameterBag;
* Class Controller. * Class Controller.
* *
* @codeCoverageIgnore * @codeCoverageIgnore
* @SuppressWarnings(PHPMD.NumberOfChildren) *
*/ */
class Controller extends BaseController class Controller extends BaseController
{ {
@@ -61,7 +63,6 @@ class Controller extends BaseController
* *
* @return string * @return string
* *
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
protected function buildParams(): string protected function buildParams(): string
{ {
@@ -82,11 +83,23 @@ class Controller extends BaseController
return $return; return $return;
} }
/**
* @return Manager
*/
protected function getManager(): Manager
{
// create some objects:
$manager = new Manager;
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
return $manager;
}
/** /**
* Method to grab all parameters from the URI. * Method to grab all parameters from the URI.
* *
* @return ParameterBag * @return ParameterBag
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
private function getParameters(): ParameterBag private function getParameters(): ParameterBag
{ {

View File

@@ -26,20 +26,18 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\CurrencyRequest; use FireflyIII\Api\V1\Requests\CurrencyRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Recurrence; use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\RuleTrigger;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
@@ -55,22 +53,18 @@ use FireflyIII\Transformers\CurrencyExchangeRateTransformer;
use FireflyIII\Transformers\CurrencyTransformer; use FireflyIII\Transformers\CurrencyTransformer;
use FireflyIII\Transformers\RecurrenceTransformer; use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class CurrencyController. * Class CurrencyController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class CurrencyController extends Controller class CurrencyController extends Controller
{ {
@@ -82,6 +76,8 @@ class CurrencyController extends Controller
/** /**
* CurrencyRepository constructor. * CurrencyRepository constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -108,12 +104,11 @@ class CurrencyController extends Controller
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function accounts(Request $request, TransactionCurrency $currency): JsonResponse public function accounts(Request $request, TransactionCurrency $currency): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// read type from URI // read type from URI
$type = $request->get('type') ?? 'all'; $type = $request->get('type') ?? 'all';
@@ -130,7 +125,7 @@ class CurrencyController extends Controller
// filter list on currency preference: // filter list on currency preference:
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
function (Account $account) use ($currency, $accountRepository) { static function (Account $account) use ($currency, $accountRepository) {
$currencyId = (int)$accountRepository->getMetaValue($account, 'currency_id'); $currencyId = (int)$accountRepository->getMetaValue($account, 'currency_id');
return $currencyId === $currency->id; return $currencyId === $currency->id;
@@ -144,9 +139,6 @@ class CurrencyController extends Controller
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AccountTransformer $transformer */ /** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class); $transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -161,37 +153,30 @@ class CurrencyController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function availableBudgets(Request $request, TransactionCurrency $currency): JsonResponse public function availableBudgets(TransactionCurrency $currency): JsonResponse
{ {
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of available budgets. Count it and split it. // get list of available budgets. Count it and split it.
/** @var BudgetRepositoryInterface $repository */ /** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class); $repository = app(BudgetRepositoryInterface::class);
/** @var AvailableBudgetRepositoryInterface $abRepository */
$abRepository = app(AvailableBudgetRepositoryInterface::class);
$repository->setUser($admin); $repository->setUser($admin);
$unfiltered = $repository->getAvailableBudgets(); $collection = $abRepository->getAvailableBudgetsByCurrency($currency);
// filter list.
$collection = $unfiltered->filter(
function (AvailableBudget $availableBudget) use ($currency) {
return $availableBudget->transaction_currency_id === $currency->id;
}
);
$count = $collection->count(); $count = $collection->count();
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
@@ -200,9 +185,6 @@ class CurrencyController extends Controller
$paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.available_budgets', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.available_budgets', [$currency->code]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -216,27 +198,23 @@ class CurrencyController extends Controller
/** /**
* List all bills * List all bills
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function bills(Request $request, TransactionCurrency $currency): JsonResponse public function bills(TransactionCurrency $currency): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
/** @var BillRepositoryInterface $repository */ /** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class); $repository = app(BillRepositoryInterface::class);
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$paginator = $repository->getPaginator($pageSize); $unfiltered = $repository->getBills();
/** @var Collection $bills */
$unfiltered = $paginator->getCollection();
// filter and paginate list: // filter and paginate list:
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
function (Bill $bill) use ($currency) { static function (Bill $bill) use ($currency) {
return $bill->transaction_currency_id === $currency->id; return $bill->transaction_currency_id === $currency->id;
} }
); );
@@ -247,9 +225,6 @@ class CurrencyController extends Controller
$paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.bills', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.bills', [$currency->code]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BillTransformer $transformer */ /** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class); $transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -263,36 +238,24 @@ class CurrencyController extends Controller
/** /**
* List all budget limits * List all budget limits
* *
* @param Request $request
*
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function budgetLimits(Request $request, TransactionCurrency $currency): JsonResponse public function budgetLimits(TransactionCurrency $currency): JsonResponse
{ {
/** @var BudgetRepositoryInterface $repository */ /** @var BudgetLimitRepositoryInterface $blRepository */
$repository = app(BudgetRepositoryInterface::class); $blRepository = app(BudgetLimitRepositoryInterface::class);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager = $this->getManager();
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$unfiltered = $repository->getAllBudgetLimits($this->parameters->get('start'), $this->parameters->get('end')); $collection = $blRepository->getAllBudgetLimitsByCurrency($currency, $this->parameters->get('start'), $this->parameters->get('end'));
// TODO replace this
// filter budget limits on currency ID
$collection = $unfiltered->filter(
function (BudgetLimit $budgetLimit) use ($currency) {
return $budgetLimit->transaction_currency_id === $currency->id;
}
);
$count = $collection->count(); $count = $collection->count();
$budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.budget_limits', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.budget_limits', [$currency->code]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */ /** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class); $transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -306,19 +269,17 @@ class CurrencyController extends Controller
/** /**
* Show a list of known exchange rates * Show a list of known exchange rates
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function cer(Request $request, TransactionCurrency $currency): JsonResponse public function cer(TransactionCurrency $currency): JsonResponse
{ {
// create some objects: // create some objects:
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$collection = $this->repository->getExchangeRates($currency); $collection = $this->repository->getExchangeRates($currency);
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$count = $collection->count(); $count = $collection->count();
@@ -326,8 +287,6 @@ class CurrencyController extends Controller
$paginator = new LengthAwarePaginator($exchangeRates, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($exchangeRates, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.cer', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.cer', [$currency->code]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var CurrencyExchangeRateTransformer $transformer */ /** @var CurrencyExchangeRateTransformer $transformer */
$transformer = app(CurrencyExchangeRateTransformer::class); $transformer = app(CurrencyExchangeRateTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -345,6 +304,7 @@ class CurrencyController extends Controller
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
* @codeCoverageIgnore
*/ */
public function delete(TransactionCurrency $currency): JsonResponse public function delete(TransactionCurrency $currency): JsonResponse
{ {
@@ -366,21 +326,19 @@ class CurrencyController extends Controller
/** /**
* Disable a currency. * Disable a currency.
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function disable(Request $request, TransactionCurrency $currency): JsonResponse public function disable(TransactionCurrency $currency): JsonResponse
{ {
// must be unused. // must be unused.
if ($this->repository->currencyInUse($currency)) { if ($this->repository->currencyInUse($currency)) {
return response()->json([], 409); return response()->json([], 409);
} }
$this->repository->disable($currency); $this->repository->disable($currency);
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency); $this->parameters->set('defaultCurrency', $defaultCurrency);
@@ -398,17 +356,15 @@ class CurrencyController extends Controller
/** /**
* Enable a currency. * Enable a currency.
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function enable(Request $request, TransactionCurrency $currency): JsonResponse public function enable(TransactionCurrency $currency): JsonResponse
{ {
$this->repository->enable($currency); $this->repository->enable($currency);
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency); $this->parameters->set('defaultCurrency', $defaultCurrency);
@@ -426,11 +382,10 @@ class CurrencyController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$collection = $this->repository->getAll(); $collection = $this->repository->getAll();
@@ -441,9 +396,7 @@ class CurrencyController extends Controller
$paginator->setPath(route('api.v1.currencies.index') . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.index') . $this->buildParams());
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency); $this->parameters->set('defaultCurrency', $defaultCurrency);
@@ -460,21 +413,19 @@ class CurrencyController extends Controller
/** /**
* Make the currency a default currency. * Make the currency a default currency.
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function makeDefault(Request $request, TransactionCurrency $currency): JsonResponse public function makeDefault(TransactionCurrency $currency): JsonResponse
{ {
$this->repository->enable($currency); $this->repository->enable($currency);
app('preferences')->set('currencyPreference', $currency->code); app('preferences')->set('currencyPreference', $currency->code);
app('preferences')->mark(); app('preferences')->mark();
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$this->parameters->set('defaultCurrency', $currency); $this->parameters->set('defaultCurrency', $currency);
@@ -491,18 +442,14 @@ class CurrencyController extends Controller
/** /**
* List all recurring transactions. * List all recurring transactions.
* *
* @param Request $request
*
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse] * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function recurrences(Request $request, TransactionCurrency $currency): JsonResponse public function recurrences(TransactionCurrency $currency): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -513,7 +460,7 @@ class CurrencyController extends Controller
// filter selection // filter selection
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
function (Recurrence $recurrence) use ($currency) { static function (Recurrence $recurrence) use ($currency) {
/** @var RecurrenceTransaction $transaction */ /** @var RecurrenceTransaction $transaction */
foreach ($recurrence->recurrenceTransactions as $transaction) { foreach ($recurrence->recurrenceTransactions as $transaction) {
if ($transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id) { if ($transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id) {
@@ -533,9 +480,6 @@ class CurrencyController extends Controller
$paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.recurrences', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.recurrences', [$currency->code]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */ /** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class); $transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -550,15 +494,14 @@ class CurrencyController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse] * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function rules(Request $request, TransactionCurrency $currency): JsonResponse public function rules(TransactionCurrency $currency): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it. // get list of budgets. Count it and split it.
@@ -567,7 +510,7 @@ class CurrencyController extends Controller
$unfiltered = $repository->getAll(); $unfiltered = $repository->getAll();
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
function (Rule $rule) use ($currency) { static function (Rule $rule) use ($currency) {
/** @var RuleTrigger $trigger */ /** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) { foreach ($rule->ruleTriggers as $trigger) {
if ('currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value) { if ('currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value) {
@@ -586,9 +529,6 @@ class CurrencyController extends Controller
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rules.index') . $this->buildParams()); $paginator->setPath(route('api.v1.rules.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -603,16 +543,14 @@ class CurrencyController extends Controller
/** /**
* Show a currency. * Show a currency.
* *
* @param Request $request
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, TransactionCurrency $currency): JsonResponse public function show(TransactionCurrency $currency): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency); $this->parameters->set('defaultCurrency', $defaultCurrency);
@@ -642,9 +580,7 @@ class CurrencyController extends Controller
app('preferences')->set('currencyPreference', $currency->code); app('preferences')->set('currencyPreference', $currency->code);
app('preferences')->mark(); app('preferences')->mark();
} }
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency); $this->parameters->set('defaultCurrency', $defaultCurrency);
@@ -668,6 +604,7 @@ class CurrencyController extends Controller
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, TransactionCurrency $currency): JsonResponse public function transactions(Request $request, TransactionCurrency $currency): JsonResponse
{ {
@@ -676,34 +613,37 @@ class CurrencyController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setCurrency($currency);
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on currency.
->setCurrency($currency)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.currencies.transactions', [$currency->code]) . $this->buildParams()); $paginator->setPath(route('api.v1.currencies.transactions', [$currency->code]) . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -730,9 +670,7 @@ class CurrencyController extends Controller
app('preferences')->mark(); app('preferences')->mark();
} }
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency); $this->parameters->set('defaultCurrency', $defaultCurrency);

View File

@@ -31,12 +31,12 @@ use FireflyIII\Transformers\CurrencyExchangeRateTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use League\Fractal\Manager;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class CurrencyExchangeRateController * Class CurrencyExchangeRateController
*
* @codeCoverageIgnore
*/ */
class CurrencyExchangeRateController extends Controller class CurrencyExchangeRateController extends Controller
{ {
@@ -45,6 +45,7 @@ class CurrencyExchangeRateController extends Controller
/** /**
* CurrencyExchangeRateController constructor. * CurrencyExchangeRateController constructor.
*
*/ */
public function __construct() public function __construct()
{ {
@@ -73,11 +74,7 @@ class CurrencyExchangeRateController extends Controller
*/ */
public function index(Request $request): JsonResponse public function index(Request $request): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$fromCurrency = $this->repository->findByCodeNull($request->get('from') ?? 'EUR'); $fromCurrency = $this->repository->findByCodeNull($request->get('from') ?? 'EUR');
$toCurrency = $this->repository->findByCodeNull($request->get('to') ?? 'USD'); $toCurrency = $this->repository->findByCodeNull($request->get('to') ?? 'USD');
@@ -88,6 +85,7 @@ class CurrencyExchangeRateController extends Controller
throw new FireflyException('Unknown destination currency.'); throw new FireflyException('Unknown destination currency.');
} }
/** @var Carbon $dateObj */
$dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d')); $dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d'));
$this->parameters->set('from', $fromCurrency->code); $this->parameters->set('from', $fromCurrency->code);
$this->parameters->set('to', $toCurrency->code); $this->parameters->set('to', $toCurrency->code);

View File

@@ -23,24 +23,20 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\ImportJobTransformer; use FireflyIII\Transformers\ImportJobTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class ImportController * Class ImportController
@@ -52,7 +48,9 @@ class ImportController extends Controller
private $repository; private $repository;
/** /**
* LinkTypeController constructor. * ImportController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -70,15 +68,13 @@ class ImportController extends Controller
} }
/** /**
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function listAll(Request $request): JsonResponse public function listAll(): JsonResponse
{ {
// create some objects: // create some objects:
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of accounts. Count it and split it. // get list of accounts. Count it and split it.
@@ -90,9 +86,6 @@ class ImportController extends Controller
$paginator = new LengthAwarePaginator($importJobs, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($importJobs, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.import.list') . $this->buildParams()); $paginator->setPath(route('api.v1.import.list') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var ImportJobTransformer $transformer */ /** @var ImportJobTransformer $transformer */
$transformer = app(ImportJobTransformer::class); $transformer = app(ImportJobTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -104,17 +97,14 @@ class ImportController extends Controller
} }
/** /**
* @param Request $request
* @param ImportJob $importJob * @param ImportJob $importJob
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, ImportJob $importJob): JsonResponse public function show(ImportJob $importJob): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var ImportJobTransformer $transformer */ /** @var ImportJobTransformer $transformer */
$transformer = app(ImportJobTransformer::class); $transformer = app(ImportJobTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -131,6 +121,7 @@ class ImportController extends Controller
* @param ImportJob $importJob * @param ImportJob $importJob
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, ImportJob $importJob): JsonResponse public function transactions(Request $request, ImportJob $importJob): JsonResponse
{ {
@@ -139,9 +130,7 @@ class ImportController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$tag = $importJob->tag; $tag = $importJob->tag;
$transactions = new Collection(); $transactions = new Collection();
@@ -151,29 +140,33 @@ class ImportController extends Controller
if (null !== $tag) { if (null !== $tag) {
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setTag($tag);
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on tag.
->setTag($tag)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
} }
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');

View File

@@ -25,29 +25,24 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\LinkTypeRequest; use FireflyIII\Api\V1\Requests\LinkTypeRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\LinkType; use FireflyIII\Models\LinkType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class LinkTypeController. * Class LinkTypeController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class LinkTypeController extends Controller class LinkTypeController extends Controller
{ {
@@ -60,6 +55,8 @@ class LinkTypeController extends Controller
/** /**
* LinkTypeController constructor. * LinkTypeController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -84,6 +81,7 @@ class LinkTypeController extends Controller
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
* @codeCoverageIgnore
*/ */
public function delete(LinkType $linkType): JsonResponse public function delete(LinkType $linkType): JsonResponse
{ {
@@ -98,15 +96,13 @@ class LinkTypeController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: // create some objects:
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of accounts. Count it and split it. // get list of accounts. Count it and split it.
@@ -118,9 +114,6 @@ class LinkTypeController extends Controller
$paginator = new LengthAwarePaginator($linkTypes, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($linkTypes, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.link_types.index') . $this->buildParams()); $paginator->setPath(route('api.v1.link_types.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var LinkTypeTransformer $transformer */ /** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class); $transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -135,16 +128,14 @@ class LinkTypeController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param LinkType $linkType * @param LinkType $linkType
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, LinkType $linkType): JsonResponse public function show(LinkType $linkType): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var LinkTypeTransformer $transformer */ /** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class); $transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -174,9 +165,7 @@ class LinkTypeController extends Controller
$data = $request->getAll(); $data = $request->getAll();
// if currency ID is 0, find the currency by the code: // if currency ID is 0, find the currency by the code:
$linkType = $this->repository->store($data); $linkType = $this->repository->store($data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var LinkTypeTransformer $transformer */ /** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class); $transformer = app(LinkTypeTransformer::class);
@@ -194,6 +183,7 @@ class LinkTypeController extends Controller
* @param LinkType $linkType * @param LinkType $linkType
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, LinkType $linkType): JsonResponse public function transactions(Request $request, LinkType $linkType): JsonResponse
{ {
@@ -202,37 +192,40 @@ class LinkTypeController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// whatever is returned by the query, it must be part of these journals: // whatever is returned by the query, it must be part of these journals:
$journalIds = $this->repository->getJournalIds($linkType); $journalIds = $this->repository->getJournalIds($linkType);
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setJournalIds($journalIds);
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on journal IDs.
->setJournalIds($journalIds)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -266,10 +259,7 @@ class LinkTypeController extends Controller
$data = $request->getAll(); $data = $request->getAll();
$this->repository->update($linkType, $data); $this->repository->update($linkType, $data);
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var LinkTypeTransformer $transformer */ /** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class); $transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -31,18 +31,14 @@ use FireflyIII\Transformers\PiggyBankEventTransformer;
use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\Transformers\PiggyBankTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class PiggyBankController. * Class PiggyBankController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class PiggyBankController extends Controller class PiggyBankController extends Controller
{ {
@@ -52,6 +48,8 @@ class PiggyBankController extends Controller
/** /**
* PiggyBankController constructor. * PiggyBankController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -76,6 +74,7 @@ class PiggyBankController extends Controller
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(PiggyBank $piggyBank): JsonResponse public function delete(PiggyBank $piggyBank): JsonResponse
{ {
@@ -87,16 +86,12 @@ class PiggyBankController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -109,9 +104,6 @@ class PiggyBankController extends Controller
$paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.piggy_banks.index') . $this->buildParams()); $paginator->setPath(route('api.v1.piggy_banks.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PiggyBankTransformer $transformer */ /** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class); $transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -126,18 +118,16 @@ class PiggyBankController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function piggyBankEvents(Request $request, PiggyBank $piggyBank): JsonResponse public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse
{ {
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$collection = $this->repository->getEvents($piggyBank); $collection = $this->repository->getEvents($piggyBank);
$count = $collection->count(); $count = $collection->count();
@@ -161,16 +151,14 @@ class PiggyBankController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, PiggyBank $piggyBank): JsonResponse public function show(PiggyBank $piggyBank): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PiggyBankTransformer $transformer */ /** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class); $transformer = app(PiggyBankTransformer::class);
@@ -194,9 +182,7 @@ class PiggyBankController extends Controller
{ {
$piggyBank = $this->repository->store($request->getAll()); $piggyBank = $this->repository->store($request->getAll());
if (null !== $piggyBank) { if (null !== $piggyBank) {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PiggyBankTransformer $transformer */ /** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class); $transformer = app(PiggyBankTransformer::class);
@@ -220,11 +206,15 @@ class PiggyBankController extends Controller
*/ */
public function update(PiggyBankRequest $request, PiggyBank $piggyBank): JsonResponse public function update(PiggyBankRequest $request, PiggyBank $piggyBank): JsonResponse
{ {
$piggyBank = $this->repository->update($piggyBank, $request->getAll()); $data = $request->getAll();
$manager = new Manager(); $piggyBank = $this->repository->update($piggyBank, $data);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
if ('' !== $data['current_amount']) {
$this->repository->setCurrentAmount($piggyBank, $data['current_amount']);
}
$manager = $this->getManager();
/** @var PiggyBankTransformer $transformer */ /** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class); $transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -30,12 +30,9 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Transformers\PreferenceTransformer; use FireflyIII\Transformers\PreferenceTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* *
@@ -45,12 +42,14 @@ class PreferenceController extends Controller
{ {
/** /**
* LinkTypeController constructor. * LinkTypeController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->middleware( $this->middleware(
function ($request, $next) { static function ($request, $next) {
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
@@ -59,7 +58,7 @@ class PreferenceController extends Controller
// an important fallback is that the frontPageAccount array gets refilled automatically // an important fallback is that the frontPageAccount array gets refilled automatically
// when it turns up empty. // when it turns up empty.
$frontPageAccounts = app('preferences')->getForUser($user, 'frontPageAccounts', [])->data; $frontPageAccounts = app('preferences')->getForUser($user, 'frontPageAccounts', [])->data;
if (0 === \count($frontPageAccounts)) { if (0 === count($frontPageAccounts)) {
/** @var Collection $accounts */ /** @var Collection $accounts */
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$accountIds = $accounts->pluck('id')->toArray(); $accountIds = $accounts->pluck('id')->toArray();
@@ -74,18 +73,17 @@ class PreferenceController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$available = [ $available = [
'language', 'customFiscalYear', 'fiscalYearStart', 'currencyPreference', 'language', 'customFiscalYear', 'fiscalYearStart', 'currencyPreference',
'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange', 'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange',
'listPageSize, twoFactorAuthEnabled', 'listPageSize',
]; ];
$preferences = new Collection; $preferences = new Collection;
@@ -96,12 +94,7 @@ class PreferenceController extends Controller
} }
} }
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PreferenceTransformer $transformer */ /** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class); $transformer = app(PreferenceTransformer::class);
@@ -111,26 +104,19 @@ class PreferenceController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
} }
/** /**
* Return a single preference by name. * Return a single preference by name.
* *
* @param Request $request
* @param Preference $preference * @param Preference $preference
* *
* @return JsonResponse * @return JsonResponse
* @SuppressWarnings(PHPMD.CyclomaticComplexity) * @codeCoverageIgnore
*/ */
public function show(Request $request, Preference $preference): JsonResponse public function show(Preference $preference): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PreferenceTransformer $transformer */ /** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class); $transformer = app(PreferenceTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -147,7 +133,6 @@ class PreferenceController extends Controller
* @param Preference $preference * @param Preference $preference
* *
* @return JsonResponse * @return JsonResponse
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function update(PreferenceRequest $request, Preference $preference): JsonResponse public function update(PreferenceRequest $request, Preference $preference): JsonResponse
{ {
@@ -165,18 +150,12 @@ class PreferenceController extends Controller
$newValue = (int)$data['data']; $newValue = (int)$data['data'];
break; break;
case 'customFiscalYear': case 'customFiscalYear':
case 'twoFactorAuthEnabled':
$newValue = 1 === (int)$data['data']; $newValue = 1 === (int)$data['data'];
break; break;
} }
$result = app('preferences')->set($preference->name, $newValue); $result = app('preferences')->set($preference->name, $newValue);
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var PreferenceTransformer $transformer */ /** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class); $transformer = app(PreferenceTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -23,26 +23,23 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\RecurrenceRequest; use FireflyIII\Api\V1\Requests\RecurrenceStoreRequest;
use FireflyIII\Api\V1\Requests\RecurrenceUpdateRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Recurrence; use FireflyIII\Models\Recurrence;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Support\Cronjobs\RecurringCronjob; use FireflyIII\Support\Cronjobs\RecurringCronjob;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\RecurrenceTransformer; use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log; use Log;
/** /**
@@ -56,6 +53,8 @@ class RecurrenceController extends Controller
/** /**
* RecurrenceController constructor. * RecurrenceController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -80,6 +79,7 @@ class RecurrenceController extends Controller
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Recurrence $recurrence): JsonResponse public function delete(Recurrence $recurrence): JsonResponse
{ {
@@ -91,15 +91,12 @@ class RecurrenceController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -113,9 +110,6 @@ class RecurrenceController extends Controller
$paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.recurrences.index') . $this->buildParams()); $paginator->setPath(route('api.v1.recurrences.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */ /** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class); $transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -130,16 +124,14 @@ class RecurrenceController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, Recurrence $recurrence): JsonResponse public function show(Recurrence $recurrence): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */ /** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class); $transformer = app(RecurrenceTransformer::class);
@@ -155,16 +147,16 @@ class RecurrenceController extends Controller
/** /**
* Store new object. * Store new object.
* *
* @param RecurrenceRequest $request * @param RecurrenceStoreRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function store(RecurrenceRequest $request): JsonResponse public function store(RecurrenceStoreRequest $request): JsonResponse
{ {
$recurrence = $this->repository->store($request->getAll()); $data = $request->getAll();
$manager = new Manager(); $recurrence = $this->repository->store($data);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager = $this->getManager();
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */ /** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class); $transformer = app(RecurrenceTransformer::class);
@@ -182,6 +174,7 @@ class RecurrenceController extends Controller
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, Recurrence $recurrence): JsonResponse public function transactions(Request $request, Recurrence $recurrence): JsonResponse
{ {
@@ -190,37 +183,38 @@ class RecurrenceController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// whatever is returned by the query, it must be part of these journals: // whatever is returned by the query, it must be part of these journals:
$journalIds = $this->repository->getJournalIds($recurrence); $journalIds = $this->repository->getJournalIds($recurrence);
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setJournalIds($journalIds);
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on journal IDs.
->setJournalIds($journalIds)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -232,6 +226,7 @@ class RecurrenceController extends Controller
/** /**
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
* @codeCoverageIgnore
*/ */
public function trigger(): JsonResponse public function trigger(): JsonResponse
{ {
@@ -256,18 +251,17 @@ class RecurrenceController extends Controller
/** /**
* Update single recurrence. * Update single recurrence.
* *
* @param RecurrenceRequest $request * @param RecurrenceUpdateRequest $request
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse public function update(RecurrenceUpdateRequest $request, Recurrence $recurrence): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$category = $this->repository->update($recurrence, $data); $category = $this->repository->update($recurrence, $data);
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */ /** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class); $transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);

View File

@@ -23,31 +23,30 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon; use FireflyIII\Api\V1\Requests\RuleStoreRequest;
use FireflyIII\Api\V1\Requests\RuleRequest; use FireflyIII\Api\V1\Requests\RuleTestRequest;
use FireflyIII\Api\V1\Requests\RuleTriggerRequest;
use FireflyIII\Api\V1\Requests\RuleUpdateRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use FireflyIII\TransactionRules\TransactionMatcher; use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log; use Log;
/** /**
* Class RuleController * Class RuleController
*
*/ */
class RuleController extends Controller class RuleController extends Controller
{ {
@@ -58,6 +57,8 @@ class RuleController extends Controller
/** /**
* RuleController constructor. * RuleController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -84,6 +85,7 @@ class RuleController extends Controller
* @param Rule $rule * @param Rule $rule
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Rule $rule): JsonResponse public function delete(Rule $rule): JsonResponse
{ {
@@ -95,15 +97,12 @@ class RuleController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -117,9 +116,6 @@ class RuleController extends Controller
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rules.index') . $this->buildParams()); $paginator->setPath(route('api.v1.rules.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -132,18 +128,15 @@ class RuleController extends Controller
} }
/** /**
* List single resource.
*
* @param Request $request
* @param Rule $rule * @param Rule $rule
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function show(Request $request, Rule $rule): JsonResponse public function moveDown(Rule $rule): JsonResponse
{ {
$manager = new Manager(); $this->ruleRepository->moveDown($rule);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $rule = $this->ruleRepository->find($rule->id);
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $manager = $this->getManager();
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
@@ -155,20 +148,59 @@ class RuleController extends Controller
} }
/**
* @param Rule $rule
*
* @return JsonResponse
*/
public function moveUp(Rule $rule): JsonResponse
{
$this->ruleRepository->moveUp($rule);
$rule = $this->ruleRepository->find($rule->id);
$manager = $this->getManager();
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'rules');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List single resource.
*
* @param Rule $rule
*
* @return JsonResponse
* @codeCoverageIgnore
*/
public function show(Rule $rule): JsonResponse
{
$manager = $this->getManager();
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'rules');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/** /**
* Store new object. * Store new object.
* *
* @param RuleRequest $request * @param RuleStoreRequest $request
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function store(RuleRequest $request): JsonResponse public function store(RuleStoreRequest $request): JsonResponse
{ {
$rule = $this->ruleRepository->store($request->getAll()); $rule = $this->ruleRepository->store($request->getAll());
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -179,64 +211,38 @@ class RuleController extends Controller
} }
/** /**
* @param Request $request * @param RuleTestRequest $request
* @param Rule $rule * @param Rule $rule
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
*/ */
public function testRule(Request $request, Rule $rule): JsonResponse public function testRule(RuleTestRequest $request, Rule $rule): JsonResponse
{ {
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$page = 0 === (int)$request->query('page') ? 1 : (int)$request->query('page'); $parameters = $request->getTestParameters();
$startDate = null === $request->query('start_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('start_date'));
$endDate = null === $request->query('end_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('end_date'));
$searchLimit = 0 === (int)$request->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$request->query('search_limit');
$triggerLimit = 0 === (int)$request->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$request->query('triggered_limit');
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
$accounts = new Collection;
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $this->accountRepository->findNull((int)$accountId);
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
/** @var Rule $rule */ /** @var Rule $rule */
Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title)); Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title));
/** @var TransactionMatcher $matcher */ /** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class); $matcher = app(TransactionMatcher::class);
// set all parameters: // set all parameters:
$matcher->setRule($rule); $matcher->setRule($rule);
$matcher->setStartDate($startDate); $matcher->setStartDate($parameters['start_date']);
$matcher->setEndDate($endDate); $matcher->setEndDate($parameters['end_date']);
$matcher->setSearchLimit($searchLimit); $matcher->setSearchLimit($parameters['search_limit']);
$matcher->setTriggeredLimit($triggerLimit); $matcher->setTriggeredLimit($parameters['trigger_limit']);
$matcher->setAccounts($accounts); $matcher->setAccounts($parameters['accounts']);
$matchingTransactions = $matcher->findTransactionsByRule(); $matchingTransactions = $matcher->findTransactionsByRule();
$matchingTransactions = $matchingTransactions->unique('id'); $count = count($matchingTransactions);
$transactions = array_slice($matchingTransactions, ($parameters['page'] - 1) * $pageSize, $pageSize);
// make paginator out of results.
$count = $matchingTransactions->count();
$transactions = $matchingTransactions->slice(($page - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rules.test', [$rule->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.rules.test', [$rule->id]) . $this->buildParams());
// resulting list is presented as JSON thing. // resulting list is presented as JSON thing.
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; /** @var TransactionGroupTransformer $transformer */
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $transformer = app(TransactionGroupTransformer::class);
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($matchingTransactions, $transformer, 'transactions'); $resource = new FractalCollection($matchingTransactions, $transformer, 'transactions');
@@ -248,44 +254,37 @@ class RuleController extends Controller
/** /**
* Execute the given rule group on a set of existing transactions. * Execute the given rule group on a set of existing transactions.
* *
* @param Request $request * @param RuleTriggerRequest $request
* @param Rule $rule * @param Rule $rule
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function triggerRule(Request $request, Rule $rule): JsonResponse public function triggerRule(RuleTriggerRequest $request, Rule $rule): JsonResponse
{ {
// Get parameters specified by the user // Get parameters specified by the user
/** @var User $user */ $parameters = $request->getTriggerParameters();
$user = auth()->user();
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
$accounts = new Collection;
foreach ($accountList as $accountId) { /** @var RuleEngine $ruleEngine */
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); $ruleEngine = app(RuleEngine::class);
$account = $this->accountRepository->findNull((int)$accountId); $ruleEngine->setUser(auth()->user());
if (null !== $account && $this->accountRepository->isAsset($account)) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); $rules = [$rule->id];
$accounts->push($account);
$ruleEngine->setRulesToApply($rules);
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($parameters['accounts']);
$collector->setRange($parameters['start_date'], $parameters['end_date']);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
Log::debug('Start of new journal.');
$ruleEngine->processJournalArray($journal);
Log::debug('Done with all rules for this group + done with journal.');
} }
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
// Apply parameters to the job
$job->setUser($user);
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
return response()->json([], 204); return response()->json([], 204);
} }
@@ -293,17 +292,16 @@ class RuleController extends Controller
/** /**
* Update a rule. * Update a rule.
* *
* @param RuleRequest $request * @param RuleUpdateRequest $request
* @param Rule $rule * @param Rule $rule
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function update(RuleRequest $request, Rule $rule): JsonResponse public function update(RuleUpdateRequest $request, Rule $rule): JsonResponse
{ {
$rule = $this->ruleRepository->update($rule, $request->getAll()); $data = $request->getAll();
$manager = new Manager(); $rule = $this->ruleRepository->update($rule, $data);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager = $this->getManager();
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
@@ -312,6 +310,5 @@ class RuleController extends Controller
$resource = new Item($rule, $transformer, 'rules'); $resource = new Item($rule, $transformer, 'rules');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
} }
} }

View File

@@ -23,32 +23,30 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon; use Exception;
use FireflyIII\Api\V1\Requests\RuleGroupRequest; use FireflyIII\Api\V1\Requests\RuleGroupRequest;
use FireflyIII\Api\V1\Requests\RuleGroupTestRequest;
use FireflyIII\Api\V1\Requests\RuleGroupTriggerRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use FireflyIII\TransactionRules\TransactionMatcher; use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\RuleGroupTransformer; use FireflyIII\Transformers\RuleGroupTransformer;
use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log; use Log;
/** /**
* Class RuleGroupController * Class RuleGroupController
*/ */
@@ -61,6 +59,8 @@ class RuleGroupController extends Controller
/** /**
* RuleGroupController constructor. * RuleGroupController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -87,6 +87,7 @@ class RuleGroupController extends Controller
* @param RuleGroup $ruleGroup * @param RuleGroup $ruleGroup
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(RuleGroup $ruleGroup): JsonResponse public function delete(RuleGroup $ruleGroup): JsonResponse
{ {
@@ -98,16 +99,12 @@ class RuleGroupController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -120,9 +117,6 @@ class RuleGroupController extends Controller
$paginator = new LengthAwarePaginator($ruleGroups, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($ruleGroups, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rule_groups.index') . $this->buildParams()); $paginator->setPath(route('api.v1.rule_groups.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleGroupTransformer $transformer */ /** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class); $transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -134,17 +128,54 @@ class RuleGroupController extends Controller
} }
/** /**
* @param Request $request * @param RuleGroup $ruleGroup
* @param RuleGroup $group
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function rules(Request $request, RuleGroup $group): JsonResponse public function moveDown(RuleGroup $ruleGroup): JsonResponse
{ {
// create some objects: $this->ruleGroupRepository->moveDown($ruleGroup);
$manager = new Manager; $ruleGroup = $this->ruleGroupRepository->find($ruleGroup->id);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager = $this->getManager();
/** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($ruleGroup, $transformer, 'rule_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param RuleGroup $ruleGroup
*
* @return JsonResponse
*/
public function moveUp(RuleGroup $ruleGroup): JsonResponse
{
$this->ruleGroupRepository->moveUp($ruleGroup);
$ruleGroup = $this->ruleGroupRepository->find($ruleGroup->id);
$manager = $this->getManager();
/** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($ruleGroup, $transformer, 'rule_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param RuleGroup $group
*
* @return JsonResponse
* @codeCoverageIgnore
*/
public function rules(RuleGroup $group): JsonResponse
{
$manager = $this->getManager();
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -157,9 +188,6 @@ class RuleGroupController extends Controller
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rule_groups.rules', [$group->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.rule_groups.rules', [$group->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */ /** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class); $transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -174,17 +202,14 @@ class RuleGroupController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param RuleGroup $ruleGroup * @param RuleGroup $ruleGroup
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, RuleGroup $ruleGroup): JsonResponse public function show(RuleGroup $ruleGroup): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleGroupTransformer $transformer */ /** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class); $transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -205,9 +230,7 @@ class RuleGroupController extends Controller
public function store(RuleGroupRequest $request): JsonResponse public function store(RuleGroupRequest $request): JsonResponse
{ {
$ruleGroup = $this->ruleGroupRepository->store($request->getAll()); $ruleGroup = $this->ruleGroupRepository->store($request->getAll());
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleGroupTransformer $transformer */ /** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class); $transformer = app(RuleGroupTransformer::class);
@@ -220,23 +243,24 @@ class RuleGroupController extends Controller
} }
/** /**
* @param Request $request * @param RuleGroupTestRequest $request
* @param RuleGroup $group * @param RuleGroup $group
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
*
*/ */
public function testGroup(Request $request, RuleGroup $group): JsonResponse public function testGroup(RuleGroupTestRequest $request, RuleGroup $group): JsonResponse
{ {
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
Log::debug('Now in testGroup()'); Log::debug('Now in testGroup()');
/** @var Collection $rules */ /** @var Collection $rules */
$rules = $this->ruleGroupRepository->getActiveRules($group); $rules = $this->ruleGroupRepository->getActiveRules($group);
if (0 === $rules->count()) { if (0 === $rules->count()) {
throw new FireflyException('No rules in this rule group.'); throw new FireflyException('No rules in this rule group.');
} }
$parameters = $this->getTestParameters($request); $parameters = $request->getTestParameters();
$accounts = $this->getAccountParameter($parameters['account_list']); $matchingTransactions = [];
$matchingTransactions = new Collection;
Log::debug(sprintf('Going to test %d rules', $rules->count())); Log::debug(sprintf('Going to test %d rules', $rules->count()));
/** @var Rule $rule */ /** @var Rule $rule */
@@ -250,27 +274,23 @@ class RuleGroupController extends Controller
$matcher->setEndDate($parameters['end_date']); $matcher->setEndDate($parameters['end_date']);
$matcher->setSearchLimit($parameters['search_limit']); $matcher->setSearchLimit($parameters['search_limit']);
$matcher->setTriggeredLimit($parameters['trigger_limit']); $matcher->setTriggeredLimit($parameters['trigger_limit']);
$matcher->setAccounts($accounts); $matcher->setAccounts($parameters['accounts']);
$result = $matcher->findTransactionsByRule(); $result = $matcher->findTransactionsByRule();
$matchingTransactions = $result->merge($matchingTransactions); /** @noinspection AdditionOperationOnArraysInspection */
$matchingTransactions = $result + $matchingTransactions;
} }
$matchingTransactions = $matchingTransactions->unique('id');
// make paginator out of results. // make paginator out of results.
$count = $matchingTransactions->count(); $count = count($matchingTransactions);
$transactions = $matchingTransactions->slice(($parameters['page'] - 1) * $parameters['page_size'], $parameters['page_size']); $transactions = array_slice($matchingTransactions, ($parameters['page'] - 1) * $pageSize, $pageSize);
// make paginator: // make paginator:
$paginator = new LengthAwarePaginator($transactions, $count, $parameters['page_size'], $parameters['page']); $paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $parameters['page']);
$paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams()); $paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams());
// resulting list is presented as JSON thing. $manager = $this->getManager();
$manager = new Manager(); $transformer = app(TransactionGroupTransformer::class);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($matchingTransactions, $transformer, 'transactions'); $resource = new FractalCollection($matchingTransactions, $transformer, 'transactions');
@@ -282,47 +302,42 @@ class RuleGroupController extends Controller
/** /**
* Execute the given rule group on a set of existing transactions. * Execute the given rule group on a set of existing transactions.
* *
* @param Request $request * @param RuleGroupTriggerRequest $request
* @param RuleGroup $group * @param RuleGroup $group
* *
* @return JsonResponse * @return JsonResponse
* @throws Exception
*/ */
public function triggerGroup(Request $request, RuleGroup $group): JsonResponse public function triggerGroup(RuleGroupTriggerRequest $request, RuleGroup $group): JsonResponse
{ {
// Get parameters specified by the user $parameters = $request->getTriggerParameters();
/** @var User $user */
$user = auth()->user();
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
$accounts = new Collection;
foreach ($accountList as $accountId) { /** @var Collection $collection */
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); $collection = $this->ruleGroupRepository->getActiveRules($group);
$account = $this->accountRepository->findNull((int)$accountId); $rules = [];
if (null !== $account && AccountType::ASSET === $account->accountType->type) { /** @var Rule $item */
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); foreach ($collection as $item) {
$accounts->push($account); $rules[] = $item->id;
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
} }
/** @var Collection $rules */ // start looping.
$rules = $this->ruleGroupRepository->getActiveRules($group); /** @var RuleEngine $ruleEngine */
foreach ($rules as $rule) { $ruleEngine = app(RuleEngine::class);
// Create a job to do the work asynchronously $ruleEngine->setUser(auth()->user());
$job = new ExecuteRuleOnExistingTransactions($rule); $ruleEngine->setRulesToApply($rules);
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
// Apply parameters to the job /** @var GroupCollectorInterface $collector */
$job->setUser($user); $collector = app(GroupCollectorInterface::class);
$job->setAccounts($accounts); $collector->setAccounts($parameters['accounts']);
$job->setStartDate($startDate); $collector->setRange($parameters['start_date'], $parameters['end_date']);
$job->setEndDate($endDate); $journals = $collector->getExtractedJournals();
// Dispatch a new job to execute it in a queue /** @var array $journal */
$this->dispatch($job); foreach ($journals as $journal) {
Log::debug('Start of new journal.');
$ruleEngine->processJournalArray($journal);
Log::debug('Done with all rules for this group + done with journal.');
} }
return response()->json([], 204); return response()->json([], 204);
@@ -330,7 +345,6 @@ class RuleGroupController extends Controller
/** /**
* Update a rule group. * Update a rule group.
* TODO update order of rule group
* *
* @param RuleGroupRequest $request * @param RuleGroupRequest $request
* @param RuleGroup $ruleGroup * @param RuleGroup $ruleGroup
@@ -340,9 +354,7 @@ class RuleGroupController extends Controller
public function update(RuleGroupRequest $request, RuleGroup $ruleGroup): JsonResponse public function update(RuleGroupRequest $request, RuleGroup $ruleGroup): JsonResponse
{ {
$ruleGroup = $this->ruleGroupRepository->update($ruleGroup, $request->getAll()); $ruleGroup = $this->ruleGroupRepository->update($ruleGroup, $request->getAll());
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleGroupTransformer $transformer */ /** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class); $transformer = app(RuleGroupTransformer::class);
@@ -351,51 +363,5 @@ class RuleGroupController extends Controller
$resource = new Item($ruleGroup, $transformer, 'rule_groups'); $resource = new Item($ruleGroup, $transformer, 'rule_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param array $accounts
*
* @return Collection
*/
private function getAccountParameter(array $accounts): Collection
{
$return = new Collection;
foreach ($accounts as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $this->accountRepository->findNull((int)$accountId);
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$return->push($account);
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
return $return;
}
/**
* @param Request $request
*
* @return array
*/
private function getTestParameters(Request $request): array
{
return [
'page_size' => (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data,
'page' => 0 === (int)$request->query('page') ? 1 : (int)$request->query('page'),
'start_date' => null === $request->query('start_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('start_date')),
'end_date' => null === $request->query('end_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('end_date')),
'search_limit' => 0 === (int)$request->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$request->query('search_limit'),
'trigger_limit' => 0 === (int)$request->query('triggered_limit')
? (int)config('firefly.test-triggers.range')
: (int)$request->query(
'triggered_limit'
),
'account_list' => '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts')),
];
} }
} }

View File

@@ -26,21 +26,22 @@ namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use Exception;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Api\V1\Requests\DateRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface; use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@@ -48,6 +49,8 @@ use Illuminate\Support\Collection;
*/ */
class SummaryController extends Controller class SummaryController extends Controller
{ {
/** @var AvailableBudgetRepositoryInterface */
private $abRepository;
/** @var AccountRepositoryInterface */ /** @var AccountRepositoryInterface */
private $accountRepository; private $accountRepository;
/** @var BillRepositoryInterface */ /** @var BillRepositoryInterface */
@@ -57,8 +60,13 @@ class SummaryController extends Controller
/** @var CurrencyRepositoryInterface */ /** @var CurrencyRepositoryInterface */
private $currencyRepos; private $currencyRepos;
/** @var OperationsRepositoryInterface */
private $opsRepository;
/** /**
* AccountController constructor. * SummaryController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -71,11 +79,15 @@ class SummaryController extends Controller
$this->billRepository = app(BillRepositoryInterface::class); $this->billRepository = app(BillRepositoryInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class); $this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->billRepository->setUser($user); $this->billRepository->setUser($user);
$this->currencyRepos->setUser($user); $this->currencyRepos->setUser($user);
$this->budgetRepository->setUser($user); $this->budgetRepository->setUser($user);
$this->accountRepository->setUser($user); $this->accountRepository->setUser($user);
$this->abRepository->setUser($user);
$this->opsRepository->setUser($user);
return $next($request); return $next($request);
@@ -84,21 +96,19 @@ class SummaryController extends Controller
} }
/** /**
* @param Request $request * @param DateRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws Exception
*/ */
public function basic(Request $request): JsonResponse public function basic(DateRequest $request): JsonResponse
{ {
// parameters for boxes: // parameters for boxes:
$start = (string)$request->get('start'); $dates = $request->getAll();
$end = (string)$request->get('end'); $start = $dates['start'];
if ('' === $start || '' === $end) { $end = $dates['end'];
throw new FireflyException('Start and end are mandatory parameters.'); $code = $request->get('currency_code');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
// balance information: // balance information:
$balanceData = $this->getBalanceInformation($start, $end); $balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end); $billData = $this->getBillInformation($start, $end);
@@ -106,9 +116,15 @@ class SummaryController extends Controller
$networthData = $this->getNetWorthInfo($start, $end); $networthData = $this->getNetWorthInfo($start, $end);
$total = array_merge($balanceData, $billData, $spentData, $networthData); $total = array_merge($balanceData, $billData, $spentData, $networthData);
// TODO: liabilities with icon line-chart // give new keys
$return = [];
foreach ($total as $entry) {
if (null === $code || (null !== $code && $code === $entry['currency_code'])) {
$return[$entry['key']] = $entry;
}
}
return response()->json($total); return response()->json($return);
} }
@@ -121,7 +137,6 @@ class SummaryController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return bool * @return bool
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{ {
@@ -143,17 +158,17 @@ class SummaryController extends Controller
* @param array $spentInfo * @param array $spentInfo
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *
* @return float * @return string
*/ */
private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): float private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): string
{ {
foreach ($spentInfo as $array) { foreach ($spentInfo as $array) {
if ($array['currency_id'] === $currency->id) { if ($array['currency_id'] === $currency->id) {
return $array['amount']; return (string)$array['amount'];
} }
} }
return 0.0; return '0'; // @codeCoverageIgnore
} }
/** /**
@@ -170,36 +185,47 @@ class SummaryController extends Controller
$sums = []; $sums = [];
$return = []; $return = [];
// collect income of user: // collect income of user using the new group collector.
/** @var TransactionCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end) $collector
->setTypes([TransactionType::DEPOSIT]) ->setRange($start, $end)
->withOpposingAccount(); // set page to retrieve
$set = $collector->getTransactions(); ->setPage($this->parameters->get('page'))
/** @var Transaction $transaction */ // set types of transactions to return.
foreach ($set as $transaction) { ->setTypes([TransactionType::DEPOSIT]);
$currencyId = (int)$transaction->transaction_currency_id;
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
$currencyId = (int)$transactionJournal['currency_id'];
$incomes[$currencyId] = $incomes[$currencyId] ?? '0'; $incomes[$currencyId] = $incomes[$currencyId] ?? '0';
$incomes[$currencyId] = bcadd($incomes[$currencyId], $transaction->transaction_amount); $incomes[$currencyId] = bcadd($incomes[$currencyId], bcmul($transactionJournal['amount'], '-1'));
$sums[$currencyId] = $sums[$currencyId] ?? '0'; $sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount); $sums[$currencyId] = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1'));
} }
// collect expenses: // collect expenses of user using the new group collector.
/** @var TransactionCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end) $collector
->setTypes([TransactionType::WITHDRAWAL]) ->setRange($start, $end)
->withOpposingAccount(); // set page to retrieve
$set = $collector->getTransactions(); ->setPage($this->parameters->get('page'))
/** @var Transaction $transaction */ // set types of transactions to return.
foreach ($set as $transaction) { ->setTypes([TransactionType::WITHDRAWAL]);
$currencyId = (int)$transaction->transaction_currency_id;
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
$currencyId = (int)$transactionJournal['currency_id'];
$expenses[$currencyId] = $expenses[$currencyId] ?? '0'; $expenses[$currencyId] = $expenses[$currencyId] ?? '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transaction->transaction_amount); $expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']);
$sums[$currencyId] = $sums[$currencyId] ?? '0'; $sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount); $sums[$currencyId] = bcadd($sums[$currencyId], $transactionJournal['amount']);
} }
// format amounts: // format amounts:
@@ -315,14 +341,15 @@ class SummaryController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return array * @return array
* @throws Exception
*/ */
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{ {
$return = []; $return = [];
$today = new Carbon; $today = new Carbon;
$available = $this->budgetRepository->getAvailableBudgetWithCurrency($start, $end); $available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets(); $budgets = $this->budgetRepository->getActiveBudgets();
$spentInfo = $this->budgetRepository->spentInPeriodMc($budgets, new Collection, $start, $end); $spentInfo = $this->opsRepository->spentInPeriodMc($budgets, new Collection, $start, $end);
foreach ($available as $currencyId => $amount) { foreach ($available as $currencyId => $amount) {
$currency = $this->currencyRepos->findNull($currencyId); $currency = $this->currencyRepos->findNull($currencyId);
if (null === $currency) { if (null === $currency) {
@@ -389,7 +416,7 @@ class SummaryController extends Controller
$netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date); $netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date);
$return = []; $return = [];
foreach ($netWorthSet as $index => $data) { foreach ($netWorthSet as $data) {
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
$currency = $data['currency']; $currency = $data['currency'];
$amount = round($data['balance'], $currency->decimal_places); $amount = round($data['balance'], $currency->decimal_places);

View File

@@ -24,25 +24,22 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Api\V1\Requests\DateRequest;
use FireflyIII\Api\V1\Requests\TagRequest; use FireflyIII\Api\V1\Requests\TagRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\TagTransformer; use FireflyIII\Transformers\TagTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager; use Illuminate\Support\Collection;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class TagController * Class TagController
@@ -55,7 +52,9 @@ class TagController extends Controller
private $repository; private $repository;
/** /**
* RuleController constructor. * TagController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -74,52 +73,22 @@ class TagController extends Controller
} }
/** /**
* @param Request $request * @param DateRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function cloud(Request $request): JsonResponse public function cloud(DateRequest $request): JsonResponse
{ {
// parameters for cloud: // parameters for boxes:
$start = (string)$request->get('start'); $dates = $request->getAll();
$end = (string)$request->get('end'); $start = $dates['start'];
if ('' === $start || '' === $end) { $end = $dates['end'];
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
// get all tags: // get all tags:
$tags = $this->repository->get(); $tags = $this->repository->get();
$min = null; $cloud = $this->getTagCloud($tags, $start, $end);
$max = 0;
$return = [
'tags' => [],
];
/** @var Tag $tag */
foreach ($tags as $tag) {
$earned = (float)$this->repository->earnedInPeriod($tag, $start, $end);
$spent = (float)$this->repository->spentInPeriod($tag, $start, $end);
$size = ($spent * -1) + $earned;
$min = $min ?? $size;
if ($size > 0) {
$max = $size > $max ? $size : $max;
$return['tags'][] = [
'tag' => $tag->tag,
'id' => $tag->id,
'size' => $size,
];
}
}
foreach ($return['tags'] as $index => $info) {
$return['tags'][$index]['relative'] = $return['tags'][$index]['size'] / $max;
}
$return['min'] = $min;
$return['max'] = $max;
return response()->json($cloud);
return response()->json($return);
} }
/** /**
@@ -128,6 +97,7 @@ class TagController extends Controller
* @param Tag $tag * @param Tag $tag
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Tag $tag): JsonResponse public function delete(Tag $tag): JsonResponse
{ {
@@ -139,16 +109,12 @@ class TagController extends Controller
/** /**
* List all of them. * List all of them.
* *
* @param Request $request * @return JsonResponse
* * @codeCoverageIgnore
* @return JsonResponse]
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// create some objects: $manager = $this->getManager();
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -161,9 +127,6 @@ class TagController extends Controller
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.tags.index') . $this->buildParams()); $paginator->setPath(route('api.v1.tags.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */ /** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class); $transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -177,17 +140,14 @@ class TagController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param Tag $tag * @param Tag $tag
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, Tag $tag): JsonResponse public function show(Tag $tag): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */ /** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class); $transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -208,10 +168,7 @@ class TagController extends Controller
public function store(TagRequest $request): JsonResponse public function store(TagRequest $request): JsonResponse
{ {
$rule = $this->repository->store($request->getAll()); $rule = $this->repository->store($request->getAll());
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */ /** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class); $transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -228,6 +185,7 @@ class TagController extends Controller
* @param Tag $tag * @param Tag $tag
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function transactions(Request $request, Tag $tag): JsonResponse public function transactions(Request $request, Tag $tag): JsonResponse
{ {
@@ -236,34 +194,35 @@ class TagController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setTag($tag);
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on tag.
->setTag($tag)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -283,9 +242,7 @@ class TagController extends Controller
public function update(TagRequest $request, Tag $tag): JsonResponse public function update(TagRequest $request, Tag $tag): JsonResponse
{ {
$rule = $this->repository->update($tag, $request->getAll()); $rule = $this->repository->update($tag, $request->getAll());
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */ /** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class); $transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -295,4 +252,56 @@ class TagController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
} }
/**
* @param array $cloud
* @param float $min
* @param float $max
*
* @return array
*/
private function analyseTagCloud(array $cloud, float $min, float $max): array
{
foreach (array_keys($cloud['tags']) as $index) {
$cloud['tags'][$index]['relative'] = round($cloud['tags'][$index]['size'] / $max, 4);
}
$cloud['min'] = $min;
$cloud['max'] = $max;
return $cloud;
}
/**
* @param Collection $tags
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getTagCloud(Collection $tags, Carbon $start, Carbon $end): array
{
$min = null;
$max = 0;
$cloud = [
'tags' => [],
];
/** @var Tag $tag */
foreach ($tags as $tag) {
$earned = (float)$this->repository->earnedInPeriod($tag, $start, $end);
$spent = (float)$this->repository->spentInPeriod($tag, $start, $end);
$size = ($spent * -1) + $earned;
$min = $min ?? $size;
if ($size > 0) {
$max = $size > $max ? $size : $max;
$cloud['tags'][] = [
'tag' => $tag->tag,
'id' => $tag->id,
'size' => $size,
];
}
}
$cloud = $this->analyseTagCloud($cloud, $min, $max);
return $cloud;
}
} }

View File

@@ -24,43 +24,47 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\TransactionRequest; use FireflyIII\Api\V1\Requests\TransactionStoreRequest;
use FireflyIII\Events\StoredTransactionJournal; use FireflyIII\Api\V1\Requests\TransactionUpdateRequest;
use FireflyIII\Events\UpdatedTransactionJournal; use FireflyIII\Events\StoredTransactionGroup;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Helpers\Filter\NegativeAmountFilter; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Helpers\Filter\PositiveAmountFilter; use FireflyIII\Repositories\Journal\JournalAPIRepositoryInterface;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\PiggyBankEventTransformer; use FireflyIII\Transformers\PiggyBankEventTransformer;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Serializer\JsonApiSerializer; use League\Fractal\Resource\Item;
use Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Class TransactionController * Class TransactionController
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class TransactionController extends Controller class TransactionController extends Controller
{ {
use TransactionFilter; use TransactionFilter;
/** @var TransactionGroupRepositoryInterface Group repository. */
private $groupRepository;
/** @var JournalAPIRepositoryInterface Journal API repos */
private $journalAPIRepository;
/** @var JournalRepositoryInterface The journal repository */ /** @var JournalRepositoryInterface The journal repository */
private $repository; private $repository;
/** /**
* TransactionController constructor. * TransactionController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -70,9 +74,12 @@ class TransactionController extends Controller
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var JournalRepositoryInterface repository */
$this->repository = app(JournalRepositoryInterface::class); $this->repository = app(JournalRepositoryInterface::class);
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
$this->journalAPIRepository = app(JournalAPIRepositoryInterface::class);
$this->repository->setUser($admin); $this->repository->setUser($admin);
$this->groupRepository->setUser($admin);
$this->journalAPIRepository->setUser($admin);
return $next($request); return $next($request);
} }
@@ -80,18 +87,15 @@ class TransactionController extends Controller
} }
/** /**
* @param Request $request * @param TransactionJournal $transactionJournal
* @param Transaction $transaction
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function attachments(Request $request, Transaction $transaction): JsonResponse public function attachments(TransactionJournal $transactionJournal): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $attachments = $this->journalAPIRepository->getAttachments($transactionJournal);
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$attachments = $this->repository->getAttachmentsByTr($transaction);
/** @var AttachmentTransformer $transformer */ /** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class); $transformer = app(AttachmentTransformer::class);
@@ -106,14 +110,29 @@ class TransactionController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @param \FireflyIII\Models\Transaction $transaction * @param TransactionGroup $transactionGroup
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(Transaction $transaction): JsonResponse public function delete(TransactionGroup $transactionGroup): JsonResponse
{ {
$journal = $transaction->transactionJournal; $this->repository->destroyGroup($transactionGroup);
$this->repository->destroy($journal);
return response()->json([], 204);
}
/**
* Remove the specified resource from storage.
*
* @param TransactionJournal $transactionJournal
*
* @codeCoverageIgnore
* @return JsonResponse
*/
public function deleteJournal(TransactionJournal $transactionJournal): JsonResponse
{
$this->repository->destroyJournal($transactionJournal);
return response()->json([], 204); return response()->json([], 204);
} }
@@ -124,6 +143,7 @@ class TransactionController extends Controller
* @param Request $request * @param Request $request
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(Request $request): JsonResponse
{ {
@@ -132,33 +152,34 @@ class TransactionController extends Controller
$this->parameters->set('type', $type); $this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type')); $types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
if (\in_array(TransactionType::TRANSFER, $types, true)) { // use new group collector:
$collector->removeFilter(InternalTransferFilter::class); /** @var GroupCollectorInterface $collector */
} $collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
} }
$collector->setLimit($pageSize)->setPage($this->parameters->get('page')); $paginator = $collector->getPaginatedGroups();
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection(); $transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new FractalCollection($transactions, $transformer, 'transactions');
@@ -168,18 +189,15 @@ class TransactionController extends Controller
} }
/** /**
* @param Request $request * @param TransactionJournal $transactionJournal
* @param Transaction $transaction
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function piggyBankEvents(Request $request, Transaction $transaction): JsonResponse public function piggyBankEvents(TransactionJournal $transactionJournal): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $events = $this->journalAPIRepository->getPiggyBankEvents($transactionJournal);
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$events = $this->repository->getPiggyBankEventsbyTr($transaction);
/** @var PiggyBankEventTransformer $transformer */ /** @var PiggyBankEventTransformer $transformer */
$transformer = app(PiggyBankEventTransformer::class); $transformer = app(PiggyBankEventTransformer::class);
@@ -194,38 +212,34 @@ class TransactionController extends Controller
/** /**
* Show a single transaction. * Show a single transaction.
* *
* @param Request $request * @param TransactionGroup $transactionGroup
* @param Transaction $transaction
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, Transaction $transaction): JsonResponse public function show(TransactionGroup $transactionGroup): JsonResponse
{ {
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; /** @var User $admin */
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on transaction group.
->setTransactionGroup($transactionGroup)
// all info needed for the API:
->withAPIInformation();
// collect transactions using the journal collector $selectedGroup = $collector->getGroups()->first();
$collector = app(TransactionCollectorInterface::class); if (null === $selectedGroup) {
$collector->setUser(auth()->user()); throw new NotFoundHttpException();
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
// filter on specific journals.
$collector->setJournals(new Collection([$transaction->transactionJournal]));
// add filter to remove transactions:
$transactionType = $transaction->transactionJournal->transactionType->type;
if ($transactionType === TransactionType::WITHDRAWAL) {
$collector->addFilter(PositiveAmountFilter::class);
} }
if (!($transactionType === TransactionType::WITHDRAWAL)) { /** @var TransactionGroupTransformer $transformer */
$collector->addFilter(NegativeAmountFilter::class); // @codeCoverageIgnore $transformer = app(TransactionGroupTransformer::class);
}
$transactions = $collector->getTransactions();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions'); $resource = new Item($selectedGroup, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
} }
@@ -233,48 +247,43 @@ class TransactionController extends Controller
/** /**
* Store a new transaction. * Store a new transaction.
* *
* @param TransactionRequest $request * @param TransactionStoreRequest $request
* *
* @param JournalRepositoryInterface $repository
*
* @throws FireflyException
* @return JsonResponse * @return JsonResponse
*/ */
public function store(TransactionRequest $request, JournalRepositoryInterface $repository): JsonResponse public function store(TransactionStoreRequest $request): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$data['user'] = auth()->user()->id; $data['user'] = auth()->user()->id;
$journal = $repository->store($data);
event(new StoredTransactionJournal($journal)); Log::channel('audit')
->info('Store new transaction over API.', $data);
$manager = new Manager(); $transactionGroup = $this->groupRepository->store($data);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// collect transactions using the journal collector event(new StoredTransactionGroup($transactionGroup));
$collector = app(TransactionCollectorInterface::class);
$collector->setUser(auth()->user());
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
// filter on specific journals.
$collector->setJournals(new Collection([$journal]));
// add filter to remove transactions: $manager = $this->getManager();
$transactionType = $journal->transactionType->type; /** @var User $admin */
if ($transactionType === TransactionType::WITHDRAWAL) { $admin = auth()->user();
$collector->addFilter(PositiveAmountFilter::class); // use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on transaction group.
->setTransactionGroup($transactionGroup)
// all info needed for the API:
->withAPIInformation();
$selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) {
throw new NotFoundHttpException(); // @codeCoverageIgnore
} }
if (!($transactionType === TransactionType::WITHDRAWAL)) { /** @var TransactionGroupTransformer $transformer */
$collector->addFilter(NegativeAmountFilter::class); $transformer = app(TransactionGroupTransformer::class);
}
$transactions = $collector->getTransactions();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new Item($selectedGroup, $transformer, 'transactions');
$resource = new FractalCollection($transactions, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
} }
@@ -283,47 +292,40 @@ class TransactionController extends Controller
/** /**
* Update a transaction. * Update a transaction.
* *
* @param TransactionRequest $request * @param TransactionUpdateRequest $request
* @param JournalRepositoryInterface $repository * @param TransactionGroup $transactionGroup
* @param Transaction $transaction
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function update(TransactionRequest $request, JournalRepositoryInterface $repository, Transaction $transaction): JsonResponse public function update(TransactionUpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
{ {
Log::debug('Now in update routine.');
$data = $request->getAll(); $data = $request->getAll();
$data['user'] = auth()->user()->id; $transactionGroup = $this->groupRepository->update($transactionGroup, $data);
$journal = $repository->update($transaction->transactionJournal, $data); $manager = $this->getManager();
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
event(new UpdatedTransactionJournal($journal)); event(new UpdatedTransactionGroup($transactionGroup));
// needs a lot of extra data to match the journal collector. Or just expand that one. /** @var User $admin */
// collect transactions using the journal collector $admin = auth()->user();
$collector = app(TransactionCollectorInterface::class); // use new group collector:
$collector->setUser(auth()->user()); /** @var GroupCollectorInterface $collector */
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); $collector = app(GroupCollectorInterface::class);
// filter on specific journals. $collector
$collector->setJournals(new Collection([$journal])); ->setUser($admin)
// filter on transaction group.
->setTransactionGroup($transactionGroup)
// all info needed for the API:
->withAPIInformation();
// add filter to remove transactions: $selectedGroup = $collector->getGroups()->first();
$transactionType = $journal->transactionType->type; if (null === $selectedGroup) {
if ($transactionType === TransactionType::WITHDRAWAL) { throw new NotFoundHttpException(); // @codeCoverageIgnore
$collector->addFilter(PositiveAmountFilter::class);
} }
if (!($transactionType === TransactionType::WITHDRAWAL)) { /** @var TransactionGroupTransformer $transformer */
$collector->addFilter(NegativeAmountFilter::class); $transformer = app(TransactionGroupTransformer::class);
}
$transactions = $collector->getTransactions();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new Item($selectedGroup, $transformer, 'transactions');
$resource = new FractalCollection($transactions, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -34,11 +34,9 @@ use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class TransactionLinkController * Class TransactionLinkController
@@ -53,7 +51,9 @@ class TransactionLinkController extends Controller
private $repository; private $repository;
/** /**
* JournalLinkController constructor. * TransactionLinkController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -80,6 +80,7 @@ class TransactionLinkController extends Controller
* @param TransactionJournalLink $link * @param TransactionJournalLink $link
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function delete(TransactionJournalLink $link): JsonResponse public function delete(TransactionJournalLink $link): JsonResponse
{ {
@@ -93,16 +94,15 @@ class TransactionLinkController extends Controller
* *
* @param Request $request * @param Request $request
* *
* @return JsonResponse] * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(Request $request): JsonResponse
{ {
// create some objects: // create some objects:
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// read type from URI // read type from URI
$name = $request->get('name') ?? null; $name = $request->get('name');
// types to get, page size: // types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -117,9 +117,6 @@ class TransactionLinkController extends Controller
$paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.transaction_links.index') . $this->buildParams()); $paginator->setPath(route('api.v1.transaction_links.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TransactionLinkTransformer $transformer */ /** @var TransactionLinkTransformer $transformer */
$transformer = app(TransactionLinkTransformer::class); $transformer = app(TransactionLinkTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
@@ -134,16 +131,14 @@ class TransactionLinkController extends Controller
/** /**
* List single resource. * List single resource.
* *
* @param Request $request
* @param TransactionJournalLink $journalLink * @param TransactionJournalLink $journalLink
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, TransactionJournalLink $journalLink): JsonResponse public function show(TransactionJournalLink $journalLink): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TransactionLinkTransformer $transformer */ /** @var TransactionLinkTransformer $transformer */
$transformer = app(TransactionLinkTransformer::class); $transformer = app(TransactionLinkTransformer::class);
@@ -165,7 +160,7 @@ class TransactionLinkController extends Controller
*/ */
public function store(TransactionLinkRequest $request): JsonResponse public function store(TransactionLinkRequest $request): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$data = $request->getAll(); $data = $request->getAll();
$inward = $this->journalRepository->findNull($data['inward_id'] ?? 0); $inward = $this->journalRepository->findNull($data['inward_id'] ?? 0);
$outward = $this->journalRepository->findNull($data['outward_id'] ?? 0); $outward = $this->journalRepository->findNull($data['outward_id'] ?? 0);
@@ -183,7 +178,6 @@ class TransactionLinkController extends Controller
$resource = new Item($journalLink, $transformer, 'transaction_links'); $resource = new Item($journalLink, $transformer, 'transaction_links');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
} }
/** /**
@@ -197,7 +191,7 @@ class TransactionLinkController extends Controller
*/ */
public function update(TransactionLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse public function update(TransactionLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse
{ {
$manager = new Manager; $manager = $this->getManager();
$data = $request->getAll(); $data = $request->getAll();
$data['inward'] = $this->journalRepository->findNull($data['inward_id'] ?? 0); $data['inward'] = $this->journalRepository->findNull($data['inward_id'] ?? 0);
$data['outward'] = $this->journalRepository->findNull($data['outward_id'] ?? 0); $data['outward'] = $this->journalRepository->findNull($data['outward_id'] ?? 0);

View File

@@ -24,25 +24,22 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers; namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\UserRequest; use FireflyIII\Api\V1\Requests\UserStoreRequest;
use FireflyIII\Api\V1\Requests\UserUpdateRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\UserTransformer; use FireflyIII\Transformers\UserTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class UserController. * Class UserController.
* *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class UserController extends Controller class UserController extends Controller
{ {
@@ -52,6 +49,8 @@ class UserController extends Controller
/** /**
* UserController constructor. * UserController constructor.
*
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -69,10 +68,11 @@ class UserController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @param \FireflyIII\User $user * @param User $user
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
* @codeCoverageIgnore
*/ */
public function delete(User $user): JsonResponse public function delete(User $user): JsonResponse
{ {
@@ -89,19 +89,14 @@ class UserController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param Request $request
*
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function index(Request $request): JsonResponse public function index(): JsonResponse
{ {
// user preferences // user preferences
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$manager = $this->getManager();
// make manager
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// build collection // build collection
$collection = $this->repository->all(); $collection = $this->repository->all();
@@ -126,18 +121,15 @@ class UserController extends Controller
/** /**
* Show a single user. * Show a single user.
* *
* @param Request $request
* @param User $user * @param User $user
* *
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function show(Request $request, User $user): JsonResponse public function show(User $user): JsonResponse
{ {
// make manager // make manager
$manager = new Manager(); $manager = $this->getManager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// make resource // make resource
/** @var UserTransformer $transformer */ /** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class); $transformer = app(UserTransformer::class);
@@ -151,19 +143,15 @@ class UserController extends Controller
/** /**
* Store a new user. * Store a new user.
* *
* @param UserRequest $request * @param UserStoreRequest $request
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function store(UserRequest $request): JsonResponse public function store(UserStoreRequest $request): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$user = $this->repository->store($data); $user = $this->repository->store($data);
$manager = $this->getManager();
// make manager
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// make resource // make resource
@@ -179,21 +167,16 @@ class UserController extends Controller
/** /**
* Update a user. * Update a user.
* *
* @param UserRequest $request * @param UserUpdateRequest $request
* @param User $user * @param User $user
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function update(UserRequest $request, User $user): JsonResponse public function update(UserUpdateRequest $request, User $user): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$user = $this->repository->update($user, $data); $user = $this->repository->update($user, $data);
$manager = $this->getManager();
// make manager
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// make resource // make resource
/** @var UserTransformer $transformer */ /** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class); $transformer = app(UserTransformer::class);

View File

@@ -0,0 +1,84 @@
<?php
/**
* AccountStoreRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
/**
* Class AccountStoreRequest
*
* @codeCoverageIgnore
*/
class AccountStoreRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$accountRoles = implode(',', config('firefly.accountRoles'));
$types = implode(',', array_keys(config('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [
'name' => 'required|min:1|uniqueAccountForUser',
'type' => 'required|' . sprintf('in:%s', $types),
'iban' => 'iban|nullable',
'bic' => 'bic|nullable',
'account_number' => 'between:1,255|nullable|uniqueAccountNumberForUser',
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
'virtual_balance' => 'numeric|nullable',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'active' => [new IsBoolean],
'include_net_worth' => [new IsBoolean],
'account_role' => sprintf('in:%s|required_if:type,asset', $accountRoles),
'credit_card_type' => sprintf('in:%s|required_if:account_role,ccAsset', $ccPaymentTypes),
'monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage',
'liability_amount' => 'required_if:type,liability|min:0|numeric',
'liability_start_date' => 'required_if:type,liability|date',
'interest' => 'required_if:type,liability|between:0,100|numeric',
'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly',
'notes' => 'min:0|max:65536',
];
return $rules;
}
}

View File

@@ -1,7 +1,7 @@
<?php <?php
/** /**
* AccountRequest.php * AccountUpdateRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2018 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
@@ -27,9 +27,11 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
/** /**
* Class AccountRequest * Class AccountUpdateRequest
*
* @codeCoverageIgnore
*/ */
class AccountRequest extends Request class AccountUpdateRequest extends Request
{ {
/** /**
@@ -44,14 +46,12 @@ class AccountRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): array public function getUpdateData(): array
{ {
$active = true; $active = null;
$includeNetWorth = true; $includeNetWorth = null;
if (null !== $this->get('active')) { if (null !== $this->get('active')) {
$active = $this->boolean('active'); $active = $this->boolean('active');
} }
@@ -60,31 +60,31 @@ class AccountRequest extends Request
} }
$data = [ $data = [
'name' => $this->string('name'), 'name' => $this->nullableString('name'),
'active' => $active, 'active' => $active,
'include_net_worth' => $includeNetWorth, 'include_net_worth' => $includeNetWorth,
'accountType' => $this->string('type'), 'account_type' => $this->nullableString('type'),
'account_type_id' => null, 'account_type_id' => null,
'currency_id' => $this->integer('currency_id'), 'currency_id' => $this->nullableInteger('currency_id'),
'currency_code' => $this->string('currency_code'), 'currency_code' => $this->nullableString('currency_code'),
'virtualBalance' => $this->string('virtual_balance'), 'virtual_balance' => $this->nullableString('virtual_balance'),
'iban' => $this->string('iban'), 'iban' => $this->nullableString('iban'),
'BIC' => $this->string('bic'), 'BIC' => $this->nullableString('bic'),
'accountNumber' => $this->string('account_number'), 'account_number' => $this->nullableString('account_number'),
'accountRole' => $this->string('account_role'), 'account_role' => $this->nullableString('account_role'),
'openingBalance' => $this->string('opening_balance'), 'opening_balance' => $this->nullableString('opening_balance'),
'openingBalanceDate' => $this->date('opening_balance_date'), 'opening_balance_date' => $this->date('opening_balance_date'),
'ccType' => $this->string('credit_card_type'), 'cc_type' => $this->nullableString('credit_card_type'),
'ccMonthlyPaymentDate' => $this->string('monthly_payment_date'), 'cc_Monthly_payment_date' => $this->nullableString('monthly_payment_date'),
'notes' => $this->string('notes'), 'notes' => $this->nullableString('notes'),
'interest' => $this->string('interest'), 'interest' => $this->nullableString('interest'),
'interest_period' => $this->string('interest_period'), 'interest_period' => $this->nullableString('interest_period'),
]; ];
if ('liability' === $data['accountType']) { if ('liability' === $data['account_type']) {
$data['openingBalance'] = bcmul($this->string('liability_amount'), '-1'); $data['opening_balance'] = bcmul($this->nullableString('liability_amount'), '-1');
$data['openingBalanceDate'] = $this->date('liability_start_date'); $data['opening_balance_date'] = $this->date('liability_start_date');
$data['accountType'] = $this->string('liability_type'); $data['account_type'] = $this->nullableString('liability_type');
$data['account_type_id'] = null; $data['account_type_id'] = null;
} }
@@ -98,15 +98,16 @@ class AccountRequest extends Request
*/ */
public function rules(): array public function rules(): array
{ {
$account = $this->route()->parameter('account');
$accountRoles = implode(',', config('firefly.accountRoles')); $accountRoles = implode(',', config('firefly.accountRoles'));
$types = implode(',', array_keys(config('firefly.subTitlesByIdentifier'))); $types = implode(',', array_keys(config('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes'))); $ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [ $rules = [
'name' => 'required|min:1|uniqueAccountForUser', 'name' => sprintf('min:1|uniqueAccountForUser:%d', $account->id),
'type' => 'required|in:' . $types, 'type' => sprintf('in:%s', $types),
'iban' => 'iban|nullable', 'iban' => 'iban|nullable',
'bic' => 'bic|nullable', 'bic' => 'bic|nullable',
'account_number' => 'between:1,255|nullable|uniqueAccountNumberForUser', 'account_number' => sprintf('between:1,255|nullable|uniqueAccountNumberForUser:%d', $account->id),
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable', 'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
'opening_balance_date' => 'date|required_with:opening_balance|nullable', 'opening_balance_date' => 'date|required_with:opening_balance|nullable',
'virtual_balance' => 'numeric|nullable', 'virtual_balance' => 'numeric|nullable',
@@ -114,8 +115,8 @@ class AccountRequest extends Request
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'active' => [new IsBoolean], 'active' => [new IsBoolean],
'include_net_worth' => [new IsBoolean], 'include_net_worth' => [new IsBoolean],
'account_role' => 'in:' . $accountRoles . '|required_if:type,asset', 'account_role' => sprintf('in:%s|required_if:type,asset', $accountRoles),
'credit_card_type' => 'in:' . $ccPaymentTypes . '|required_if:account_role,ccAsset', 'credit_card_type' => sprintf('in:%s|required_if:account_role,ccAsset', $ccPaymentTypes),
'monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull', 'monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage', 'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage',
'liability_amount' => 'required_if:type,liability|min:0|numeric', 'liability_amount' => 'required_if:type,liability|min:0|numeric',
@@ -124,17 +125,6 @@ class AccountRequest extends Request
'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly', 'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly',
'notes' => 'min:0|max:65536', 'notes' => 'min:0|max:65536',
]; ];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
$account = $this->route()->parameter('account');
$rules['name'] .= ':' . $account->id;
$rules['account_number'] .= ':' . $account->id;
$rules['type'] = 'in:' . $types;
break;
}
return $rules; return $rules;
} }

View File

@@ -1,7 +1,7 @@
<?php <?php
/** /**
* AttachmentRequest.php * AttachmentStoreRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2019 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
* *
@@ -25,14 +25,15 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\IsValidAttachmentModel; use FireflyIII\Rules\IsValidAttachmentModel;
/** /**
* Class AttachmentRequest * Class AttachmentStoreRequest
*
* @codeCoverageIgnore
*/ */
class AttachmentRequest extends Request class AttachmentStoreRequest extends Request
{ {
/** /**
* Authorize logged in users. * Authorize logged in users.
@@ -69,31 +70,21 @@ class AttachmentRequest extends Request
public function rules(): array public function rules(): array
{ {
$models = implode( $models = implode(
',', [ ',',
[
str_replace('FireflyIII\\Models\\', '', Bill::class), str_replace('FireflyIII\\Models\\', '', Bill::class),
str_replace('FireflyIII\\Models\\', '', ImportJob::class), str_replace('FireflyIII\\Models\\', '', ImportJob::class),
str_replace('FireflyIII\\Models\\', '', TransactionJournal::class), str_replace('FireflyIII\\Models\\', '', TransactionJournal::class),
str_replace('FireflyIII\\Models\\', '', Transaction::class),
] ]
); );
$model = $this->string('model'); $model = $this->string('model');
$rules = [
return [
'filename' => 'required|between:1,255', 'filename' => 'required|between:1,255',
'title' => 'between:1,255', 'title' => 'between:1,255',
'notes' => 'between:1,65000', 'notes' => 'between:1,65000',
'model' => sprintf('required|in:%s', $models), 'model' => sprintf('required|in:%s', $models),
'model_id' => ['required', 'numeric', new IsValidAttachmentModel($model)], 'model_id' => ['required', 'numeric', new IsValidAttachmentModel($model)],
]; ];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
unset($rules['model'], $rules['model_id']);
$rules['filename'] = 'between:1,255';
break;
}
return $rules;
} }
} }

View File

@@ -0,0 +1,73 @@
<?php
/**
* AttachmentUpdateRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
/**
* ClassAttachmentUpdateRequest
*
* @codeCoverageIgnore
*/
class AttachmentUpdateRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
return [
'filename' => $this->string('filename'),
'title' => $this->string('title'),
'notes' => $this->string('notes'),
'model' => $this->string('model'),
'model_id' => $this->integer('model_id'),
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
return [
'filename' => 'between:1,255',
'title' => 'between:1,255',
'notes' => 'between:1,65000',
];
}
}

View File

@@ -25,6 +25,8 @@ namespace FireflyIII\Api\V1\Requests;
/** /**
* Class AvailableBudgetRequest * Class AvailableBudgetRequest
*
* @codeCoverageIgnore
*/ */
class AvailableBudgetRequest extends Request class AvailableBudgetRequest extends Request
{ {

View File

@@ -29,6 +29,10 @@ use Illuminate\Validation\Validator;
/** /**
* Class BillRequest * Class BillRequest
*
* TODO AFTER 4.8,0: split this into two request classes.
*
* @codeCoverageIgnore
*/ */
class BillRequest extends Request class BillRequest extends Request
{ {
@@ -76,6 +80,7 @@ class BillRequest extends Request
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
* *
* @return array * @return array
*
*/ */
public function rules(): array public function rules(): array
{ {
@@ -88,7 +93,6 @@ class BillRequest extends Request
'date' => 'required|date', 'date' => 'required|date',
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly', 'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'between:0,31', 'skip' => 'between:0,31',
'automatch' => [new IsBoolean],
'active' => [new IsBoolean], 'active' => [new IsBoolean],
'notes' => 'between:1,65536', 'notes' => 'between:1,65536',
]; ];
@@ -115,7 +119,7 @@ class BillRequest extends Request
public function withValidator(Validator $validator): void public function withValidator(Validator $validator): void
{ {
$validator->after( $validator->after(
function (Validator $validator) { static function (Validator $validator) {
$data = $validator->getData(); $data = $validator->getData();
$min = (float)($data['amount_min'] ?? 0); $min = (float)($data['amount_min'] ?? 0);
$max = (float)($data['amount_max'] ?? 0); $max = (float)($data['amount_max'] ?? 0);

View File

@@ -23,9 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests; namespace FireflyIII\Api\V1\Requests;
/** /**
* Class BudgetLimitRequest * Class BudgetLimitRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class BudgetLimitRequest extends Request class BudgetLimitRequest extends Request
{ {

View File

@@ -28,6 +28,9 @@ use FireflyIII\Rules\IsBoolean;
/** /**
* Class BudgetRequest * Class BudgetRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class BudgetRequest extends Request class BudgetRequest extends Request
{ {

View File

@@ -27,6 +27,9 @@ use FireflyIII\Models\Category;
/** /**
* Class CategoryRequest * Class CategoryRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class CategoryRequest extends Request class CategoryRequest extends Request
{ {

View File

@@ -28,6 +28,8 @@ use FireflyIII\Rules\IsBoolean;
/** /**
* Class ConfigurationRequest * Class ConfigurationRequest
*
* @codeCoverageIgnore
*/ */
class ConfigurationRequest extends Request class ConfigurationRequest extends Request
{ {

View File

@@ -28,6 +28,9 @@ use FireflyIII\Rules\IsBoolean;
/** /**
* Class CurrencyRequest * Class CurrencyRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class CurrencyRequest extends Request class CurrencyRequest extends Request
{ {
@@ -78,7 +81,7 @@ class CurrencyRequest extends Request
$rules = [ $rules = [
'name' => 'required|between:1,255|unique:transaction_currencies,name', 'name' => 'required|between:1,255|unique:transaction_currencies,name',
'code' => 'required|between:3,3|unique:transaction_currencies,code', 'code' => 'required|between:3,3|unique:transaction_currencies,code',
'symbol' => 'required|between:1,5|unique:transaction_currencies,symbol', 'symbol' => 'required|between:1,8|unique:transaction_currencies,symbol',
'decimal_places' => 'between:0,20|numeric|min:0|max:20', 'decimal_places' => 'between:0,20|numeric|min:0|max:20',
'enabled' => [new IsBoolean()], 'enabled' => [new IsBoolean()],
'default' => [new IsBoolean()], 'default' => [new IsBoolean()],
@@ -92,8 +95,8 @@ class CurrencyRequest extends Request
case 'PATCH': case 'PATCH':
$currency = $this->route()->parameter('currency_code'); $currency = $this->route()->parameter('currency_code');
$rules['name'] = 'required|between:1,255|unique:transaction_currencies,name,' . $currency->id; $rules['name'] = 'required|between:1,255|unique:transaction_currencies,name,' . $currency->id;
$rules['code'] = 'required|between:1,255|unique:transaction_currencies,code,' . $currency->id; $rules['code'] = 'required|between:3,3|unique:transaction_currencies,code,' . $currency->id;
$rules['symbol'] = 'required|between:1,255|unique:transaction_currencies,symbol,' . $currency->id; $rules['symbol'] = 'required|between:1,8|unique:transaction_currencies,symbol,' . $currency->id;
break; break;
} }

View File

@@ -1,7 +1,8 @@
<?php <?php
/** /**
* ReconciliationUpdateRequest.php * DateRequest.php
* Copyright (c) 2017 thegrumpydictator@gmail.com * Copyright (c) 2019 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
* *
@@ -18,54 +19,53 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>. * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Http\Requests; namespace FireflyIII\Api\V1\Requests;
/** /**
* Class ReconciliationUpdateRequest. * Request class for end points that require date parameters.
*
* Class DateRequest
*/ */
class ReconciliationUpdateRequest extends Request class DateRequest extends Request
{ {
/** /**
* Verify the request. * Authorize logged in users.
* *
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
{ {
// Only allow logged in users // Only allow authenticated users
return auth()->check(); return auth()->check();
} }
/** /**
* Returns and validates the data required to update a reconciliation. * Get all data from the request.
* *
* @return array * @return array
*/ */
public function getJournalData(): array public function getAll(): array
{ {
$data = [ return [
'tags' => explode(',', $this->string('tags')), 'start' => $this->date('start'),
'amount' => $this->string('amount'), 'end' => $this->date('end'),
'category' => $this->string('category'),
]; ];
return $data;
} }
/** /**
* Rules for this request. * The rules that the incoming request must be matched against.
* *
* @return array * @return array
*/ */
public function rules(): array public function rules(): array
{ {
$rules = [ return [
'amount' => 'numeric|required', 'start' => 'required|date',
'category' => 'between:1,255|nullable', 'end' => 'required|date|after:start',
]; ];
return $rules;
} }
} }

View File

@@ -29,6 +29,9 @@ use Illuminate\Validation\Rule;
/** /**
* *
* Class LinkTypeRequest * Class LinkTypeRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class LinkTypeRequest extends Request class LinkTypeRequest extends Request
{ {

View File

@@ -25,10 +25,15 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Rules\IsAssetAccountId; use FireflyIII\Rules\IsAssetAccountId;
use FireflyIII\Rules\LessThanPiggyTarget;
use FireflyIII\Rules\ZeroOrMore;
/** /**
* *
* Class PiggyBankRequest * Class PiggyBankRequest
*
* @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class PiggyBankRequest extends Request class PiggyBankRequest extends Request
{ {
@@ -50,14 +55,11 @@ class PiggyBankRequest extends Request
*/ */
public function getAll(): array public function getAll(): array
{ {
$current = $this->string('current_amount');
$current = '' === $current ? '0' : $current;
return [ return [
'name' => $this->string('name'), 'name' => $this->string('name'),
'account_id' => $this->integer('account_id'), 'account_id' => $this->integer('account_id'),
'targetamount' => $this->string('target_amount'), 'targetamount' => $this->string('target_amount'),
'current_amount' => $current, 'current_amount' => $this->string('current_amount'),
'startdate' => $this->date('start_date'), 'startdate' => $this->date('start_date'),
'targetdate' => $this->date('target_date'), 'targetdate' => $this->date('target_date'),
'notes' => $this->string('notes'), 'notes' => $this->string('notes'),
@@ -73,9 +75,7 @@ class PiggyBankRequest extends Request
{ {
$rules = [ $rules = [
'name' => 'required|between:1,255|uniquePiggyBankForUser', 'name' => 'required|between:1,255|uniquePiggyBankForUser',
'account_id' => ['required', 'belongsToUser:accounts', new IsAssetAccountId], 'current_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount'],
'target_amount' => 'required|numeric|more:0',
'current_amount' => 'numeric|more:0|lte:target_amount',
'start_date' => 'date|nullable', 'start_date' => 'date|nullable',
'target_date' => 'date|nullable|after:start_date', 'target_date' => 'date|nullable|after:start_date',
'notes' => 'max:65000', 'notes' => 'max:65000',
@@ -88,7 +88,10 @@ class PiggyBankRequest extends Request
case 'PATCH': case 'PATCH':
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
$piggyBank = $this->route()->parameter('piggyBank'); $piggyBank = $this->route()->parameter('piggyBank');
$rules['name'] = 'required|between:1,255|uniquePiggyBankForUser:' . $piggyBank->id; $rules['name'] = 'between:1,255|uniquePiggyBankForUser:' . $piggyBank->id;
$rules['account_id'] = ['belongsToUser:accounts', new IsAssetAccountId];
$rules['target_amount'] = 'numeric|more:0';
$rules['current_amount'] = ['numeric', new ZeroOrMore, new LessThanPiggyTarget];
break; break;
} }

View File

@@ -26,6 +26,8 @@ namespace FireflyIII\Api\V1\Requests;
/** /**
* *
* Class PreferenceRequest * Class PreferenceRequest
*
* @codeCoverageIgnore
*/ */
class PreferenceRequest extends Request class PreferenceRequest extends Request
{ {

View File

@@ -1,7 +1,7 @@
<?php <?php
/** /**
* RecurrenceRequest.php * RecurrenceStoreRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2019 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
* *
@@ -31,9 +31,9 @@ use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
/** /**
* Class RecurrenceRequest * Class RecurrenceStoreRequest
*/ */
class RecurrenceRequest extends Request class RecurrenceStoreRequest extends Request
{ {
use RecurrenceValidation, TransactionValidation; use RecurrenceValidation, TransactionValidation;
@@ -74,11 +74,6 @@ class RecurrenceRequest extends Request
'apply_rules' => $applyRules, 'apply_rules' => $applyRules,
'active' => $active, 'active' => $active,
], ],
'meta' => [
'piggy_bank_id' => $this->integer('piggy_bank_id'),
'piggy_bank_name' => $this->string('piggy_bank_name'),
'tags' => explode(',', $this->string('tags')),
],
'transactions' => $this->getTransactionData(), 'transactions' => $this->getTransactionData(),
'repetitions' => $this->getRepetitionData(), 'repetitions' => $this->getRepetitionData(),
]; ];
@@ -99,13 +94,11 @@ class RecurrenceRequest extends Request
'type' => 'required|in:withdrawal,transfer,deposit', 'type' => 'required|in:withdrawal,transfer,deposit',
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', 'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
'description' => 'between:1,65000', 'description' => 'between:1,65000',
'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')), 'first_date' => 'required|date',
'apply_rules' => [new IsBoolean], 'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean], 'active' => [new IsBoolean],
'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')), 'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')),
'nr_of_repetitions' => 'numeric|between:1,31', 'nr_of_repetitions' => 'numeric|between:1,31',
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly', 'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10', 'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31', 'repetitions.*.skip' => 'required|numeric|between:0,31',
@@ -117,13 +110,20 @@ class RecurrenceRequest extends Request
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code', 'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id', 'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code', 'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser], 'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable', 'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser], 'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable', 'transactions.*.destination_name' => 'between:1,255|nullable',
// new and updated fields:
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.piggy_bank_id' => ['numeric', 'mustExist:piggy_banks,id', new BelongsUser],
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.tags' => 'between:1,64000',
]; ];
} }
@@ -139,7 +139,7 @@ class RecurrenceRequest extends Request
{ {
$validator->after( $validator->after(
function (Validator $validator) { function (Validator $validator) {
$this->validateOneTransaction($validator); $this->validateOneRecurrenceTransaction($validator);
$this->validateOneRepetition($validator); $this->validateOneRepetition($validator);
$this->validateRecurrenceRepetition($validator); $this->validateRecurrenceRepetition($validator);
$this->validateRepetitionMoment($validator); $this->validateRepetitionMoment($validator);
@@ -149,7 +149,6 @@ class RecurrenceRequest extends Request
); );
} }
/** /**
* Returns the repetition data as it is found in the submitted data. * Returns the repetition data as it is found in the submitted data.
* *
@@ -161,6 +160,9 @@ class RecurrenceRequest extends Request
// repetition data: // repetition data:
/** @var array $repetitions */ /** @var array $repetitions */
$repetitions = $this->get('repetitions'); $repetitions = $this->get('repetitions');
if (null === $repetitions) {
return [];
}
/** @var array $repetition */ /** @var array $repetition */
foreach ($repetitions as $repetition) { foreach ($repetitions as $repetition) {
$return[] = [ $return[] = [
@@ -179,8 +181,6 @@ class RecurrenceRequest extends Request
* standards but it just has a lot of ??-statements because of the fields that may or may not exist. * standards but it just has a lot of ??-statements because of the fields that may or may not exist.
* *
* @return array * @return array
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
private function getTransactionData(): array private function getTransactionData(): array
{ {
@@ -188,6 +188,9 @@ class RecurrenceRequest extends Request
// transaction data: // transaction data:
/** @var array $transactions */ /** @var array $transactions */
$transactions = $this->get('transactions'); $transactions = $this->get('transactions');
if (null === $transactions) {
return [];
}
/** @var array $transaction */ /** @var array $transaction */
foreach ($transactions as $transaction) { foreach ($transactions as $transaction) {
$return[] = [ $return[] = [
@@ -197,15 +200,21 @@ class RecurrenceRequest extends Request
'foreign_amount' => $transaction['foreign_amount'] ?? null, 'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, 'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, 'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, 'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, 'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, 'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'description' => $transaction['description'], 'description' => $transaction['description'],
'type' => $this->string('type'),
// new and updated fields:
'piggy_bank_id' => isset($transaction['piggy_bank_id']) ? (int)$transaction['piggy_bank_id'] : null,
'piggy_bank_name' => $transaction['piggy_bank_name'] ?? null,
'tags' => $transaction['tags'] ?? [],
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
]; ];
} }

View File

@@ -0,0 +1,225 @@
<?php
/**
* RecurrenceUpdateRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Recurrence;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
/**
* Class RecurrenceUpdateRequest
*/
class RecurrenceUpdateRequest extends Request
{
use RecurrenceValidation, TransactionValidation;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$active = null;
$applyRules = null;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('apply_rules')) {
$applyRules = $this->boolean('apply_rules');
}
$return = [
'recurrence' => [
'type' => $this->nullableString('type'),
'title' => $this->nullableString('title'),
'description' => $this->nullableString('description'),
'first_date' => $this->date('first_date'),
'notes' => $this->nullableString('notes'),
'repeat_until' => $this->date('repeat_until'),
'nr_of_repetitions' => $this->nullableInteger('nr_of_repetitions'),
'apply_rules' => $applyRules,
'active' => $active,
],
'transactions' => $this->getTransactionData(),
'repetitions' => $this->getRepetitionData(),
];
return $return;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
/** @var Recurrence $recurrence */
$recurrence = $this->route()->parameter('recurrence');
return [
'type' => 'in:withdrawal,transfer,deposit',
'title' => sprintf('between:1,255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id),
'description' => 'between:1,65000',
'first_date' => 'date',
'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean],
'repeat_until' => 'date',
'nr_of_repetitions' => 'numeric|between:1,31',
'repetitions.*.type' => 'in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
// new and updated fields:
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.piggy_bank_id' => ['numeric', 'mustExist:piggy_banks,id', new BelongsUser],
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.tags' => 'between:1,64000',
];
}
/**
* Configure the validator instance.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
$this->validateOneRecurrenceTransactionUpdate($validator);
$this->validateOneRepetitionUpdate($validator);
$this->validateRecurrenceRepetition($validator);
$this->validateRepetitionMoment($validator);
$this->validateForeignCurrencyInformation($validator);
$this->valUpdateAccountInfo($validator);
}
);
}
/**
* Returns the repetition data as it is found in the submitted data.
*
* @return array|null
*/
private function getRepetitionData(): ?array
{
$return = [];
// repetition data:
/** @var array $repetitions */
$repetitions = $this->get('repetitions');
if (null === $repetitions) {
return null;
}
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$return[] = [
'type' => $repetition['type'],
'moment' => $repetition['moment'],
'skip' => (int)$repetition['skip'],
'weekend' => (int)$repetition['weekend'],
];
}
return $return;
}
/**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
*
* @return array|null
*/
private function getTransactionData(): ?array
{
$return = [];
// transaction data:
/** @var array $transactions */
$transactions = $this->get('transactions');
if (null === $transactions) {
return null;
}
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = [
'amount' => $transaction['amount'],
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'description' => $transaction['description'],
'type' => $this->string('type'),
// new and updated fields:
'piggy_bank_id' => isset($transaction['piggy_bank_id']) ? (int)$transaction['piggy_bank_id'] : null,
'piggy_bank_name' => $transaction['piggy_bank_name'] ?? null,
'tags' => $transaction['tags'] ?? [],
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
];
}
return $return;
}
}

View File

@@ -31,9 +31,52 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest;
* *
* Technically speaking this class does not have to be extended like this but who knows what the future brings. * Technically speaking this class does not have to be extended like this but who knows what the future brings.
* *
* @SuppressWarnings(PHPMD.NumberOfChildren)
*/ */
class Request extends FireflyIIIRequest class Request extends FireflyIIIRequest
{ {
/**
* @return array
*/
public function getAllAccountData(): array
{
$active = true;
$includeNetWorth = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('include_net_worth')) {
$includeNetWorth = $this->boolean('include_net_worth');
}
$data = [
'name' => $this->string('name'),
'active' => $active,
'include_net_worth' => $includeNetWorth,
'account_type' => $this->string('type'),
'account_type_id' => null,
'currency_id' => $this->integer('currency_id'),
'currency_code' => $this->string('currency_code'),
'virtual_balance' => $this->string('virtual_balance'),
'iban' => $this->string('iban'),
'BIC' => $this->string('bic'),
'account_number' => $this->string('account_number'),
'account_role' => $this->string('account_role'),
'opening_balance' => $this->string('opening_balance'),
'opening_balance_date' => $this->date('opening_balance_date'),
'cc_type' => $this->string('credit_card_type'),
'cc_Monthly_payment_date' => $this->string('monthly_payment_date'),
'notes' => $this->string('notes'),
'interest' => $this->string('interest'),
'interest_period' => $this->string('interest_period'),
];
if ('liability' === $data['account_type']) {
$data['opening_balance'] = bcmul($this->string('liability_amount'), '-1');
$data['opening_balance_date'] = $this->date('liability_start_date');
$data['account_type'] = $this->string('liability_type');
$data['account_type_id'] = null;
}
return $data;
}
} }

View File

@@ -28,8 +28,9 @@ use FireflyIII\Rules\IsBoolean;
/** /**
* * @codeCoverageIgnore
* Class RuleGroupRequest * Class RuleGroupRequest
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class RuleGroupRequest extends Request class RuleGroupRequest extends Request
{ {

View File

@@ -0,0 +1,150 @@
<?php
/**
* RuleGroupTestRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
/**
* Class RuleGroupTestRequest
*/
class RuleGroupTestRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* @return array
*/
public function getTestParameters(): array
{
$return = [
'page' => $this->getPage(),
'start_date' => $this->getDate('start_date'),
'end_date' => $this->getDate('end_date'),
'search_limit' => $this->getSearchLimit(),
'trigger_limit' => $this->getTriggerLimit(),
'accounts' => $this->getAccounts(),
];
return $return;
}
/**
* @return array
*/
public function rules(): array
{
return [];
}
/**
* @return Collection
*/
private function getAccounts(): Collection
{
$accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts'));
$accounts = new Collection;
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $accountRepository->findNull((int)$accountId);
if ($this->validAccount($account)) {
/** @noinspection NullPointerExceptionInspection */
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
}
return $accounts;
}
/**
* @param string $field
*
* @return Carbon|null
*/
private function getDate(string $field): ?Carbon
{
/** @var Carbon $result */
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
return $result;
}
/**
* @return int
*/
private function getPage(): int
{
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
}
/**
* @return int
*/
private function getSearchLimit(): int
{
return 0 === (int)$this->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$this->query('search_limit');
}
/**
* @return int
*/
private function getTriggerLimit(): int
{
return 0 === (int)$this->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$this->query('triggered_limit');
}
/**
* @param Account|null $account
*
* @return bool
*/
private function validAccount(?Account $account): bool
{
return null !== $account && AccountType::ASSET === $account->accountType->type;
}
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* RuleGroupTriggerRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
/**
* Class RuleGroupTriggerRequest
*/
class RuleGroupTriggerRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* @return array
*/
public function getTriggerParameters(): array
{
$return = [
'start_date' => $this->getDate('start_date'),
'end_date' => $this->getDate('end_date'),
'accounts' => $this->getAccounts(),
];
return $return;
}
/**
* @return array
*/
public function rules(): array
{
return [
'start_date' => 'required|date',
'end_date' => 'required|date|after:start_date',
];
}
/**
* @return Collection
*/
private function getAccounts(): Collection
{
$accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts'));
$accounts = new Collection;
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $accountRepository->findNull((int)$accountId);
if ($this->validAccount($account)) {
/** @noinspection NullPointerExceptionInspection */
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
}
return $accounts;
}
/**
* @param string $field
*
* @return Carbon|null
*/
private function getDate(string $field): ?Carbon
{
/** @var Carbon $result */
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
return $result;
}
/**
* @param Account|null $account
*
* @return bool
*/
private function validAccount(?Account $account): bool
{
return null !== $account && AccountType::ASSET === $account->accountType->type;
}
}

View File

@@ -1,6 +1,6 @@
<?php <?php
/** /**
* RuleRequest.php * RuleStoreRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2018 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
@@ -25,12 +25,14 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use function is_array;
/** /**
* Class RuleRequest * Class RuleStoreRequest
*
*/ */
class RuleRequest extends Request class RuleStoreRequest extends Request
{ {
/** /**
* Authorize logged in users. * Authorize logged in users.
@@ -141,7 +143,7 @@ class RuleRequest extends Request
$data = $validator->getData(); $data = $validator->getData();
$actions = $data['actions'] ?? []; $actions = $data['actions'] ?? [];
// need at least one trigger // need at least one trigger
if (0 === \count($actions)) { if (0 === count($actions)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_action')); $validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
} }
} }
@@ -155,8 +157,8 @@ class RuleRequest extends Request
{ {
$data = $validator->getData(); $data = $validator->getData();
$triggers = $data['triggers'] ?? []; $triggers = $data['triggers'] ?? [];
// need at least one trugger // need at least one trigger
if (0 === \count($triggers)) { if (0 === count($triggers)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger')); $validator->errors()->add('title', (string)trans('validation.at_least_one_trigger'));
} }
} }
@@ -168,7 +170,7 @@ class RuleRequest extends Request
{ {
$actions = $this->get('actions'); $actions = $this->get('actions');
$return = []; $return = [];
if (\is_array($actions)) { if (is_array($actions)) {
foreach ($actions as $action) { foreach ($actions as $action) {
$return[] = [ $return[] = [
'type' => $action['type'], 'type' => $action['type'],
@@ -189,7 +191,7 @@ class RuleRequest extends Request
{ {
$triggers = $this->get('triggers'); $triggers = $this->get('triggers');
$return = []; $return = [];
if (\is_array($triggers)) { if (is_array($triggers)) {
foreach ($triggers as $trigger) { foreach ($triggers as $trigger) {
$return[] = [ $return[] = [
'type' => $trigger['type'], 'type' => $trigger['type'],

View File

@@ -0,0 +1,150 @@
<?php
/**
* RuleTestRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
/**
* Class RuleTestRequest
*/
class RuleTestRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* @return array
*/
public function getTestParameters(): array
{
$return = [
'page' => $this->getPage(),
'start_date' => $this->getDate('start_date'),
'end_date' => $this->getDate('end_date'),
'search_limit' => $this->getSearchLimit(),
'trigger_limit' => $this->getTriggerLimit(),
'accounts' => $this->getAccounts(),
];
return $return;
}
/**
* @return array
*/
public function rules(): array
{
return [];
}
/**
* @return Collection
*/
private function getAccounts(): Collection
{
$accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts'));
$accounts = new Collection;
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $accountRepository->findNull((int)$accountId);
if ($this->validAccount($account)) {
/** @noinspection NullPointerExceptionInspection */
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
}
return $accounts;
}
/**
* @param string $field
*
* @return Carbon|null
*/
private function getDate(string $field): ?Carbon
{
/** @var Carbon $result */
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
return $result;
}
/**
* @return int
*/
private function getPage(): int
{
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
}
/**
* @return int
*/
private function getSearchLimit(): int
{
return 0 === (int)$this->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$this->query('search_limit');
}
/**
* @return int
*/
private function getTriggerLimit(): int
{
return 0 === (int)$this->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$this->query('triggered_limit');
}
/**
* @param Account|null $account
*
* @return bool
*/
private function validAccount(?Account $account): bool
{
return null !== $account && AccountType::ASSET === $account->accountType->type;
}
}

View File

@@ -0,0 +1,124 @@
<?php
/**
* RuleTriggerRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
/**
* Class RuleTriggerRequest
*/
class RuleTriggerRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* @return array
*/
public function getTriggerParameters(): array
{
$return = [
'start_date' => $this->getDate('start_date'),
'end_date' => $this->getDate('end_date'),
'accounts' => $this->getAccounts(),
];
return $return;
}
/**
* @return array
*/
public function rules(): array
{
return [
'start_date' => 'required|date',
'end_date' => 'required|date|after:start_date',
];
}
/**
* @return Collection
*/
private function getAccounts(): Collection
{
$accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts'));
$accounts = new Collection;
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $accountRepository->findNull((int)$accountId);
if ($this->validAccount($account)) {
/** @noinspection NullPointerExceptionInspection */
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
}
return $accounts;
}
/**
* @param string $field
*
* @return Carbon|null
*/
private function getDate(string $field): ?Carbon
{
/** @var Carbon $result */
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
return $result;
}
/**
* @param Account|null $account
*
* @return bool
*/
private function validAccount(?Account $account): bool
{
return null !== $account && AccountType::ASSET === $account->accountType->type;
}
}

View File

@@ -0,0 +1,214 @@
<?php
/**
* RuleUpdateRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
use function is_array;
/**
* Class RuleUpdateRequest
*
*/
class RuleUpdateRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$strict = null;
$active = null;
$stopProcessing = null;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('strict')) {
$strict = $this->boolean('strict');
}
if (null !== $this->get('stop_processing')) {
$stopProcessing = $this->boolean('stop_processing');
}
$data = [
'title' => $this->nullableString('title'),
'description' => $this->nullableString('description'),
'rule_group_id' => $this->nullableInteger('rule_group_id'),
'rule_group_title' => $this->nullableString('rule_group_title'),
'trigger' => $this->nullableString('trigger'),
'strict' => $strict,
'stop_processing' => $stopProcessing,
'active' => $active,
'triggers' => $this->getRuleTriggers(),
'actions' => $this->getRuleActions(),
];
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$validTriggers = array_keys(config('firefly.rule-triggers'));
$validActions = array_keys(config('firefly.rule-actions'));
$rule = $this->route()->parameter('rule');
// some triggers and actions require text:
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
$contextActions = implode(',', config('firefly.context-rule-actions'));
$rules = [
'title' => sprintf('between:1,100|uniqueObjectForUser:rules,title,%d', $rule->id),
'description' => 'between:1,5000|nullable',
'rule_group_id' => 'belongsToUser:rule_groups',
'rule_group_title' => 'nullable|between:1,255|belongsToUser:rule_groups,title',
'trigger' => 'in:store-journal,update-journal',
'triggers.*.type' => 'required|in:' . implode(',', $validTriggers),
'triggers.*.value' => 'required_if:actions.*.type,' . $contextTriggers . '|min:1|ruleTriggerValue',
'triggers.*.stop_processing' => [new IsBoolean],
'triggers.*.active' => [new IsBoolean],
'actions.*.type' => 'required|in:' . implode(',', $validActions),
'actions.*.value' => 'required_if:actions.*.type,' . $contextActions . '|ruleActionValue',
'actions.*.stop_processing' => [new IsBoolean],
'actions.*.active' => [new IsBoolean],
'strict' => [new IsBoolean],
'stop_processing' => [new IsBoolean],
'active' => [new IsBoolean],
];
return $rules;
}
/**
* Configure the validator instance.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
$this->atLeastOneTrigger($validator);
$this->atLeastOneAction($validator);
}
);
}
/**
* Adds an error to the validator when there are no repetitions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneAction(Validator $validator): void
{
$data = $validator->getData();
$actions = $data['actions'] ?? null;
// need at least one action
if (is_array($actions) && 0 === count($actions)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
}
}
/**
* Adds an error to the validator when there are no repetitions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneTrigger(Validator $validator): void
{
$data = $validator->getData();
$triggers = $data['triggers'] ?? null;
// need at least one trigger
if (is_array($triggers) && 0 === count($triggers)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger'));
}
}
/**
* @return array|null
*/
private function getRuleActions(): ?array
{
if (!$this->has('actions')) {
return null;
}
$actions = $this->get('actions');
$return = [];
if (is_array($actions)) {
foreach ($actions as $action) {
$return[] = [
'type' => $action['type'],
'value' => $action['value'],
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
];
}
}
return $return;
}
/**
* @return array|null
*/
private function getRuleTriggers(): ?array
{
if (!$this->has('triggers')) {
return null;
}
$triggers = $this->get('triggers');
$return = [];
if (is_array($triggers)) {
foreach ($triggers as $trigger) {
$return[] = [
'type' => $trigger['type'],
'value' => $trigger['value'],
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'false')),
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
];
}
}
return $return;
}
}

View File

@@ -28,6 +28,10 @@ use FireflyIII\Models\Tag;
/** /**
* Class TagRequest * Class TagRequest
*
* @codeCoverageIgnore
*
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class TagRequest extends Request class TagRequest extends Request
{ {

View File

@@ -1,222 +0,0 @@
<?php
/**
* TransactionRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
/**
* Class TransactionRequest
*/
class TransactionRequest extends Request
{
use TransactionValidation;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data. Is pretty complex because of all the ??-statements.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @return array
*/
public function getAll(): array
{
$data = [
'type' => $this->string('type'),
'date' => $this->dateTime('date'),
'description' => $this->string('description'),
'piggy_bank_id' => $this->integer('piggy_bank_id'),
'piggy_bank_name' => $this->string('piggy_bank_name'),
'bill_id' => $this->integer('bill_id'),
'bill_name' => $this->string('bill_name'),
'tags' => explode(',', $this->string('tags')),
'notes' => $this->string('notes'),
'sepa-cc' => $this->string('sepa_cc'),
'sepa-ct-op' => $this->string('sepa_ct_op'),
'sepa-ct-id' => $this->string('sepa_ct_id'),
'sepa-db' => $this->string('sepa_db'),
'sepa-country' => $this->string('sepa_country'),
'sepa-ep' => $this->string('sepa_ep'),
'sepa-ci' => $this->string('sepa_ci'),
'sepa-batch-id' => $this->string('sepa_batch_id'),
'interest_date' => $this->date('interest_date'),
'book_date' => $this->date('book_date'),
'process_date' => $this->date('process_date'),
'due_date' => $this->date('due_date'),
'payment_date' => $this->date('payment_date'),
'invoice_date' => $this->date('invoice_date'),
'internal_reference' => $this->string('internal_reference'),
'bunq_payment_id' => $this->string('bunq_payment_id'),
'external_id' => $this->string('external_id'),
'original-source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'transactions' => $this->getTransactionData(),
];
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function rules(): array
{
$rules = [
// basic fields for journal:
'type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
'description' => 'between:1,255',
'date' => ['required', new IsDateOrTime],
'piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser],
'piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser],
'bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
'bill_name' => ['between:1,255', 'nullable', new BelongsUser],
'tags' => 'between:1,255',
// then, custom fields for journal
'notes' => 'min:1,max:50000|nullable',
// SEPA fields:
'sepa_cc' => 'min:1,max:255|nullable',
'sepa_ct_op' => 'min:1,max:255|nullable',
'sepa_ct_id' => 'min:1,max:255|nullable',
'sepa_db' => 'min:1,max:255|nullable',
'sepa_country' => 'min:1,max:255|nullable',
'sepa_ep' => 'min:1,max:255|nullable',
'sepa_ci' => 'min:1,max:255|nullable',
'sepa_batch_id' => 'min:1,max:255|nullable',
// dates
'interest_date' => 'date|nullable',
'book_date' => 'date|nullable',
'process_date' => 'date|nullable',
'due_date' => 'date|nullable',
'payment_date' => 'date|nullable',
'invoice_date' => 'date|nullable',
'internal_reference' => 'min:1,max:255|nullable',
'bunq_payment_id' => 'min:1,max:255|nullable',
'external_id' => 'min:1,max:255|nullable',
// transaction rules (in array for splits):
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.description' => 'nullable|between:1,255',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.reconciled' => [new IsBoolean],
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
];
if ('PUT' === $this->method()) {
unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']);
}
return $rules;
}
/**
* Configure the validator instance.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
$this->validateOneTransaction($validator);
$this->validateDescriptions($validator);
$this->validateJournalDescription($validator);
$this->validateSplitDescriptions($validator);
$this->validateForeignCurrencyInformation($validator);
$this->validateAccountInformation($validator);
$this->validateSplitAccounts($validator);
}
);
}
/**
* Get transaction data.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @return array
*/
private function getTransactionData(): array
{
$return = [];
foreach ($this->get('transactions') as $index => $transaction) {
$return[] = [
'amount' => $transaction['amount'],
'description' => $transaction['description'] ?? null,
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'reconciled' => $this->convertBoolean((string)($transaction['reconciled'] ?? 'false')),
'identifier' => $index,
];
}
return $return;
}
}

View File

@@ -0,0 +1,278 @@
<?php
/**
* TransactionStoreRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Support\NullArrayObject;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
/**
* Class TransactionStoreRequest
*/
class TransactionStoreRequest extends Request
{
use TransactionValidation;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data. Is pretty complex because of all the ??-statements.
*
* @return array
*/
public function getAll(): array
{
$data = [
'group_title' => $this->string('group_title'),
'transactions' => $this->getTransactionData(),
];
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$rules = [
// basic fields for group:
'group_title' => 'between:1,255',
// transaction rules (in array for splits):
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
'transactions.*.date' => ['required', new IsDateOrTime],
'transactions.*.order' => 'numeric|min:0',
// currency info
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
// amount
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
// description
'transactions.*.description' => 'nullable|between:1,255',
// source of transaction
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
// destination of transaction
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
// budget, category, bill and piggy
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser],
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser],
// other interesting fields
'transactions.*.reconciled' => [new IsBoolean],
'transactions.*.notes' => 'min:1,max:50000|nullable',
'transactions.*.tags' => 'between:1,255',
// meta info fields
'transactions.*.internal_reference' => 'min:1,max:255|nullable',
'transactions.*.external_id' => 'min:1,max:255|nullable',
'transactions.*.recurrence_id' => 'min:1,max:255|nullable',
'transactions.*.bunq_payment_id' => 'min:1,max:255|nullable',
// SEPA fields:
'transactions.*.sepa_cc' => 'min:1,max:255|nullable',
'transactions.*.sepa_ct_op' => 'min:1,max:255|nullable',
'transactions.*.sepa_ct_id' => 'min:1,max:255|nullable',
'transactions.*.sepa_db' => 'min:1,max:255|nullable',
'transactions.*.sepa_country' => 'min:1,max:255|nullable',
'transactions.*.sepa_ep' => 'min:1,max:255|nullable',
'transactions.*.sepa_ci' => 'min:1,max:255|nullable',
'transactions.*.sepa_batch_id' => 'min:1,max:255|nullable',
// dates
'transactions.*.interest_date' => 'date|nullable',
'transactions.*.book_date' => 'date|nullable',
'transactions.*.process_date' => 'date|nullable',
'transactions.*.due_date' => 'date|nullable',
'transactions.*.payment_date' => 'date|nullable',
'transactions.*.invoice_date' => 'date|nullable',
];
return $rules;
}
/**
* Configure the validator instance.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
// must submit at least one transaction.
$this->validateOneTransaction($validator);
// all journals must have a description
$this->validateDescriptions($validator);
// all transaction types must be equal:
$this->validateTransactionTypes($validator);
// validate foreign currency info
$this->validateForeignCurrencyInformation($validator);
// validate all account info
$this->validateAccountInformation($validator);
// validate source/destination is equal, depending on the transaction journal type.
$this->validateEqualAccounts($validator);
// the group must have a description if > 1 journal.
$this->validateGroupDescription($validator);
}
);
}
/**
* Get transaction data.
*
* @return array
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var int $index
* @var array $transaction
*/
foreach ($this->get('transactions') as $index => $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->stringFromValue($object['type']),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue($object['currency_id']),
'currency_code' => $this->stringFromValue($object['currency_code']),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->stringFromValue($object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->stringFromValue((string)$object['amount']),
'foreign_amount' => $this->stringFromValue((string)$object['foreign_amount']),
// description.
'description' => $this->stringFromValue($object['description']),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->stringFromValue($object['source_name']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->stringFromValue($object['destination_name']),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->stringFromValue($object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->stringFromValue($object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->stringFromValue($object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->stringFromValue($object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->stringFromValue($object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->stringFromValue($object['internal_reference']),
'external_id' => $this->stringFromValue($object['external_id']),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->stringFromValue($object['bunq_payment_id']),
'sepa_cc' => $this->stringFromValue($object['sepa_cc']),
'sepa_ct_op' => $this->stringFromValue($object['sepa_ct_op']),
'sepa_ct_id' => $this->stringFromValue($object['sepa_ct_id']),
'sepa_db' => $this->stringFromValue($object['sepa_db']),
'sepa_country' => $this->stringFromValue($object['sepa_country']),
'sepa_ep' => $this->stringFromValue($object['sepa_ep']),
'sepa_ci' => $this->stringFromValue($object['sepa_ci']),
'sepa_batch_id' => $this->stringFromValue($object['sepa_batch_id']),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
}
return $return;
}
}

View File

@@ -0,0 +1,323 @@
<?php
/**
* TransactionUpdateRequest.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
use Log;
/**
* Class TransactionUpdateRequest
*/
class TransactionUpdateRequest extends Request
{
use TransactionValidation;
/** @var array Array values. */
private $arrayFields;
/** @var array Boolean values. */
private $booleanFields;
/** @var array Fields that contain date values. */
private $dateFields;
/** @var array Fields that contain integer values. */
private $integerFields;
/** @var array Fields that contain string values. */
private $stringFields;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data. Is pretty complex because of all the ??-statements.
*
* @return array
*/
public function getAll(): array
{
$this->integerFields = [
'order',
'currency_id',
'foreign_currency_id',
'transaction_journal_id',
'source_id',
'destination_id',
'budget_id',
'category_id',
'bill_id',
'recurrence_id',
];
$this->dateFields = [
'date',
'interest_date',
'book_date',
'process_date',
'due_date',
'payment_date',
'invoice_date',
];
$this->stringFields = [
'type',
'currency_code',
'foreign_currency_code',
'amount',
'foreign_amount',
'description',
'source_name',
'destination_name',
'budget_name',
'category_name',
'bill_name',
'notes',
'internal_reference',
'external_id',
'bunq_payment_id',
'sepa_cc',
'sepa_ct_op',
'sepa_ct_id',
'sepa_db',
'sepa_country',
'sepa_ep',
'sepa_ci',
'sepa_batch_id',
];
$this->booleanFields = [
'reconciled',
];
$this->arrayFields = [
'tags',
];
$data = [
'transactions' => $this->getTransactionData(),
];
if ($this->has('group_title')) {
$data['group_title'] = $this->string('group_title');
}
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$rules = [
// basic fields for group:
'group_title' => 'between:1,255',
// transaction rules (in array for splits):
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
'transactions.*.date' => [new IsDateOrTime],
'transactions.*.order' => 'numeric|min:0',
// currency info
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
// amount
'transactions.*.amount' => 'numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|gte:0',
// description
'transactions.*.description' => 'nullable|between:1,255',
// source of transaction
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
// destination of transaction
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
// budget, category, bill and piggy
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
// other interesting fields
'transactions.*.reconciled' => [new IsBoolean],
'transactions.*.notes' => 'min:1,max:50000|nullable',
'transactions.*.tags' => 'between:0,255',
// meta info fields
'transactions.*.internal_reference' => 'min:1,max:255|nullable',
'transactions.*.external_id' => 'min:1,max:255|nullable',
'transactions.*.recurrence_id' => 'min:1,max:255|nullable',
'transactions.*.bunq_payment_id' => 'min:1,max:255|nullable',
// SEPA fields:
'transactions.*.sepa_cc' => 'min:1,max:255|nullable',
'transactions.*.sepa_ct_op' => 'min:1,max:255|nullable',
'transactions.*.sepa_ct_id' => 'min:1,max:255|nullable',
'transactions.*.sepa_db' => 'min:1,max:255|nullable',
'transactions.*.sepa_country' => 'min:1,max:255|nullable',
'transactions.*.sepa_ep' => 'min:1,max:255|nullable',
'transactions.*.sepa_ci' => 'min:1,max:255|nullable',
'transactions.*.sepa_batch_id' => 'min:1,max:255|nullable',
// dates
'transactions.*.interest_date' => 'date|nullable',
'transactions.*.book_date' => 'date|nullable',
'transactions.*.process_date' => 'date|nullable',
'transactions.*.due_date' => 'date|nullable',
'transactions.*.payment_date' => 'date|nullable',
'transactions.*.invoice_date' => 'date|nullable',
];
return $rules;
}
/**
* Configure the validator instance.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
/** @var TransactionGroup $transactionGroup */
$transactionGroup = $this->route()->parameter('transactionGroup');
$validator->after(
function (Validator $validator) use ($transactionGroup) {
// must submit at least one transaction.
$this->validateOneTransaction($validator);
// if more than one, verify that there are journal ID's present.
$this->validateJournalIds($validator, $transactionGroup);
// all transaction types must be equal:
$this->validateTransactionTypesForUpdate($validator);
// validate source/destination is equal, depending on the transaction journal type.
$this->validateEqualAccountsForUpdate($validator, $transactionGroup);
// If type is set, source + destination info is mandatory.
// Not going to do this. Not sure where the demand came from.
// validate that the currency fits the source and/or destination account.
// validate all account info
$this->validateAccountInformationUpdate($validator);
// The currency info must match the accounts involved.
// Instead will ignore currency info as much as possible.
// TODO if the transaction_journal_id is empty, some fields are mandatory, like the amount!
// all journals must have a description
//$this->validateDescriptions($validator);
// // validate foreign currency info
// $this->validateForeignCurrencyInformation($validator);
//
//
// // make sure all splits have valid source + dest info
// $this->validateSplitAccounts($validator);
// the group must have a description if > 1 journal.
// $this->validateGroupDescription($validator);
}
);
}
/**
* Get transaction data.
*
* @return array
*/
private function getTransactionData(): array
{
Log::debug('Now in getTransactionData()');
$return = [];
/**
* @var int $index
* @var array $transaction
*/
foreach ($this->get('transactions') as $index => $transaction) {
// default response is to update nothing in the transaction:
$current = [];
// for each field, add it to the array if a reference is present in the request:
foreach ($this->integerFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
}
}
foreach ($this->stringFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->stringFromValue((string)$transaction[$fieldName]);
}
}
foreach ($this->dateFields as $fieldName) {
Log::debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
Log::debug(sprintf('New value: "%s"', (string)$transaction[$fieldName]));
}
}
foreach ($this->booleanFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
}
}
foreach ($this->arrayFields as $fieldName) {
if (array_key_exists($fieldName, $transaction)) {
$current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]);
}
}
$return[] = $current;
}
return $return;
}
}

View File

@@ -1,7 +1,7 @@
<?php <?php
/** /**
* UserRequest.php * UserStoreRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2018 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
@@ -30,9 +30,9 @@ use FireflyIII\User;
/** /**
* Class UserRequest * Class UserStoreRequest
*/ */
class UserRequest extends Request class UserStoreRequest extends Request
{ {
/** /**
* Authorize logged in users. * Authorize logged in users.
@@ -66,7 +66,7 @@ class UserRequest extends Request
public function getAll(): array public function getAll(): array
{ {
$blocked = false; $blocked = false;
if (null === $this->get('blocked')) { if (null !== $this->get('blocked')) {
$blocked = $this->boolean('blocked'); $blocked = $this->boolean('blocked');
} }
$data = [ $data = [
@@ -86,23 +86,12 @@ class UserRequest extends Request
*/ */
public function rules(): array public function rules(): array
{ {
$rules = [ return [
'email' => 'required|email|unique:users,email,', 'email' => 'required|email|unique:users,email',
'blocked' => [new IsBoolean], 'blocked' => [new IsBoolean],
'blocked_code' => 'in:email_changed', 'blocked_code' => 'in:email_changed',
'role' => 'in:owner,demo', 'role' => 'in:owner,demo',
]; ];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
$user = $this->route()->parameter('user');
$rules['email'] = 'required|email|unique:users,email,' . $user->id;
break;
}
return $rules;
} }
} }

View File

@@ -0,0 +1,100 @@
<?php
/**
* UserUpdateRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\User;
/**
* Class UserUpdateRequest
*/
class UserUpdateRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
$result = false;
// Only allow authenticated users
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if ($repository->hasRole($user, 'owner')) {
$result = true; // @codeCoverageIgnore
}
}
return $result;
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$blocked = false;
if (null !== $this->get('blocked')) {
$blocked = $this->boolean('blocked');
}
$data = [
'email' => $this->string('email'),
'blocked' => $blocked,
'blocked_code' => $this->string('blocked_code'),
'role' => $this->string('role'),
];
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$user = $this->route()->parameter('user');
$rules = [
'email' => sprintf('email|unique:users,email,%d', $user->id),
'blocked' => [new IsBoolean],
'blocked_code' => 'in:email_changed',
'role' => 'in:owner,demo,',
];
return $rules;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* CorrectDatabase.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use Artisan;
use Illuminate\Console\Command;
use Schema;
/**
* Class CorrectDatabase
* @codeCoverageIgnore
*/
class CorrectDatabase extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Will correct the integrity of your database, if necessary.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:correct-database';
/**
* Execute the console command.
*/
public function handle(): int
{
// if table does not exist, return false
if (!Schema::hasTable('users')) {
return 1;
}
$commands = [
'firefly-iii:fix-piggies',
'firefly-iii:create-link-types',
'firefly-iii:create-access-tokens',
'firefly-iii:remove-bills',
'firefly-iii:enable-currencies',
'firefly-iii:fix-transfer-budgets',
'firefly-iii:fix-uneven-amount',
'firefly-iii:delete-zero-amount',
'firefly-iii:delete-orphaned-transactions',
'firefly-iii:delete-empty-journals',
'firefly-iii:delete-empty-groups',
'firefly-iii:fix-account-types',
'firefly-iii:rename-meta-fields',
'firefly-iii:fix-ob-currencies'
];
foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command));
Artisan::call($command);
$result = Artisan::output();
echo $result;
}
return 0;
}
}

View File

@@ -0,0 +1,158 @@
<?php
/**
* CorrectOpeningBalanceCurrencies.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Console\Command;
use Log;
/**
* Class CorrectOpeningBalanceCurrencies
*/
class CorrectOpeningBalanceCurrencies extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-ob-currencies';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
// get all OB journals:
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->whereNull('transaction_journals.deleted_at')
->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']);
$this->line(sprintf('Going to verify %d opening balance transactions.', $set->count()));
$count = 0;
/** @var TransactionJournal $journal */
foreach ($set as $journal) {
$count += $this->correctJournal($journal);
}
if ($count > 0) {
$this->line(sprintf('Corrected %d opening balance transactions.', $count));
}
if (0 === $count) {
$this->info('There was nothing to fix in the opening balance transactions.');
}
return 0;
}
/**
* @param TransactionJournal $journal
*
* @return int
*/
private function correctJournal(TransactionJournal $journal): int
{
Log::debug(sprintf('Going to correct journal #%d', $journal->id));
// get the asset account for this opening balance:
$account = $this->getAccount($journal);
if (null === $account) {
$this->warn(sprintf('Transaction journal #%d has no valid account. Cant fix this line.', $journal->id));
return 0;
}
Log::debug(sprintf('Found %s #%d "%s".', $account->accountType->type, $account->id, $account->name));
$currency = $this->getCurrency($account);
Log::debug(sprintf('Found currency #%d (%s)', $currency->id, $currency->code));
// update journal and all transactions:
$this->setCurrency($journal, $currency);
return 1;
}
/**
* @param TransactionJournal $journal
*
* @return Account|null
*/
private function getAccount(TransactionJournal $journal): ?Account
{
$excluded = [];
$transactions = $journal->transactions()->with(['account', 'account.accountType'])->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$account = $transaction->account;
if (null !== $account) {
if (AccountType::INITIAL_BALANCE !== $account->accountType->type) {
return $account;
}
}
}
return null;
}
/**
* @param Account $account
*
* @return TransactionCurrency
*/
private function getCurrency(Account $account): TransactionCurrency
{
/** @var AccountRepositoryInterface $repos */
$repos = app(AccountRepositoryInterface::class);
$repos->setUser($account->user);
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
}
/**
* @param TransactionJournal $journal
* @param TransactionCurrency $currency
*/
private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): void
{
$journal->transaction_currency_id = $currency->id;
$journal->save();
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$transaction->transaction_currency_id = $currency->id;
$transaction->save();
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* VerifySkeleton.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use Illuminate\Console\Command;
/**
* Class CorrectionSkeleton
*/
class CorrectionSkeleton extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'DESCRIPTION HERE';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:CORR_COMMAND';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
//
$this->warn('Congrats, you found the skeleton command. Boo!');
return 0;
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* CreateAccessTokens.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use Exception;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Console\Command;
/**
* Class CreateAccessTokens
*/
class CreateAccessTokens extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Creates user access tokens which are used for command line access to personal data.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:create-access-tokens';
/**
* Execute the console command.
*
* @return int
* @throws Exception
*/
public function handle(): int
{
// make repository:
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
$start = microtime(true);
$count = 0;
$users = $repository->all();
/** @var User $user */
foreach ($users as $user) {
$pref = app('preferences')->getForUser($user, 'access_token', null);
if (null === $pref) {
$token = $user->generateAccessToken();
app('preferences')->setForUser($user, 'access_token', $token);
$this->line(sprintf('Generated access token for user %s', $user->email));
++$count;
}
}
if (0 === $count) {
$this->info('All access tokens OK!');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verify access tokens in %s seconds.', $end));
return 0;
}
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/**
* CreateLinkTypes.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\LinkType;
use Illuminate\Console\Command;
/**
* Class CreateLinkTypes. Created all link types in case a migration hasn't fired.
*/
class CreateLinkTypes extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Creates all link types.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:create-link-types';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(): int
{
$start = microtime(true);
$count = 0;
$set = [
'Related' => ['relates to', 'relates to'],
'Refund' => ['(partially) refunds', 'is (partially) refunded by'],
'Paid' => ['(partially) pays for', 'is (partially) paid for by'],
'Reimbursement' => ['(partially) reimburses', 'is (partially) reimbursed by'],
];
foreach ($set as $name => $values) {
$link = LinkType::where('name', $name)
->first();
if (null === $link) {
$link = new LinkType;
$link->name = $name;
$link->inward = $values[1];
$link->outward = $values[0];
++$count;
$this->line(sprintf('Created missing link type "%s"', $name));
}
$link->editable = false;
$link->save();
}
if (0 === $count) {
$this->info('All link types OK!');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified link types in %s seconds', $end));
return 0;
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/**
* DeleteEmptyGroups.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use Exception;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
/**
* Class DeleteEmptyGroups
*/
class DeleteEmptyGroups extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete empty transaction groups.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:delete-empty-groups';
/**
* Execute the console command.
*
* @return mixed
* @throws Exception;
*/
public function handle(): int
{
$start = microtime(true);
$groups = array_unique(TransactionJournal::get(['transaction_group_id'])->pluck('transaction_group_id')->toArray());
$count = TransactionGroup::whereNull('deleted_at')->whereNotIn('id', $groups)->count();
if (0 === $count) {
$this->info('No empty transaction groups.');
}
if ($count > 0) {
$this->info(sprintf('Deleted %d empty transaction group(s).', $count));
TransactionGroup::whereNull('deleted_at')->whereNotIn('id', $groups)->delete();
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified empty groups in %s seconds', $end));
return 0;
}
}

View File

@@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
/**
* DeleteEmptyJournals.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use DB;
use Exception;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Log;
/**
* Class DeleteEmptyJournals
*/
class DeleteEmptyJournals extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete empty and uneven transaction journals.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:delete-empty-journals';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$this->deleteUnevenJournals();
$this->deleteEmptyJournals();
return 0;
}
private function deleteEmptyJournals(): void
{
$start = microtime(true);
$count = 0;
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id')
->get(['transaction_journals.id']);
foreach ($set as $entry) {
try {
TransactionJournal::find($entry->id)->delete();
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
}
// @codeCoverageIgnoreEnd
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
++$count;
}
if (0 === $count) {
$this->info('No empty transaction journals.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified empty journals in %s seconds', $end));
}
/**
* Delete transactions and their journals if they have an uneven number of transactions.
*/
private function deleteUnevenJournals(): void
{
$set = Transaction
::whereNull('deleted_at')
->groupBy('transactions.transaction_journal_id')
->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']);
$total = 0;
foreach ($set as $row) {
$count = (int)$row->the_count;
if (1 === $count % 2) {
// uneven number, delete journal and transactions:
try {
TransactionJournal::find((int)$row->transaction_journal_id)->delete();
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::info(sprintf('Could not delete journal: %s', $e->getMessage()));
}
// @codeCoverageIgnoreEnd
Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete();
$this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id));
$total++;
}
}
if (0 === $total) {
$this->info('No uneven transaction journals.');
}
}
}

View File

@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
/**
* DeleteOrphanedTransactions.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use Exception;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Log;
use stdClass;
/**
* Deletes transactions where the journal has been deleted.
*/
class DeleteOrphanedTransactions extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Deletes orphaned transactions.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:delete-orphaned-transactions';
/**
* Execute the console command.
*
* @return int
* @throws Exception
*/
public function handle(): int
{
$start = microtime(true);
$this->deleteOrphanedTransactions();
$this->deleteFromOrphanedAccounts();
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified orphans in %s seconds', $end));
return 0;
}
/**
*
*/
private function deleteFromOrphanedAccounts(): void
{
$set
= Transaction
::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->whereNotNull('accounts.deleted_at')
->get(['transactions.*']);
$count = 0;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
// delete journals
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
if ($journal) {
try {
$journal->delete();
// @codeCoverageIgnoreStart
} catch (Exception $e) {
Log::info(sprintf('Could not delete journal %s', $e->getMessage()));
}
// @codeCoverageIgnoreEnd
}
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
$this->line(
sprintf('Deleted transaction journal #%d because account #%d was already deleted.',
$transaction->transaction_journal_id, $transaction->account_id)
);
$count++;
}
if (0 === $count) {
$this->info('No orphaned accounts.');
}
}
/**
* @throws Exception
*/
private function deleteOrphanedTransactions(): void
{
$count = 0;
$set = Transaction
::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->whereNotNull('transactions.id')
->get(
[
'transaction_journals.id as journal_id',
'transactions.id as transaction_id',
]
);
/** @var stdClass $entry */
foreach ($set as $entry) {
$transaction = Transaction::find((int)$entry->transaction_id);
$transaction->delete();
$this->info(
sprintf(
'Transaction #%d (part of deleted transaction journal #%d) has been deleted as well.',
$entry->transaction_id,
$entry->journal_id
)
);
++$count;
}
if (0 === $count) {
$this->info('No orphaned transactions.');
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* DeleteZeroAmount.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use Exception;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
/**
* Class DeleteZeroAmount
*/
class DeleteZeroAmount extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete transactions with zero amount.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:delete-zero-amount';
/**
* Execute the console command.
* @return int
*/
public function handle(): int
{
$start = microtime(true);
$set = Transaction::where('amount', 0)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
$set = array_unique($set);
/** @var Collection $journals */
$journals = TransactionJournal::whereIn('id', $set)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$this->info(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id));
try {
$journal->delete();
// @codeCoverageIgnoreStart
} catch (Exception $e) {
$this->line($e->getMessage());
}
// @codeCoverageIgnoreEnd
Transaction::where('transaction_journal_id', $journal->id)->delete();
}
if (0 === $journals->count()) {
$this->info('No zero-amount transaction journals.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified zero-amount integrity in %s seconds', $end));
return 0;
}
}

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
/**
* EnableCurrencies.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
/**
* Class EnableCurrencies
*/
class EnableCurrencies extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Enables all currencies in use.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:enable-currencies';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(): int
{
$start = microtime(true);
$found = [];
// get all meta entries
/** @var Collection $meta */
$meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']);
foreach ($meta as $entry) {
$found[] = (int)$entry->data;
}
// get all from journals:
/** @var Collection $journals */
$journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($journals as $entry) {
$found[] = (int)$entry->transaction_currency_id;
}
// get all from transactions
/** @var Collection $transactions */
$transactions = Transaction::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($transactions as $entry) {
$found[] = (int)$entry->transaction_currency_id;
}
// get all from budget limits
/** @var Collection $limits */
$limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($limits as $entry) {
$found[] = (int)$entry->transaction_currency_id;
}
$found = array_unique($found);
$this->info(sprintf('%d different currencies are currently in use.', count($found)));
$disabled = TransactionCurrency::whereIn('id', $found)->where('enabled', false)->count();
if ($disabled > 0) {
$this->info(sprintf('%d were (was) still disabled. This has been corrected.', $disabled));
}
if (0 === $disabled) {
$this->info('All currencies are correctly enabled or disabled.');
}
TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]);
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified currencies in %s seconds.', $end));
return 0;
}
}

View File

@@ -0,0 +1,256 @@
<?php
declare(strict_types=1);
/**
* FixAccountTypes.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
* Class FixAccountTypes
*/
class FixAccountTypes extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Make sure all journals have the correct from/to account types.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-account-types';
/** @var array */
private $expected;
/** @var AccountFactory */
private $factory;
/** @var array */
private $fixable;
/** @var int */
private $count;
/**
* Execute the console command.
*
* @return int
* @throws FireflyException
*/
public function handle(): int
{
$this->stupidLaravel();
$start = microtime(true);
$this->factory = app(AccountFactory::class);
// some combinations can be fixed by this script:
$this->fixable = [
// transfers from asset to liability and vice versa
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::LOAN),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::DEBT),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::MORTGAGE),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::LOAN, AccountType::ASSET),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::DEBT, AccountType::ASSET),
sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::MORTGAGE, AccountType::ASSET),
// withdrawals with a revenue account as destination instead of an expense account.
sprintf('%s%s%s', TransactionType::WITHDRAWAL, AccountType::ASSET, AccountType::REVENUE),
// deposits with an expense account as source instead of a revenue account.
sprintf('%s%s%s', TransactionType::DEPOSIT, AccountType::EXPENSE, AccountType::ASSET),
];
$this->expected = config('firefly.source_dests');
$journals = TransactionJournal::with(['TransactionType', 'transactions', 'transactions.account', 'transactions.account.accounttype'])->get();
foreach ($journals as $journal) {
$this->inspectJournal($journal);
}
if (0 === $this->count) {
$this->info('All account types are OK!');
}
if (0 !== $this->count) {
$this->info(sprintf('Acted on %d transaction(s)!', $this->count));
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verifying account types took %s seconds', $end));
return 0;
}
/**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
* be called from the handle method instead of using the constructor to initialize the command.
*
* @codeCoverageIgnore
*/
private function stupidLaravel(): void
{
$this->count = 0;
}
/**
* @param TransactionJournal $journal
* @param string $type
* @param Transaction $source
* @param Transaction $dest
* @throws FireflyException
*/
private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void
{
$this->count++;
// variables:
$combination = sprintf('%s%s%s', $type, $source->account->accountType->type, $dest->account->accountType->type);
switch ($combination) {
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::LOAN):
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::DEBT):
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::ASSET, AccountType::MORTGAGE):
// from an asset to a liability should be a withdrawal:
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
$journal->transactionType()->associate($withdrawal);
$journal->save();
$this->info(sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id));
// check it again:
$this->inspectJournal($journal);
break;
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::LOAN, AccountType::ASSET):
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::DEBT, AccountType::ASSET):
case sprintf('%s%s%s', TransactionType::TRANSFER, AccountType::MORTGAGE, AccountType::ASSET):
// from a liability to an asset should be a deposit.
$deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first();
$journal->transactionType()->associate($deposit);
$journal->save();
$this->info(sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id));
// check it again:
$this->inspectJournal($journal);
break;
case sprintf('%s%s%s', TransactionType::WITHDRAWAL, AccountType::ASSET, AccountType::REVENUE):
// withdrawals with a revenue account as destination instead of an expense account.
$this->factory->setUser($journal->user);
$oldDest = $dest->account;
$result = $this->factory->findOrCreate($dest->account->name, AccountType::EXPENSE);
$dest->account()->associate($result);
$dest->save();
$this->info(
sprintf(
'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id,
$oldDest->id, $oldDest->name,
$result->id, $result->name
)
);
$this->inspectJournal($journal);
break;
case sprintf('%s%s%s', TransactionType::DEPOSIT, AccountType::EXPENSE, AccountType::ASSET):
// deposits with an expense account as source instead of a revenue account.
// find revenue account.
$this->factory->setUser($journal->user);
$result = $this->factory->findOrCreate($source->account->name, AccountType::REVENUE);
$oldSource = $dest->account;
$source->account()->associate($result);
$source->save();
$this->info(
sprintf(
'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id,
$oldSource->id, $oldSource->name,
$result->id, $result->name
)
);
$this->inspectJournal($journal);
break;
default:
$this->info(sprintf('The source account of %s #%d cannot be of type "%s".', $type, $journal->id, $source->account->accountType->type));
$this->info(sprintf('The destination account of %s #%d cannot be of type "%s".', $type, $journal->id, $dest->account->accountType->type));
break;
}
}
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getDestinationTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '>', 0);
}
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getSourceTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '<', 0);
}
/**
* @param TransactionJournal $journal
*
* @throws FireflyException
*/
private function inspectJournal(TransactionJournal $journal): void
{
$count = $journal->transactions()->count();
if (2 !== $count) {
$this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $count));
return;
}
$type = $journal->transactionType->type;
$sourceTransaction = $this->getSourceTransaction($journal);
$sourceAccount = $sourceTransaction->account;
$sourceAccountType = $sourceAccount->accountType->type;
$destTransaction = $this->getDestinationTransaction($journal);
$destAccount = $destTransaction->account;
$destAccountType = $destAccount->accountType->type;
if (!isset($this->expected[$type])) {
// @codeCoverageIgnoreStart
$this->info(sprintf('No source/destination info for transaction type %s.', $type));
return;
// @codeCoverageIgnoreEnd
}
if (!isset($this->expected[$type][$sourceAccountType])) {
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
return;
}
$expectedTypes = $this->expected[$type][$sourceAccountType];
if (!in_array($destAccountType, $expectedTypes, true)) {
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/**
* FixPiggies.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
* Report (and fix) piggy banks. Make sure there are only transfers linked to piggy bank events.
*
* Class FixPiggies
*/
class FixPiggies extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes common issues with piggy banks.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-piggies';
/** @var int */
private $count;
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(): int
{
$this->count = 0;
$start = microtime(true);
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
/** @var PiggyBankEvent $event */
foreach ($set as $event) {
if (null === $event->transaction_journal_id) {
continue;
}
/** @var TransactionJournal $journal */
$journal = $event->transactionJournal;
// @codeCoverageIgnoreStart
if (null === $journal) {
$event->transaction_journal_id = null;
$event->save();
$this->count++;
continue;
}
// @codeCoverageIgnoreEnd
$type = $journal->transactionType->type;
if (TransactionType::TRANSFER !== $type) {
$event->transaction_journal_id = null;
$event->save();
$this->line(sprintf('Piggy bank #%d was referenced by an invalid event. This has been fixed.', $event->piggy_bank_id));
$this->count++;
continue;
}
}
if (0 === $this->count) {
$this->line('All piggy bank events are correct.');
}
if (0 !== $this->count) {
$this->line(sprintf('Fixed %d piggy bank event(s).', $this->count));
}
$end = round(microtime(true) - $start, 2);
$this->line(sprintf('Verified the content of %d piggy bank events in %s seconds.', $set->count(), $end));
return 0;
}
}

View File

@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/**
* FixUnevenAmount.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use DB;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use stdClass;
/**
* Class FixUnevenAmount
*/
class FixUnevenAmount extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fix journals with uneven amounts.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-uneven-amount';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$start = microtime(true);
$count = 0;
// get invalid journals
$journals = DB::table('transactions')
->groupBy('transaction_journal_id')
->whereNull('deleted_at')
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]);
/** @var stdClass $entry */
foreach ($journals as $entry) {
if (0 !== bccomp((string)$entry->the_sum, '0')) {
$this->fixJournal((int)$entry->transaction_journal_id);
$count++;
}
}
if (0 === $count) {
$this->info('Amount integrity OK!');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified amount integrity in %s seconds', $end));
return 0;
}
/**
* @param int $param
*/
private function fixJournal(int $param): void
{
// one of the transactions is bad.
$journal = TransactionJournal::find($param);
if (!$journal) {
return; // @codeCoverageIgnore
}
/** @var Transaction $source */
$source = $journal->transactions()->where('amount', '<', 0)->first();
$amount = bcmul('-1', (string)$source->amount);
// fix amount of destination:
/** @var Transaction $destination */
$destination = $journal->transactions()->where('amount', '>', 0)->first();
$destination->amount = $amount;
$destination->save();
$message = sprintf('Corrected amount in transaction journal #%d', $param);
$this->line($message);
}
}

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* RemoveBills.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
* Class RemoveBills
*/
class RemoveBills extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove bills from transactions that shouldn\'t have one.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:remove-bills';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(): int
{
$start = microtime(true);
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
$journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id));
$journal->bill_id = null;
$journal->save();
}
if (0 === $journals->count()) {
$this->info('All transaction journals have correct bill information.');
}
if ($journals->count() > 0) {
$this->info('Fixed all transaction journals so they have correct bill information.');
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified bills / journals in %s seconds', $end));
return 0;
}
}

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/**
* RenameMetaFields.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use DB;
use Illuminate\Console\Command;
/**
* Class RenameMetaFields
*/
class RenameMetaFields extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rename changed meta fields.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:rename-meta-fields';
/** @var int */
private $count;
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$this->count = 0;
$start = microtime(true);
$changes = [
'original-source' => 'original_source',
'importHash' => 'import_hash',
'importHashV2' => 'import_hash_v2',
'sepa-cc' => 'sepa_cc',
'sepa-ct-op' => 'sepa_ct_op',
'sepa-ct-id' => 'sepa_ct_id',
'sepa-db' => 'sepa_db',
'sepa-country' => 'sepa_country',
'sepa-ep' => 'sepa_ep',
'sepa-ci' => 'sepa_ci',
'sepa-batch-id' => 'sepa_batch_id',
];
foreach ($changes as $original => $update) {
$this->rename($original, $update);
}
if (0 === $this->count) {
$this->line('All meta fields are correct.');
}
if (0 !== $this->count) {
$this->line(sprintf('Renamed %d meta field(s).', $this->count));
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Renamed meta fields in %s seconds', $end));
return 0;
}
/**
* @param string $original
* @param string $update
*/
private function rename(string $original, string $update): void
{
$count = DB::table('journal_meta')
->where('name', '=', $original)
->update(['name' => $update]);
$this->count += $count;
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* TransferBudgets.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command;
/**
* Class TransferBudgets
*/
class TransferBudgets extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes budgets from transfers.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-transfer-budgets';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$start = microtime(true);
$set = TransactionJournal::distinct()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
->whereNotIn('transaction_types.type', [TransactionType::WITHDRAWAL])
->whereNotNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*']);
$count = 0;
/** @var TransactionJournal $entry */
foreach ($set as $entry) {
$this->info(sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type));
$entry->budgets()->sync([]);
$count++;
}
if (0 === $count) {
$this->info('No invalid budget/journal entries.');
}
if (0 !== $count) {
$this->line(sprintf('Corrected %d invalid budget/journal entries (entry).', $count));
}
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified budget/journals in %s seconds.', $end));
return 0;
}
}

View File

@@ -1,150 +0,0 @@
<?php
/**
* CreateExport.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Carbon\Carbon;
use FireflyIII\Export\ProcessorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
/**
* Class CreateExport.
*
* Generates export from the command line.
*
* @codeCoverageIgnore
*/
class CreateExport extends Command
{
use VerifiesAccessToken;
/**
* 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-export
{--user= : The user ID that the import should import for.}
{--token= : The user\'s access token.}
{--with_attachments : Include user\'s attachments?}
{--with_uploads : Include user\'s uploads?}';
/**
* Execute the console command.
*
* @return int
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function handle(): int
{
if (!$this->verifyAccessToken()) {
$this->error('Invalid access token.');
return 1;
}
$this->line('Full export is running...');
// make repositories
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
/** @var ExportJobRepositoryInterface $jobRepository */
$jobRepository = app(ExportJobRepositoryInterface::class);
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
/** @var JournalRepositoryInterface $journalRepository */
$journalRepository = app(JournalRepositoryInterface::class);
// set user
$user = $userRepository->findNull((int)$this->option('user'));
if (null === $user) {
return 1;
}
$jobRepository->setUser($user);
$journalRepository->setUser($user);
$accountRepository->setUser($user);
// first date
$firstJournal = $journalRepository->firstNull();
$first = new Carbon;
if (null !== $firstJournal) {
$first = $firstJournal->date;
}
// create job and settings.
$job = $jobRepository->create();
$settings = [
'accounts' => $accountRepository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]),
'startDate' => $first,
'endDate' => new Carbon,
'exportFormat' => 'csv',
'includeAttachments' => $this->option('with_attachments'),
'includeOldUploads' => $this->option('with_uploads'),
'job' => $job,
];
/** @var ProcessorInterface $processor */
$processor = app(ProcessorInterface::class);
$processor->setSettings($settings);
$processor->collectJournals();
$processor->convertJournals();
$processor->exportJournals();
if ($settings['includeAttachments']) {
$processor->collectAttachments();
}
if ($settings['includeOldUploads']) {
$processor->collectOldUploads();
}
$processor->createZipFile();
$disk = Storage::disk('export');
$fileName = sprintf('export-%s.zip', date('Y-m-d_H-i-s'));
$localPath = storage_path('export') . '/' . $job->key . '.zip';
// "move" from local to export disk
$disk->put($fileName, file_get_contents($localPath));
unlink($localPath);
$this->line('The export has finished! You can find the ZIP file in export disk with file name:');
$this->line($fileName);
return 0;
}
}

View File

@@ -1,304 +0,0 @@
<?php
/**
* CreateImport.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Import\Storage\ImportArrayStorage;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command;
use Log;
/**
* Class CreateImport.
*
* @codeCoverageIgnore
*/
class CreateImport extends Command
{
use VerifiesAccessToken;
/**
* 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? : The file to import.}
{configuration? : The configuration file to use for the import.}
{--type=csv : The file type of the import.}
{--provider=file : The file type of the import.}
{--user=1 : The user ID that the import should import for.}
{--token= : The user\'s access token.}
{--start : Starts the job immediately.}';
/**
* Run the command.
*
* @throws FireflyException
*/
public function handle(): int
{
if (!$this->verifyAccessToken()) {
$this->errorLine('Invalid access token.');
return 1;
}
/** @var UserRepositoryInterface $userRepository */
$userRepository = app(UserRepositoryInterface::class);
$file = (string)$this->argument('file');
$configuration = (string)$this->argument('configuration');
$user = $userRepository->findNull((int)$this->option('user'));
$cwd = getcwd();
$provider = strtolower((string)$this->option('provider'));
$configurationData = [];
if (null === $user) {
$this->errorLine('User is NULL.');
return 1;
}
if (!$this->validArguments()) {
$this->errorLine('Invalid arguments.');
return 1;
}
if ('' !== $configuration) {
$configurationData = json_decode(file_get_contents($configuration), true);
if (null === $configurationData) {
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return 1;
}
}
$this->infoLine(sprintf('Going to create a job to import file: %s', $file));
$this->infoLine(sprintf('Using configuration file: %s', $configuration));
$this->infoLine(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
$this->infoLine(sprintf('Type of import: %s', $provider));
/** @var ImportJobRepositoryInterface $jobRepository */
$jobRepository = app(ImportJobRepositoryInterface::class);
$jobRepository->setUser($user);
$importJob = $jobRepository->create($provider);
$this->infoLine(sprintf('Created job "%s"', $importJob->key));
// make sure that job has no prerequisites.
if ((bool)config(sprintf('import.has_prereq.%s', $provider))) {
// make prerequisites thing.
$class = (string)config(sprintf('import.prerequisites.%s', $provider));
if (!class_exists($class)) {
throw new FireflyException(sprintf('No class to handle prerequisites for "%s".', $provider)); // @codeCoverageIgnore
}
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser($user);
if (!$object->isComplete()) {
$this->errorLine(sprintf('Import provider "%s" has prerequisites that can only be filled in using the browser.', $provider));
return 1;
}
}
// store file as attachment.
if ('' !== $file) {
$messages = $jobRepository->storeCLIUpload($importJob, 'import_file', $file);
if ($messages->count() > 0) {
$this->errorLine($messages->first());
return 1;
}
$this->infoLine('File content saved.');
}
$this->infoLine('Job configuration saved.');
$jobRepository->setConfiguration($importJob, $configurationData);
$jobRepository->setStatus($importJob, 'ready_to_run');
if (true === $this->option('start')) {
$this->infoLine('The import routine has started. The process is not visible. Please wait.');
Log::debug('Go for import!');
// run it!
$key = sprintf('import.routine.%s', $provider);
$className = config($key);
if (null === $className || !class_exists($className)) {
// @codeCoverageIgnoreStart
$this->errorLine(sprintf('No routine for provider "%s"', $provider));
return 1;
// @codeCoverageIgnoreEnd
}
// keep repeating this call until job lands on "provider_finished"
$valid = ['provider_finished'];
$count = 0;
while (!\in_array($importJob->status, $valid, true) && $count < 6) {
Log::debug(sprintf('Now in loop #%d.', $count + 1));
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setImportJob($importJob);
try {
$routine->run();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$jobRepository->setStatus($importJob, 'error');
$this->errorLine($message);
return 1;
}
$count++;
}
if ('provider_finished' === $importJob->status) {
$this->infoLine('Import has finished. Please wait for storage of data.');
// set job to be storing data:
$jobRepository->setStatus($importJob, 'storing_data');
/** @var ImportArrayStorage $storage */
$storage = app(ImportArrayStorage::class);
$storage->setImportJob($importJob);
try {
$storage->store();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$jobRepository->setStatus($importJob, 'error');
$this->errorLine($message);
return 1;
}
// set storage to be finished:
$jobRepository->setStatus($importJob, 'storage_finished');
}
// give feedback:
$this->infoLine('Job has finished.');
if (null !== $importJob->tag) {
$this->infoLine(sprintf('%d transaction(s) have been imported.', $importJob->tag->transactionJournals->count()));
$this->infoLine(sprintf('You can find your transactions under tag "%s"', $importJob->tag->tag));
}
if (null === $importJob->tag) {
$this->errorLine('No transactions have been imported :(.');
}
if (\count($importJob->errors) > 0) {
$this->infoLine(sprintf('%d error(s) occurred:', \count($importJob->errors)));
foreach ($importJob->errors as $err) {
$this->errorLine('- ' . $err);
}
}
}
// clear cache for user:
app('preferences')->setForUser($user, 'lastActivity', microtime());
return 0;
}
/**
* @param string $message
* @param array|null $data
*/
private function errorLine(string $message, array $data = null): void
{
Log::error($message, $data ?? []);
$this->error($message);
}
/**
* @param string $message
* @param array $data
*/
private function infoLine(string $message, array $data = null): void
{
Log::info($message, $data ?? []);
$this->line($message);
}
/**
* Verify user inserts correct arguments.
*
* @noinspection MultipleReturnStatementsInspection
* @return bool
*/
private function validArguments(): bool
{
$file = (string)$this->argument('file');
$configuration = (string)$this->argument('configuration');
$cwd = getcwd();
$validTypes = config('import.options.file.import_formats');
$type = strtolower($this->option('type'));
$provider = strtolower($this->option('provider'));
$enabled = (bool)config(sprintf('import.enabled.%s', $provider));
if (false === $enabled) {
$this->errorLine(sprintf('Provider "%s" is not enabled.', $provider));
return false;
}
if ('file' === $provider && !\in_array($type, $validTypes, true)) {
$this->errorLine(sprintf('Cannot import file of type "%s"', $type));
return false;
}
if ('file' === $provider && !file_exists($file)) {
$this->errorLine(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
return false;
}
if ('file' === $provider && !file_exists($configuration)) {
$this->errorLine(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;
}
return true;
}
}

View File

@@ -1,111 +0,0 @@
<?php
/**
* DecryptAttachment.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Console\Command;
use Log;
/**
* Class DecryptAttachment.
*
* @codeCoverageIgnore
*/
class DecryptAttachment extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Decrypts an attachment and dumps the content in a file in the given directory.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature
= 'firefly:decrypt-attachment {id:The ID of the attachment.} {name:The file name of the attachment.}
{directory:Where the file must be stored.}';
/**
* Execute the console command.
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return int
*/
public function handle(): int
{
/** @var AttachmentRepositoryInterface $repository */
$repository = app(AttachmentRepositoryInterface::class);
$attachmentId = (int)$this->argument('id');
$attachment = $repository->findWithoutUser($attachmentId);
$attachmentName = trim((string)$this->argument('name'));
$storagePath = realpath(trim((string)$this->argument('directory')));
if (null === $attachment) {
$this->error(sprintf('No attachment with id #%d', $attachmentId));
Log::error(sprintf('DecryptAttachment: No attachment with id #%d', $attachmentId));
return 1;
}
if ($attachmentName !== $attachment->filename) {
$this->error('File name does not match.');
Log::error('DecryptAttachment: File name does not match.');
return 1;
}
if (!is_dir($storagePath)) {
$this->error(sprintf('Path "%s" is not a directory.', $storagePath));
Log::error(sprintf('DecryptAttachment: Path "%s" is not a directory.', $storagePath));
return 1;
}
if (!is_writable($storagePath)) {
$this->error(sprintf('Path "%s" is not writable.', $storagePath));
Log::error(sprintf('DecryptAttachment: Path "%s" is not writable.', $storagePath));
return 1;
}
$fullPath = $storagePath . DIRECTORY_SEPARATOR . $attachment->filename;
$content = $repository->getContent($attachment);
$this->line(sprintf('Going to write content for attachment #%d into file "%s"', $attachment->id, $fullPath));
$result = file_put_contents($fullPath, $content);
if (false === $result) {
$this->error('Could not write to file.');
return 1;
}
$this->info(sprintf('%d bytes written. Exiting now..', $result));
return 0;
}
}

View File

@@ -28,7 +28,6 @@ use Crypt;
use DB; use DB;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
use Log; use Log;
@@ -50,14 +49,15 @@ class DecryptDatabase extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'firefly:decrypt-all'; protected $signature = 'firefly-iii:decrypt-all';
/** /**
* Execute the console command. * Execute the console command.
* *
* @return mixed * @return int
* @throws FireflyException
*/ */
public function handle() public function handle(): int
{ {
$this->line('Going to decrypt the database.'); $this->line('Going to decrypt the database.');
$tables = [ $tables = [
@@ -113,7 +113,7 @@ class DecryptDatabase extends Command
$this->line(sprintf('Decrypted the data in table "%s".', $table)); $this->line(sprintf('Decrypted the data in table "%s".', $table));
// mark as decrypted: // mark as decrypted:
$configName = sprintf('is_decrypted_%s', $table); $configName = sprintf('is_decrypted_%s', $table);
FireflyConfig::set($configName, true); app('fireflyconfig')->set($configName, true);
} }
$this->info('Done!'); $this->info('Done!');
@@ -129,7 +129,7 @@ class DecryptDatabase extends Command
private function isDecrypted(string $table): bool private function isDecrypted(string $table): bool
{ {
$configName = sprintf('is_decrypted_%s', $table); $configName = sprintf('is_decrypted_%s', $table);
$configVar = FireflyConfig::get($configName, false); $configVar = app('fireflyconfig')->get($configName, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool)$configVar->data; return (bool)$configVar->data;
} }
@@ -139,17 +139,19 @@ class DecryptDatabase extends Command
/** /**
* @param $value * Tries to decrypt data. Will only throw an exception when the MAC is invalid.
* *
* @return mixed * @param $value
* @return string
* @throws FireflyException
*/ */
private function tryDecrypt($value) private function tryDecrypt($value)
{ {
try { try {
$value = Crypt::decrypt($value); $value = Crypt::decrypt($value); // verified
} catch (DecryptException $e) { } catch (DecryptException $e) {
if ('The MAC is invalid.' === $e->getMessage()) { if ('The MAC is invalid.' === $e->getMessage()) {
throw new FireflyException($e->getMessage()); throw new FireflyException($e->getMessage()); // @codeCoverageIgnore
} }
Log::debug(sprintf('Could not decrypt. %s', $e->getMessage())); Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
} }

View File

@@ -1,165 +0,0 @@
<?php
/**
* Import.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Tag;
use Illuminate\Console\Command;
use Log;
/**
* Class Import.
*
* @codeCoverageIgnore
*/
class Import extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'This will start a new import.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:start-import {key}';
/**
* Run the import routine.
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @throws FireflyException
*/
public function handle(): int
{
Log::debug('Start start-import command');
$jobKey = (string)$this->argument('key');
/** @var ImportJob $job */
$job = ImportJob::where('key', $jobKey)->first();
if (null === $job) {
$this->errorLine(sprintf('No job found with key "%s"', $jobKey));
return 1;
}
if (!$this->isValid($job)) {
$this->errorLine('Job is not valid for some reason. Exit.');
return 1;
}
$this->infoLine(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type));
// actually start job:
$type = 'csv' === $job->file_type ? 'file' : $job->file_type;
$key = sprintf('import.routine.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setImportJob($job);
$routine->run();
/**
* @var int $index
* @var string $error
*/
foreach ($job->errors as $index => $error) {
$this->errorLine(sprintf('Error importing line #%d: %s', $index, $error));
}
/** @var Tag $tag */
$tag = $job->tag()->first();
$count = 0;
if (null === $tag) {
$count = $tag->transactionJournals()->count();
}
$this->infoLine(sprintf('The import has finished. %d transactions have been imported.', $count));
return 0;
}
/**
* Displays an error.
*
* @param string $message
* @param array|null $data
*/
private function errorLine(string $message, array $data = null): void
{
Log::error($message, $data ?? []);
$this->error($message);
}
/**
* Displays an informational message.
*
* @param string $message
* @param array $data
*/
private function infoLine(string $message, array $data = null): void
{
Log::info($message, $data ?? []);
$this->line($message);
}
/**
* Check if job is valid to be imported.
*
* @param ImportJob $job
*
* @return bool
*/
private function isValid(ImportJob $job): bool
{
if (null === $job) {
$this->errorLine('This job does not seem to exist.');
return false;
}
if ('configured' !== $job->status) {
Log::error(sprintf('This job is not ready to be imported (status is %s).', $job->status));
$this->errorLine('This job is not ready to be imported.');
return false;
}
return true;
}
}

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